blob: a5c41be2080508ac65fd823f833c7754305fbbde [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.audioquality.experiments;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audioquality.AudioQualityVerifierActivity;
import com.android.cts.verifier.audioquality.Experiment;
import com.android.cts.verifier.audioquality.Utils;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
/**
* LoopbackExperiment represents a general class of experiments, all of which
* comprise playing an audio stimulus of some kind, whilst simultaneously
* recording from the microphone. The recording is then analyzed to determine
* the test results (score and report).
*/
public class LoopbackExperiment extends Experiment {
private static final String TAG = "LoopbackExperiment";
protected static final int TIMEOUT = 10;
// Amount of silence in ms before and after playback
protected static final int END_DELAY_MS = 500;
private Recorder mRecorder = null;
public LoopbackExperiment(boolean enable) {
super(enable);
}
protected byte[] getStim(Context context) {
int stimNum = 2;
byte[] data = Utils.getStim(context, stimNum);
return data;
}
@Override
public void run() {
byte[] playbackData = getStim(mContext);
byte[] recordedData = loopback(playbackData);
compare(playbackData, recordedData);
setRecording(recordedData);
mTerminator.terminate(false);
}
protected byte[] loopback(byte[] playbackData) {
int samples = playbackData.length / 2;
int duration = (samples * 1000) / AudioQualityVerifierActivity.SAMPLE_RATE; // In ms
int padSamples = (END_DELAY_MS * AudioQualityVerifierActivity.SAMPLE_RATE) / 1000;
int totalSamples = samples + 2 * padSamples;
byte[] recordedData = new byte[totalSamples * 2];
mRecorder = new Recorder(recordedData, totalSamples);
mRecorder.start();
Utils.delay(END_DELAY_MS);
Utils.playRaw(playbackData);
int timeout = duration + 2 * END_DELAY_MS;
try {
mRecorder.join(timeout);
} catch (InterruptedException e) {}
return recordedData;
}
protected void compare(byte[] stim, byte[] record) {
setScore(getString(R.string.aq_complete));
setReport(getString(R.string.aq_loopback_report));
}
private void halt() {
if (mRecorder != null) {
mRecorder.halt();
}
}
@Override
public void cancel() {
super.cancel();
halt();
}
@Override
public void stop() {
super.stop();
halt();
}
@Override
public int getTimeout() {
return TIMEOUT;
}
/* Class which records audio in a background thread, to fill the supplied buffer. */
class Recorder extends Thread {
private AudioRecord mRecord;
private int mSamples;
private byte[] mBuffer;
private boolean mProceed;
Recorder(byte[] buffer, int samples) {
mBuffer = buffer;
mSamples = samples;
mProceed = true;
}
public void halt() {
mProceed = false;
}
@Override
public void run() {
final int minBufferSize = AudioQualityVerifierActivity.SAMPLE_RATE
* AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
final int bufferSize = Utils.getAudioRecordBufferSize(minBufferSize);
mRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
AudioQualityVerifierActivity.AUDIO_FORMAT, bufferSize);
if (mRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Couldn't open audio for recording");
return;
}
mRecord.startRecording();
if (mRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
Log.e(TAG, "Couldn't record");
return;
}
captureLoop();
mRecord.stop();
mRecord.release();
mRecord = null;
}
private void captureLoop() {
int totalBytes = mSamples * AudioQualityVerifierActivity.BYTES_PER_SAMPLE;
Log.i(TAG, "Recording " + totalBytes + " bytes");
int position = 0;
int bytes;
while (position < totalBytes && mProceed) {
bytes = mRecord.read(mBuffer, position, totalBytes - position);
if (bytes < 0) {
if (bytes == AudioRecord.ERROR_INVALID_OPERATION) {
Log.e(TAG, "Recording object not initalized");
} else if (bytes == AudioRecord.ERROR_BAD_VALUE) {
Log.e(TAG, "Invalid recording parameters");
} else {
Log.e(TAG, "Error during recording");
}
return;
}
position += bytes;
}
}
}
}