blob: e066943359cfa8680905d578735293c061c45a72 [file] [log] [blame]
/*
* Copyright (C) 2015 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.audio;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.cts.verifier.CtsVerifierReportLog;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.DspBufferComplex;
import com.android.cts.verifier.audio.wavelib.DspBufferDouble;
import com.android.cts.verifier.audio.wavelib.DspBufferMath;
import com.android.cts.verifier.audio.wavelib.DspFftServer;
import com.android.cts.verifier.audio.wavelib.DspWindow;
import com.android.cts.verifier.audio.wavelib.PipeShort;
import com.android.cts.verifier.audio.soundio.SoundPlayerObject;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.cts.verifier.audio.wavelib.VectorAverage;
/**
* Tests Audio built in Microphone response using external speakers and USB reference microphone.
*/
public class AudioFrequencyMicActivity extends AudioFrequencyActivity implements Runnable,
AudioRecord.OnRecordPositionUpdateListener {
private static final String TAG = "AudioFrequencyMicActivity";
private static final int TEST_STARTED = 900;
private static final int TEST_MESSAGE = 903;
private static final int TEST_ENDED = 904;
private static final int TEST_ENDED_ERROR = 905;
private static final double MIN_ENERGY_BAND_1 = -50.0; //dB Full Scale
private static final double MAX_ENERGY_BAND_1_BASE = -60.0; //dB Full Scale
private static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
private static final double MAX_VAL = Math.pow(2, 15);
private static final double CLIP_LEVEL = (MAX_VAL-10) / MAX_VAL;
private static final int TEST_NONE = -1;
private static final int TEST_NOISE = 0;
private static final int TEST_USB_BACKGROUND = 1;
private static final int TEST_USB_NOISE = 2;
private static final int TEST_COUNT = 3;
private int mCurrentTest = TEST_NONE;
private boolean mTestsDone[] = new boolean[TEST_COUNT];
private static final int TEST_DURATION_DEFAULT = 2000;
private static final int TEST_DURATION_NOISE = TEST_DURATION_DEFAULT;
private static final int TEST_DURATION_USB_BACKGROUND = TEST_DURATION_DEFAULT;
private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
ProgressBar mProgressNoise;
TextView mResultTestNoise;
Button mButtonPlayNoise;
ProgressBar mProgressUsbBackground;
TextView mResultTestUsbBackground;
ProgressBar mProgressUsbNoise;
TextView mResultTestUsbNoise;
Button mButtonPlayUsbNoise;
TextView mGlobalResultText;
private boolean mIsRecording = false;
private final Object mRecordingLock = new Object();
private AudioRecord mRecorder;
private int mMinRecordBufferSizeInSamples = 0;
private short[] mAudioShortArray;
private short[] mAudioShortArray2;
private final int mBlockSizeSamples = 1024;
private final int mSamplingRate = 48000;
private final int mSelectedRecordSource = MediaRecorder.AudioSource.VOICE_RECOGNITION;
private final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
private final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
private Thread mRecordThread;
PipeShort mPipe = new PipeShort(65536);
SoundPlayerObject mSPlayer;
private DspBufferComplex mC;
private DspBufferDouble mData;
private DspWindow mWindow;
private DspFftServer mFftServer;
private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
private VectorAverage mFreqAverageNoise = new VectorAverage();
private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
int mBands = 4;
AudioBandSpecs[] bandSpecsArray = new AudioBandSpecs[mBands];
AudioBandSpecs[] baseBandSpecsArray = new AudioBandSpecs[mBands];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_mic_activity);
//Test Noise
findViewById(R.id.frequency_mic_test_noise_btn).setOnClickListener(mBtnClickListener);
mProgressNoise = (ProgressBar) findViewById(R.id.frequency_mic_test_noise_progress_bar);
mResultTestNoise = (TextView) findViewById(R.id.frequency_mic_test_noise_result);
mButtonPlayNoise = (Button) findViewById(R.id.frequency_mic_play_noise_btn);
mButtonPlayNoise.setOnClickListener(mBtnClickListener);
showWait(mProgressNoise, false);
//USB Background
findViewById(R.id.frequency_mic_test_usb_background_btn).
setOnClickListener(mBtnClickListener);
mProgressUsbBackground = (ProgressBar)
findViewById(R.id.frequency_mic_test_usb_background_progress_bar);
mResultTestUsbBackground = (TextView)
findViewById(R.id.frequency_mic_test_usb_background_result);
showWait(mProgressUsbBackground, false);
findViewById(R.id.frequency_mic_test_usb_noise_btn).setOnClickListener(mBtnClickListener);
mProgressUsbNoise = (ProgressBar)
findViewById(R.id.frequency_mic_test_usb_noise_progress_bar);
mResultTestUsbNoise = (TextView) findViewById(R.id.frequency_mic_test_usb_noise_result);
mButtonPlayUsbNoise = (Button) findViewById(R.id.frequency_mic_play_usb_noise_btn);
mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
showWait(mProgressUsbNoise, false);
mGlobalResultText = (TextView) findViewById(R.id.frequency_mic_test_global_result);
mSPlayer = new SoundPlayerObject();
mSPlayer.setSoundWithResId(mContext, R.raw.stereo_mono_white_noise_48);
mSPlayer.setBalance(0.5f);
//Init FFT stuff
mAudioShortArray2 = new short[mBlockSizeSamples*2];
mData = new DspBufferDouble(mBlockSizeSamples);
mC = new DspBufferComplex(mBlockSizeSamples);
mFftServer = new DspFftServer(mBlockSizeSamples);
int overlap = mBlockSizeSamples / 2;
mWindow = new DspWindow(DspWindow.WINDOW_HANNING, mBlockSizeSamples, overlap);
setPassFailButtonClickListeners();
getPassButton().setEnabled(false);
setInfoResources(R.string.audio_frequency_mic_test,
R.string.frequency_mic_info, -1);
//Init bands for BuiltIn/Reference test
bandSpecsArray[0] = new AudioBandSpecs(
50, 500, /* frequency start,stop */
4.0, -50, /* start top,bottom value */
4.0, -4.0 /* stop top,bottom value */);
bandSpecsArray[1] = new AudioBandSpecs(
500,4000, /* frequency start,stop */
4.0, -4.0, /* start top,bottom value */
4.0, -4.0 /* stop top,bottom value */);
bandSpecsArray[2] = new AudioBandSpecs(
4000, 12000, /* frequency start,stop */
4.0, -4.0, /* start top,bottom value */
5.0, -5.0 /* stop top,bottom value */);
bandSpecsArray[3] = new AudioBandSpecs(
12000, 20000, /* frequency start,stop */
5.0, -5.0, /* start top,bottom value */
5.0, -30.0 /* stop top,bottom value */);
//Init base bands for silence
baseBandSpecsArray[0] = new AudioBandSpecs(
50, 500, /* frequency start,stop */
40.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
baseBandSpecsArray[1] = new AudioBandSpecs(
500,4000, /* frequency start,stop */
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
baseBandSpecsArray[2] = new AudioBandSpecs(
4000, 12000, /* frequency start,stop */
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
baseBandSpecsArray[3] = new AudioBandSpecs(
12000, 20000, /* frequency start,stop */
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
connectRefMicUI();
}
//
// Overrides
//
void enableTestUI(boolean enable) {
enableLayout(R.id.frequency_mic_layout_test_noise_dut_buttons, enable);
enableLayout(R.id.frequency_mic_layout_test_noise_src_buttons, enable);
enableLayout(R.id.frequency_mic_layout_test_usb_background_dut_buttons, enable);
enableLayout(R.id.frequency_mic_layout_test_usb_noise_dut_buttons, enable);
enableLayout(R.id.frequency_mic_layout_test_usb_noise_src_buttons, enable);
}
private void playerToggleButton(int buttonId) {
if (playerIsPlaying()) {
playerStopAll();
} else {
playerTransport(true);
setButtonPlayStatus(buttonId);
}
}
private class OnBtnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.frequency_mic_test_noise_btn) {
startTest(TEST_NOISE);
} else if (id == R.id.frequency_mic_play_noise_btn) {
playerToggleButton(id);
} else if (id == R.id.frequency_mic_test_usb_background_btn) {
startTest(TEST_USB_BACKGROUND);
} else if (id == R.id.frequency_mic_test_usb_noise_btn) {
startTest(TEST_USB_NOISE);
} else if (id == R.id.frequency_mic_play_usb_noise_btn) {
playerToggleButton(id);
}
}
}
private void setButtonPlayStatus(int playResId) {
String play = getResources().getText(R.string.frequency_mic_play).toString();
String stop = getResources().getText(R.string.frequency_mic_stop).toString();
mButtonPlayNoise.setText(playResId == R.id.frequency_mic_play_noise_btn ? stop : play);
mButtonPlayUsbNoise.setText(playResId ==
R.id.frequency_mic_play_usb_noise_btn ? stop : play);
}
private void playerTransport(boolean play) {
if (!mSPlayer.isAlive()) {
mSPlayer.start();
}
mSPlayer.play(play);
}
private boolean playerIsPlaying() {
return mSPlayer.isPlaying();
}
private void playerStopAll() {
if (mSPlayer.isAlive() && mSPlayer.isPlaying()) {
mSPlayer.play(false);
setButtonPlayStatus(-1);
}
}
private void showWait(ProgressBar pb, boolean show) {
if (show) {
pb.setVisibility(View.VISIBLE);
} else {
pb.setVisibility(View.INVISIBLE);
}
}
private void showWait(int testId, boolean show) {
switch(testId) {
case TEST_NOISE:
showWait(mProgressNoise, show);
break;
case TEST_USB_BACKGROUND:
showWait(mProgressUsbBackground, show);
break;
case TEST_USB_NOISE:
showWait(mProgressUsbNoise, show);
break;
}
}
private String getTestString(int testId) {
String name = "undefined";
switch(testId) {
case TEST_NOISE:
name = "BuiltIn_noise";
break;
case TEST_USB_BACKGROUND:
name = "USB_background";
break;
case TEST_USB_NOISE:
name = "USB_noise";
break;
}
return name;
}
private void showMessage(int testId, String msg) {
if (msg != null && msg.length() > 0) {
switch(testId) {
case TEST_NOISE:
mResultTestNoise.setText(msg);
break;
case TEST_USB_BACKGROUND:
mResultTestUsbBackground.setText(msg);
break;
case TEST_USB_NOISE:
mResultTestUsbNoise.setText(msg);
break;
}
}
}
Thread mTestThread;
private void startTest(int testId) {
if (mTestThread != null && !mTestThread.isAlive()) {
mTestThread = null; //kill it.
}
if (mTestThread == null) {
Log.v(TAG,"Executing test Thread");
switch(testId) {
case TEST_NOISE:
mTestThread = new Thread(new TestRunnable(TEST_NOISE) {
public void run() {
super.run();
if (!mUsbMicConnected) {
sendMessage(mTestId, TEST_MESSAGE,
"Testing Built in Microphone: Noise");
mFreqAverageNoise.reset();
mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
record(TEST_DURATION_NOISE);
sendMessage(mTestId, TEST_ENDED, "Testing Completed");
mTestsDone[mTestId] = true;
} else {
sendMessage(mTestId, TEST_ENDED_ERROR,
"Please Unplug USB Microphone");
mTestsDone[mTestId] = false;
}
}
});
break;
case TEST_USB_BACKGROUND:
playerStopAll();
mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) {
public void run() {
super.run();
if (mUsbMicConnected) {
sendMessage(mTestId, TEST_MESSAGE,
"Testing USB Microphone: background");
mFreqAverageUsbBackground.reset();
mFreqAverageUsbBackground.setCaptureType(
VectorAverage.CAPTURE_TYPE_AVERAGE);
record(TEST_DURATION_USB_BACKGROUND);
sendMessage(mTestId, TEST_ENDED, "Testing Completed");
mTestsDone[mTestId] = true;
} else {
sendMessage(mTestId, TEST_ENDED_ERROR,
"USB Microphone not detected.");
mTestsDone[mTestId] = false;
}
}
});
break;
case TEST_USB_NOISE:
mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) {
public void run() {
super.run();
if (mUsbMicConnected) {
sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise");
mFreqAverageUsbNoise.reset();
mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
record(TEST_DURATION_USB_NOISE);
sendMessage(mTestId, TEST_ENDED, "Testing Completed");
mTestsDone[mTestId] = true;
} else {
sendMessage(mTestId, TEST_ENDED_ERROR,
"USB Microphone not detected.");
mTestsDone[mTestId] = false;
}
}
});
break;
}
mTestThread.start();
} else {
Log.v(TAG,"test Thread already running.");
}
}
public class TestRunnable implements Runnable {
public int mTestId;
public boolean mUsbMicConnected;
TestRunnable(int testId) {
Log.v(TAG,"New TestRunnable");
mTestId = testId;
}
public void run() {
mCurrentTest = mTestId;
sendMessage(mTestId, TEST_STARTED,"");
mUsbMicConnected =
UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
};
public void record(int durationMs) {
startRecording();
try {
Thread.sleep(durationMs);
} catch (InterruptedException e) {
e.printStackTrace();
//restore interrupted status
Thread.currentThread().interrupt();
}
stopRecording();
}
public void sendMessage(int testId, int msgType, String str) {
Message msg = Message.obtain();
msg.what = msgType;
msg.obj = str;
msg.arg1 = testId;
mMessageHandler.sendMessage(msg);
}
}
private Handler mMessageHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
int testId = msg.arg1; //testId
String str = (String) msg.obj;
switch (msg.what) {
case TEST_STARTED:
showWait(testId, true);
break;
case TEST_MESSAGE:
showMessage(testId, str);
break;
case TEST_ENDED:
showWait(testId, false);
playerStopAll();
showMessage(testId, str);
appendResultsToScreen(testId, "test finished");
computeAllResults();
break;
case TEST_ENDED_ERROR:
showWait(testId, false);
playerStopAll();
showMessage(testId, str);
computeAllResults();
default:
Log.e(TAG, String.format("Unknown message: %d", msg.what));
}
}
};
private class Results {
private String mLabel;
public double[] mValuesLog;
int[] mPointsPerBand = new int[mBands];
double[] mAverageEnergyPerBand = new double[mBands];
int[] mInBoundPointsPerBand = new int[mBands];
public boolean mIsBaseMeasurement = false;
public Results(String label) {
mLabel = label;
}
//append results
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Channel %s\n", mLabel));
sb.append("Level in Band 1 : " + (testLevel() ? "OK" :"Not Optimal") +
(mIsBaseMeasurement ? " (Base Meas.)" : "") + "\n");
for (int b = 0; b < mBands; b++) {
double percent = 0;
if (mPointsPerBand[b] > 0) {
percent = 100.0 * (double) mInBoundPointsPerBand[b] / mPointsPerBand[b];
}
sb.append(String.format(
" Band %d: Av. Level: %.1f dB InBand: %d/%d (%.1f%%) %s\n",
b, mAverageEnergyPerBand[b],
mInBoundPointsPerBand[b],
mPointsPerBand[b],
percent,
(testInBand(b) ? "OK" : "Not Optimal")));
}
return sb.toString();
}
public boolean testLevel() {
if (mIsBaseMeasurement && mAverageEnergyPerBand[1] <= MAX_ENERGY_BAND_1_BASE) {
return true;
} else if (mAverageEnergyPerBand[1] >= MIN_ENERGY_BAND_1) {
return true;
}
return false;
}
public boolean testInBand(int b) {
if (b >= 0 && b < mBands && mPointsPerBand[b] > 0) {
if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] >
MIN_FRACTION_POINTS_IN_BAND) {
return true;
}
}
return false;
}
public boolean testAll() {
if (!testLevel()) {
return false;
}
for (int b = 0; b < mBands; b++) {
if (!testInBand(b)) {
return false;
}
}
return true;
}
}
private void computeAllResults() {
StringBuilder sb = new StringBuilder();
boolean allDone = true;
for (int i = 0; i < TEST_COUNT; i++) {
allDone = allDone & mTestsDone[i];
sb.append(String.format("%s : %s\n", getTestString(i),
mTestsDone[i] ? "DONE" :" NOT DONE"));
}
if (allDone) {
sb.append(computeResults());
} else {
sb.append("Please execute all tests for results\n");
}
mGlobalResultText.setText(sb.toString());
}
private String computeResults() {
StringBuilder sb = new StringBuilder();
sb.append("\n");
Results resultsBuiltIn = new Results(getTestString(TEST_NOISE));
if (computeResultsForVector(mFreqAverageNoise, resultsBuiltIn, false, bandSpecsArray)) {
sb.append(resultsBuiltIn.toString());
sb.append("\n");
storeTestResults(resultsBuiltIn);
}
Results resultsBase = new Results(getTestString(TEST_USB_BACKGROUND));
if (computeResultsForVector(mFreqAverageUsbBackground, resultsBase, true,
baseBandSpecsArray)) {
sb.append(resultsBase.toString());
sb.append("\n");
storeTestResults(resultsBase);
}
Results resultsUsbNoise = new Results(getTestString(TEST_USB_NOISE));
if (computeResultsForVector(mFreqAverageUsbNoise, resultsUsbNoise, false,
bandSpecsArray)) {
sb.append(resultsUsbNoise.toString());
sb.append("\n");
storeTestResults(resultsUsbNoise);
getPassButton().setEnabled(true);
}
return sb.toString();
}
private boolean computeResultsForVector(VectorAverage freqAverage, Results results,
boolean isBase, AudioBandSpecs[] bandSpecs) {
results.mIsBaseMeasurement = isBase;
int points = freqAverage.getSize();
if (points > 0) {
//compute vector in db
double[] values = new double[points];
freqAverage.getData(values, false);
results.mValuesLog = new double[points];
for (int i = 0; i < points; i++) {
results.mValuesLog[i] = 20 * Math.log10(values[i]);
}
int currentBand = 0;
for (int i = 0; i < points; i++) {
double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
if (freq > bandSpecs[currentBand].mFreqStop) {
currentBand++;
if (currentBand >= mBands)
break;
}
if (freq >= bandSpecs[currentBand].mFreqStart) {
results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
results.mPointsPerBand[currentBand]++;
}
}
for (int b = 0; b < mBands; b++) {
if (results.mPointsPerBand[b] > 0) {
results.mAverageEnergyPerBand[b] =
results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
}
}
//set offset relative to band 1 level
for (int b = 0; b < mBands; b++) {
bandSpecs[b].setOffset(results.mAverageEnergyPerBand[1]);
}
//test points in band.
currentBand = 0;
for (int i = 0; i < points; i++) {
double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
if (freq > bandSpecs[currentBand].mFreqStop) {
currentBand++;
if (currentBand >= mBands)
break;
}
if (freq >= bandSpecs[currentBand].mFreqStart) {
double value = results.mValuesLog[i];
if (bandSpecs[currentBand].isInBounds(freq, value)) {
results.mInBoundPointsPerBand[currentBand]++;
}
}
}
return true;
} else {
return false;
}
}
//append results
private void appendResultsToScreen(String str, TextView text) {
String currentText = text.getText().toString();
text.setText(currentText + "\n" + str);
}
private void appendResultsToScreen(int testId, String str) {
switch(testId) {
case TEST_NOISE:
appendResultsToScreen(str, mResultTestNoise);
break;
case TEST_USB_BACKGROUND:
appendResultsToScreen(str, mResultTestUsbBackground);
break;
case TEST_USB_NOISE:
appendResultsToScreen(str, mResultTestUsbNoise);
break;
}
}
/**
* Store test results in log
*/
private void storeTestResults(Results results) {
String channelLabel = "channel_" + results.mLabel;
CtsVerifierReportLog reportLog = getReportLog();
for (int b = 0; b < mBands; b++) {
String bandLabel = String.format(channelLabel + "_%d", b);
reportLog.addValue(
bandLabel + "_Level",
results.mAverageEnergyPerBand[b],
ResultType.HIGHER_BETTER,
ResultUnit.NONE);
reportLog.addValue(
bandLabel + "_pointsinbound",
results.mInBoundPointsPerBand[b],
ResultType.HIGHER_BETTER,
ResultUnit.COUNT);
reportLog.addValue(
bandLabel + "_pointstotal",
results.mPointsPerBand[b],
ResultType.NEUTRAL,
ResultUnit.COUNT);
}
reportLog.addValues(channelLabel + "_magnitudeSpectrumLog",
results.mValuesLog,
ResultType.NEUTRAL,
ResultUnit.NONE);
Log.v(TAG, "Results Stored");
}
@Override // PassFailButtons
public void recordTestResults() {
getReportLog().submit();
}
private void recordHeasetPortFound(boolean found) {
getReportLog().addValue(
"User Reported Headset Port",
found ? 1.0 : 0,
ResultType.NEUTRAL,
ResultUnit.NONE);
}
private void startRecording() {
synchronized (mRecordingLock) {
mIsRecording = true;
}
boolean successful = initRecord();
if (successful) {
startRecordingForReal();
} else {
Log.v(TAG, "Recorder initialization error.");
synchronized (mRecordingLock) {
mIsRecording = false;
}
}
}
private void startRecordingForReal() {
// start streaming
if (mRecordThread == null) {
mRecordThread = new Thread(AudioFrequencyMicActivity.this);
mRecordThread.setName("FrequencyAnalyzerThread");
}
if (!mRecordThread.isAlive()) {
mRecordThread.start();
}
mPipe.flush();
long startTime = SystemClock.uptimeMillis();
mRecorder.startRecording();
if (mRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
stopRecording();
return;
}
Log.v(TAG, "Start time: " + (long) (SystemClock.uptimeMillis() - startTime) + " ms");
}
private void stopRecording() {
synchronized (mRecordingLock) {
stopRecordingForReal();
mIsRecording = false;
}
}
private void stopRecordingForReal() {
// stop streaming
Thread zeThread = mRecordThread;
mRecordThread = null;
if (zeThread != null) {
zeThread.interrupt();
try {
zeThread.join();
} catch(InterruptedException e) {
//restore interrupted status of recording thread
zeThread.interrupt();
}
}
// release recording resources
if (mRecorder != null) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}
private boolean initRecord() {
int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSamplingRate,
mChannelConfig, mAudioFormat);
Log.v(TAG,"FrequencyAnalyzer: min buff size = " + minRecordBuffSizeInBytes + " bytes");
if (minRecordBuffSizeInBytes <= 0) {
return false;
}
mMinRecordBufferSizeInSamples = minRecordBuffSizeInBytes / 2;
// allocate the byte array to read the audio data
mAudioShortArray = new short[mMinRecordBufferSizeInSamples];
Log.v(TAG, "Initiating record:");
Log.v(TAG, " using source " + mSelectedRecordSource);
Log.v(TAG, " at " + mSamplingRate + "Hz");
try {
mRecorder = new AudioRecord(mSelectedRecordSource, mSamplingRate,
mChannelConfig, mAudioFormat, 2 * minRecordBuffSizeInBytes);
} catch (IllegalArgumentException e) {
return false;
}
if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
mRecorder.release();
mRecorder = null;
return false;
}
mRecorder.setRecordPositionUpdateListener(this);
mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2);
return true;
}
// ---------------------------------------------------------
// Implementation of AudioRecord.OnPeriodicNotificationListener
// --------------------
public void onPeriodicNotification(AudioRecord recorder) {
int samplesAvailable = mPipe.availableToRead();
int samplesNeeded = mBlockSizeSamples;
if (samplesAvailable >= samplesNeeded) {
mPipe.read(mAudioShortArray2, 0, samplesNeeded);
//compute stuff.
int clipcount = 0;
double sum = 0;
double maxabs = 0;
int i;
for (i = 0; i < samplesNeeded; i++) {
double value = mAudioShortArray2[i] / MAX_VAL;
double valueabs = Math.abs(value);
if (valueabs > maxabs) {
maxabs = valueabs;
}
if (valueabs > CLIP_LEVEL) {
clipcount++;
}
sum += value * value;
//fft stuff
mData.mData[i] = value;
}
//for the current frame, compute FFT and send to the viewer.
//apply window and pack as complex for now.
DspBufferMath.mult(mData, mData, mWindow.mBuffer);
DspBufferMath.set(mC, mData);
mFftServer.fft(mC, 1);
double[] halfMagnitude = new double[mBlockSizeSamples / 2];
for (i = 0; i < mBlockSizeSamples / 2; i++) {
halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]);
}
switch(mCurrentTest) {
case TEST_NOISE:
mFreqAverageNoise.setData(halfMagnitude, false);
break;
case TEST_USB_BACKGROUND:
mFreqAverageUsbBackground.setData(halfMagnitude, false);
break;
case TEST_USB_NOISE:
mFreqAverageUsbNoise.setData(halfMagnitude, false);
break;
}
}
}
public void onMarkerReached(AudioRecord track) {
}
// ---------------------------------------------------------
// Implementation of Runnable for the audio recording + playback
// --------------------
public void run() {
Thread thisThread = Thread.currentThread();
while (!thisThread.isInterrupted()) {
// read from native recorder
int nSamplesRead = mRecorder.read(mAudioShortArray, 0, mMinRecordBufferSizeInSamples);
if (nSamplesRead > 0) {
mPipe.write(mAudioShortArray, 0, nSamplesRead);
}
}
}
}