Home | History | Annotate | Download | only in experiments
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.audioquality.experiments;
     18 
     19 import com.android.cts.verifier.R;
     20 import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
     21 import com.android.cts.verifier.audioquality.Experiment;
     22 import com.android.cts.verifier.audioquality.Utils;
     23 
     24 import android.content.Context;
     25 import android.media.AudioFormat;
     26 import android.media.AudioRecord;
     27 import android.media.MediaRecorder;
     28 import android.util.Log;
     29 
     30 /**
     31  * LoopbackExperiment represents a general class of experiments, all of which
     32  * comprise playing an audio stimulus of some kind, whilst simultaneously
     33  * recording from the microphone. The recording is then analyzed to determine
     34  * the test results (score and report).
     35  */
     36 public class LoopbackExperiment extends Experiment {
     37     protected static final int TIMEOUT = 10;
     38 
     39     // Amount of silence in ms before and after playback
     40     protected static final int END_DELAY_MS = 500;
     41 
     42     private Recorder mRecorder = null;
     43 
     44     public LoopbackExperiment(boolean enable) {
     45         super(enable);
     46     }
     47 
     48     protected byte[] getStim(Context context) {
     49         int stimNum = 2;
     50         byte[] data = Utils.getStim(context, stimNum);
     51         return data;
     52     }
     53 
     54     @Override
     55     public void run() {
     56         byte[] playbackData = getStim(mContext);
     57         byte[] recordedData = loopback(playbackData);
     58 
     59         compare(playbackData, recordedData);
     60         setRecording(recordedData);
     61         mTerminator.terminate(false);
     62     }
     63 
     64     protected byte[] loopback(byte[] playbackData) {
     65         int samples = playbackData.length / 2;
     66         int duration = (samples * 1000) / AudioQualityVerifierActivity.SAMPLE_RATE; // In ms
     67         int padSamples = (END_DELAY_MS * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
     68         int totalSamples = samples + 2 * padSamples;
     69         byte[] recordedData = new byte[totalSamples * 2];
     70 
     71         mRecorder = new Recorder(recordedData, totalSamples);
     72         mRecorder.start();
     73         Utils.delay(END_DELAY_MS);
     74 
     75         Utils.playRaw(playbackData);
     76 
     77         int timeout = duration + 2 * END_DELAY_MS;
     78         try {
     79             mRecorder.join(timeout);
     80         } catch (InterruptedException e) {}
     81 
     82         return recordedData;
     83     }
     84 
     85     protected void compare(byte[] stim, byte[] record) {
     86         setScore(getString(R.string.aq_complete));
     87         setReport(getString(R.string.aq_loopback_report));
     88     }
     89 
     90     private void halt() {
     91         if (mRecorder != null) {
     92             mRecorder.halt();
     93         }
     94     }
     95 
     96     @Override
     97     public void cancel() {
     98         super.cancel();
     99         halt();
    100     }
    101 
    102     @Override
    103     public void stop() {
    104         super.stop();
    105         halt();
    106     }
    107 
    108     @Override
    109     public int getTimeout() {
    110         return TIMEOUT;
    111     }
    112 
    113     /* Class which records audio in a background thread, to fill the supplied buffer. */
    114     class Recorder extends Thread {
    115         private AudioRecord mRecord;
    116         private int mSamples;
    117         private byte[] mBuffer;
    118         private boolean mProceed;
    119 
    120         Recorder(byte[] buffer, int samples) {
    121             mBuffer = buffer;
    122             mSamples = samples;
    123             mProceed = true;
    124         }
    125 
    126         public void halt() {
    127             mProceed = false;
    128         }
    129 
    130         @Override
    131         public void run() {
    132             final int minBufferSize = AudioQualityVerifierActivity.SAMPLE_RATE
    133                     * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
    134             final int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
    135 
    136             mRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
    137                     AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
    138                     AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
    139             if (mRecord.getState() != AudioRecord.STATE_INITIALIZED) {
    140                 Log.e(TAG, "Couldn't open audio for recording");
    141                 return;
    142             }
    143             mRecord.startRecording();
    144             if (mRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
    145                 Log.e(TAG, "Couldn't record");
    146                 return;
    147             }
    148 
    149             captureLoop();
    150 
    151             mRecord.stop();
    152             mRecord.release();
    153             mRecord = null;
    154         }
    155 
    156         private void captureLoop() {
    157             int totalBytes = mSamples * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
    158             Log.i(TAG, "Recording " + totalBytes + " bytes");
    159             int position = 0;
    160             int bytes;
    161             while (position < totalBytes && mProceed) {
    162                 bytes = mRecord.read(mBuffer, position, totalBytes - position);
    163                 if (bytes < 0) {
    164                     if (bytes == AudioRecord.ERROR_INVALID_OPERATION) {
    165                         Log.e(TAG, "Recording object not initalized");
    166                     } else if (bytes == AudioRecord.ERROR_BAD_VALUE) {
    167                         Log.e(TAG, "Invalid recording parameters");
    168                     } else {
    169                         Log.e(TAG, "Error during recording");
    170                     }
    171                     return;
    172                 }
    173                 position += bytes;
    174             }
    175         }
    176     }
    177 }
    178