blob: 92257cf0cded974b205d16f003cc0b62595007f0 [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 com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import android.content.Context;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
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.TextView;
import android.widget.SeekBar;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
* Tests Audio built in Microphone response for Unprocessed audio source feature.
*/
@CddTest(requirement="5.11/C-1-1,C-1-2,C-1-3,C-1-4,C-1-5,C-1-6")
public class AudioFrequencyUnprocessedActivity extends AudioFrequencyActivity implements Runnable,
AudioRecord.OnRecordPositionUpdateListener {
private static final String TAG = "AudioFrequencyUnprocessedActivity";
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_FRACTION_POINTS_IN_BAND = 0.5;
private static final double TONE_RMS_EXPECTED = -36.0;
private static final double TONE_RMS_MAX_ERROR = 3.0;
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 SOURCE_TONE = 0;
private static final int SOURCE_NOISE = 1;
private static final int TEST_NONE = -1;
private static final int TEST_TONE = 0;
private static final int TEST_NOISE = 1;
private static final int TEST_USB_BACKGROUND = 2;
private static final int TEST_USB_NOISE = 3;
private static final int TEST_COUNT = 4;
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_TONE = TEST_DURATION_DEFAULT;
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();
Context mContext;
LinearLayout mLayoutTestTone;
Button mButtonTestTone;
ProgressBar mProgressTone;
TextView mResultTestTone;
Button mButtonPlayTone;
LinearLayout mLayoutTestNoise;
Button mButtonTestNoise;
ProgressBar mProgressNoise;
TextView mResultTestNoise;
Button mButtonPlayNoise;
LinearLayout mLayoutTestUsbBackground;
Button mButtonTestUsbBackground;
ProgressBar mProgressUsbBackground;
TextView mResultTestUsbBackground;
LinearLayout mLayoutTestUsbNoise;
Button mButtonTestUsbNoise;
ProgressBar mProgressUsbNoise;
TextView mResultTestUsbNoise;
Button mButtonPlayUsbNoise;
TextView mGlobalResultText;
TextView mTextViewUnprocessedStatus;
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 = 4096;
private final int mSamplingRate = 48000;
private final int mSelectedRecordSource = MediaRecorder.AudioSource.UNPROCESSED;
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 boolean mSupportsUnprocessed = false;
private DspBufferComplex mC;
private DspBufferDouble mData;
private DspWindow mWindow;
private DspFftServer mFftServer;
private VectorAverage mFreqAverageTone = new VectorAverage();
private VectorAverage mFreqAverageNoise = new VectorAverage();
private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
//RMS for tone:
private double mRMS;
private double mRMSMax;
private double mRMSTone;
private double mRMSMaxTone;
int mBands = 3;
int mBandsTone = 3;
int mBandsBack = 3;
AudioBandSpecs[] mBandSpecsMic = new AudioBandSpecs[mBands];
AudioBandSpecs[] mBandSpecsTone = new AudioBandSpecs[mBandsTone];
AudioBandSpecs[] mBandSpecsBack = new AudioBandSpecs[mBandsBack];
private Results mResultsMic;
private Results mResultsTone;
private Results mResultsBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_unprocessed_activity);
mContext = this;
mTextViewUnprocessedStatus = (TextView) findViewById(
R.id.audio_frequency_unprocessed_defined);
//unprocessed test
mSupportsUnprocessed = supportsUnprocessed();
if (mSupportsUnprocessed) {
mTextViewUnprocessedStatus.setText(
getResources().getText(R.string.audio_frequency_unprocessed_defined));
} else {
mTextViewUnprocessedStatus.setText(
getResources().getText(R.string.audio_frequency_unprocessed_not_defined));
}
mSPlayer = new SoundPlayerObject();
playerSetSource(SOURCE_TONE);
// Test tone
mLayoutTestTone = (LinearLayout) findViewById(R.id.unprocessed_layout_test_tone);
mButtonTestTone = (Button) findViewById(R.id.unprocessed_test_tone_btn);
mButtonTestTone.setOnClickListener(mBtnClickListener);
mProgressTone = (ProgressBar) findViewById(R.id.unprocessed_test_tone_progress_bar);
mResultTestTone = (TextView) findViewById(R.id.unprocessed_test_tone_result);
mButtonPlayTone = (Button) findViewById(R.id.unprocessed_play_tone_btn);
mButtonPlayTone.setOnClickListener(mBtnClickListener);
showWait(mProgressTone, false);
//Test Noise
mLayoutTestNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_noise);
mButtonTestNoise = (Button) findViewById(R.id.unprocessed_test_noise_btn);
mButtonTestNoise.setOnClickListener(mBtnClickListener);
mProgressNoise = (ProgressBar) findViewById(R.id.unprocessed_test_noise_progress_bar);
mResultTestNoise = (TextView) findViewById(R.id.unprocessed_test_noise_result);
mButtonPlayNoise = (Button) findViewById(R.id.unprocessed_play_noise_btn);
mButtonPlayNoise.setOnClickListener(mBtnClickListener);
showWait(mProgressNoise, false);
//USB Background
mLayoutTestUsbBackground = (LinearLayout)
findViewById(R.id.unprocessed_layout_test_usb_background);
mButtonTestUsbBackground = (Button) findViewById(R.id.unprocessed_test_usb_background_btn);
mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
mProgressUsbBackground = (ProgressBar)
findViewById(R.id.unprocessed_test_usb_background_progress_bar);
mResultTestUsbBackground = (TextView)
findViewById(R.id.unprocessed_test_usb_background_result);
showWait(mProgressUsbBackground, false);
mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_usb_noise);
mButtonTestUsbNoise = (Button) findViewById(R.id.unprocessed_test_usb_noise_btn);
mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
mProgressUsbNoise = (ProgressBar)findViewById(R.id.unprocessed_test_usb_noise_progress_bar);
mResultTestUsbNoise = (TextView) findViewById(R.id.unprocessed_test_usb_noise_result);
mButtonPlayUsbNoise = (Button) findViewById(R.id.unprocessed_play_usb_noise_btn);
mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
showWait(mProgressUsbNoise, false);
setButtonPlayStatus(-1);
mGlobalResultText = (TextView) findViewById(R.id.unprocessed_test_global_result);
//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_unprocessed_test,
R.string.audio_frequency_unprocessed_info, -1);
//Init bands for Mic test
mBandSpecsMic[0] = new AudioBandSpecs(
5, 100, /* frequency start,stop */
20.0, -20.0, /* start top,bottom value */
20.0, -20.0 /* stop top,bottom value */);
mBandSpecsMic[1] = new AudioBandSpecs(
100, 7000, /* frequency start,stop */
10.0, -10.0, /* start top,bottom value */
10.0, -10.0 /* stop top,bottom value */);
mBandSpecsMic[2] = new AudioBandSpecs(
7000, 20000, /* frequency start,stop */
30.0, -30.0, /* start top,bottom value */
30.0, -30.0 /* stop top,bottom value */);
//Init bands for Tone test
mBandSpecsTone[0] = new AudioBandSpecs(
5, 900, /* frequency start,stop */
-10.0, -100.0, /* start top,bottom value */
-10.0, -100.0 /* stop top,bottom value */);
mBandSpecsTone[1] = new AudioBandSpecs(
900, 1100, /* frequency start,stop */
10.0, -50.0, /* start top,bottom value */
10.0, -10.0 /* stop top,bottom value */);
mBandSpecsTone[2] = new AudioBandSpecs(
1100, 20000, /* frequency start,stop */
-30.0, -120.0, /* start top,bottom value */
-30.0, -120.0 /* stop top,bottom value */);
//Init bands for Background test
mBandSpecsBack[0] = new AudioBandSpecs(
5, 100, /* frequency start,stop */
10.0, -120.0, /* start top,bottom value */
-10.0, -120.0 /* stop top,bottom value */);
mBandSpecsBack[1] = new AudioBandSpecs(
100, 7000, /* frequency start,stop */
-10.0, -120.0, /* start top,bottom value */
-50.0, -120.0 /* stop top,bottom value */);
mBandSpecsBack[2] = new AudioBandSpecs(
7000, 20000, /* frequency start,stop */
-50.0, -120.0, /* start top,bottom value */
-50.0, -120.0 /* stop top,bottom value */);
mSupportsUnprocessed = supportsUnprocessed();
Log.v(TAG, "Supports unprocessed: " + mSupportsUnprocessed);
mResultsMic = new Results("mic_response", mBands);
mResultsTone = new Results("tone_response", mBandsTone);
mResultsBack = new Results("background_response", mBandsBack);
}
private void playerToggleButton(int buttonId, int sourceId) {
if (playerIsPlaying()) {
playerStopAll();
} else {
playerSetSource(sourceId);
playerTransport(true);
setButtonPlayStatus(buttonId);
}
}
private class OnBtnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.unprocessed_test_tone_btn:
startTest(TEST_TONE);
break;
case R.id.unprocessed_play_tone_btn:
playerToggleButton(id, SOURCE_TONE);
break;
case R.id.unprocessed_test_noise_btn:
startTest(TEST_NOISE);
break;
case R.id.unprocessed_play_noise_btn:
playerToggleButton(id, SOURCE_NOISE);
break;
case R.id.unprocessed_test_usb_background_btn:
startTest(TEST_USB_BACKGROUND);
break;
case R.id.unprocessed_test_usb_noise_btn:
startTest(TEST_USB_NOISE);
break;
case R.id.unprocessed_play_usb_noise_btn:
playerToggleButton(id, SOURCE_NOISE);
break;
}
}
}
private void setButtonPlayStatus(int playResId) {
String play = getResources().getText(R.string.unprocessed_play).toString();
String stop = getResources().getText(R.string.unprocessed_stop).toString();
mButtonPlayTone.setText(playResId == R.id.unprocessed_play_tone_btn ? stop : play);
mButtonPlayNoise.setText(playResId == R.id.unprocessed_play_noise_btn ? stop : play);
mButtonPlayUsbNoise.setText(playResId ==
R.id.unprocessed_play_usb_noise_btn ? stop : play);
}
private void playerSetSource(int sourceIndex) {
switch (sourceIndex) {
case SOURCE_TONE:
mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.onekhztone);
break;
default:
case SOURCE_NOISE:
mSPlayer.setSoundWithResId(getApplicationContext(),
R.raw.stereo_mono_white_noise_48);
break;
}
}
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);
}
}
/**
* enable test ui elements
*/
private void enableLayout(LinearLayout layout, boolean enable) {
for (int i = 0; i < layout.getChildCount(); i++) {
View view = layout.getChildAt(i);
view.setEnabled(enable);
}
}
private void showWait(ProgressBar pb, boolean show) {
if (show) {
pb.setVisibility(View.VISIBLE);
} else {
pb.setVisibility(View.INVISIBLE);
}
}
private String getTestString(int testId) {
String name = "undefined";
switch(testId) {
case TEST_TONE:
name = "BuiltIn_tone";
break;
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 showWait(int testId, boolean show) {
switch(testId) {
case TEST_TONE:
showWait(mProgressTone, show);
break;
case TEST_NOISE:
showWait(mProgressNoise, show);
break;
case TEST_USB_BACKGROUND:
showWait(mProgressUsbBackground, show);
break;
case TEST_USB_NOISE:
showWait(mProgressUsbNoise, show);
break;
}
}
private void showMessage(int testId, String msg) {
if (msg != null && msg.length() > 0) {
switch(testId) {
case TEST_TONE:
mResultTestTone.setText(msg);
break;
case TEST_NOISE:
mResultTestNoise.setText(msg);
break;
case TEST_USB_BACKGROUND:
mResultTestUsbBackground.setText(msg);
break;
case TEST_USB_NOISE:
mResultTestUsbNoise.setText(msg);
break;
}
}
}
private boolean supportsUnprocessed() {
boolean unprocessedSupport = false;
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String unprocessedSupportString = am.getProperty(
AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED);
Log.v(TAG,"unprocessed support: " + unprocessedSupportString);
if (unprocessedSupportString == null ||
unprocessedSupportString.equalsIgnoreCase(getResources().getString(
R.string.audio_general_default_false_string))) {
unprocessedSupport = false;
} else {
unprocessedSupport = true;
}
return unprocessedSupport;
}
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 void processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand) {
int points = results.mValuesLog.length;
int bandCount = bandsSpecs.length;
int currentBand = 0;
for (int i = 0; i < points; i++) {
double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
if (freq > bandsSpecs[currentBand].mFreqStop) {
currentBand++;
if (currentBand >= bandCount)
break;
}
if (freq >= bandsSpecs[currentBand].mFreqStart) {
results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
results.mPointsPerBand[currentBand]++;
}
}
for (int b = 0; b < bandCount; b++) {
if (results.mPointsPerBand[b] > 0) {
results.mAverageEnergyPerBand[b] =
results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
}
}
//set offset relative to band anchor band level
for (int b = 0; b < bandCount; b++) {
if (anchorBand > -1 && anchorBand < bandCount) {
bandsSpecs[b].setOffset(results.mAverageEnergyPerBand[anchorBand]);
} else {
bandsSpecs[b].setOffset(0);
}
}
//test points in band.
currentBand = 0;
for (int i = 0; i < points; i++) {
double freq = (double) mSamplingRate * i / (double) mBlockSizeSamples;
if (freq > bandsSpecs[currentBand].mFreqStop) {
currentBand++;
if (currentBand >= bandCount)
break;
}
if (freq >= bandsSpecs[currentBand].mFreqStart) {
double value = results.mValuesLog[i];
if (bandsSpecs[currentBand].isInBounds(freq, value)) {
results.mInBoundPointsPerBand[currentBand]++;
}
}
}
}
private String computeResults() {
StringBuilder sb = new StringBuilder();
int points = mFreqAverageNoise.getSize();
//mFreqAverageNoise size is determined by the latest data writen to it.
//Currently, this data is always mBlockSizeSamples/2.
if (points < 1) {
return "error: not enough points";
}
double[] tone = new double[points];
double[] noise = new double[points];
double[] reference = new double[points];
double[] background = new double[points];
mFreqAverageTone.getData(tone, false);
mFreqAverageNoise.getData(noise, false);
mFreqAverageUsbNoise.getData(reference, false);
mFreqAverageUsbBackground.getData(background, false);
//Convert to dB
double[] toneDb = new double[points];
double[] noiseDb = new double[points];
double[] referenceDb = new double[points];
double[] backgroundDb = new double[points];
double[] compensatedNoiseDb = new double[points];
for (int i = 0; i < points; i++) {
toneDb[i] = 20 * Math.log10(tone[i]);
noiseDb[i] = 20 * Math.log10(noise[i]);
referenceDb[i] = 20 * Math.log10(reference[i]);
backgroundDb[i] = 20 * Math.log10(background[i]);
compensatedNoiseDb[i] = noiseDb[i] - referenceDb[i];
}
mResultsMic.reset();
mResultsTone.reset();
mResultsBack.reset();
mResultsMic.mValuesLog = compensatedNoiseDb;
mResultsTone.mValuesLog = toneDb;
mResultsBack.mValuesLog = backgroundDb;
processSpectrum(mResultsMic, mBandSpecsMic, 1);
processSpectrum(mResultsTone, mBandSpecsTone, 1);
processSpectrum(mResultsBack, mBandSpecsBack, -1); //no reference for offset
//Tone test
boolean toneTestSuccess = true;
{
//rms level should be -36 dbfs +/- 3 db?
double rmsMaxDb = 20 * Math.log10(mRMSMaxTone);
sb.append(String.format("RMS level of tone: %.2f dBFS\n", rmsMaxDb));
sb.append(String.format("Target RMS level: %.2f dBFS +/- %.2f dB\n",
TONE_RMS_EXPECTED,
TONE_RMS_MAX_ERROR));
if (Math.abs(rmsMaxDb - TONE_RMS_EXPECTED) > TONE_RMS_MAX_ERROR) {
toneTestSuccess = false;
sb.append("RMS level test FAILED\n");
} else {
sb.append(" RMS level test SUCCESSFUL\n");
}
//check the spectrum is really a tone around 1 khz
}
sb.append("\n");
sb.append(mResultsTone.toString());
if (mResultsTone.testAll()) {
sb.append(" 1 Khz Tone Frequency Response Test SUCCESSFUL\n");
} else {
sb.append(" 1 Khz Tone Frequency Response Test FAILED\n");
}
sb.append("\n");
sb.append("\n");
sb.append(mResultsBack.toString());
if (mResultsBack.testAll()) {
sb.append(" Background environment Test SUCCESSFUL\n");
} else {
sb.append(" Background environment Test FAILED\n");
}
sb.append("\n");
sb.append(mResultsMic.toString());
if (mResultsMic.testAll()) {
sb.append(" Frequency Response Test SUCCESSFUL\n");
} else {
sb.append(" Frequency Response Test FAILED\n");
}
sb.append("\n");
recordTestResults(mResultsTone);
recordTestResults(mResultsMic);
boolean allTestsPassed = false;
if (mResultsMic.testAll() && mResultsTone.testAll() && toneTestSuccess &&
mResultsBack.testAll()) {
allTestsPassed = true;
String strSuccess = getResources().getString(R.string.audio_general_test_passed);
sb.append(strSuccess);
} else {
String strFailed = getResources().getString(R.string.audio_general_test_failed);
sb.append(strFailed);
}
sb.append("\n");
if (mSupportsUnprocessed) { //test is mandatory
sb.append(getResources().getText(
R.string.audio_frequency_unprocessed_defined).toString());
if (allTestsPassed) {
getPassButton().setEnabled(true);
} else {
getPassButton().setEnabled(false);
}
} else {
//test optional
sb.append(getResources().getText(
R.string.audio_frequency_unprocessed_not_defined).toString());
getPassButton().setEnabled(true);
}
return sb.toString();
}
Thread mTestThread;
private void startTest(int testId) {
if (mTestThread != null && !mTestThread.isAlive()) {
mTestThread = null; //kill it.
}
if (mTestThread == null) {
mRMS = 0;
mRMSMax = 0;
Log.v(TAG,"Executing test Thread");
switch(testId) {
case TEST_TONE:
mTestThread = new Thread(new TestRunnable(TEST_TONE) {
public void run() {
super.run();
if (!mUsbMicConnected) {
sendMessage(mTestId, TEST_MESSAGE,
"Testing Built in Microphone: Tone");
mRMSTone = 0;
mRMSMaxTone = 0;
mFreqAverageTone.reset();
mFreqAverageTone.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
record(TEST_DURATION_TONE);
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_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(getApplicationContext());
};
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);
// getPassButton().setEnabled(false);
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 int mBandCount;
private String mLabel;
public double[] mValuesLog;
int[] mPointsPerBand; // = new int[mBands];
double[] mAverageEnergyPerBand;// = new double[mBands];
int[] mInBoundPointsPerBand;// = new int[mBands];
public Results(String label, int bandCount) {
mLabel = label;
mBandCount = bandCount;
mPointsPerBand = new int[mBandCount];
mAverageEnergyPerBand = new double[mBandCount];
mInBoundPointsPerBand = new int[mBandCount];
}
public void reset() {
for (int i = 0; i < mBandCount; i++) {
mPointsPerBand[i] = 0;
mAverageEnergyPerBand[i] = 0;
mInBoundPointsPerBand[i] = 0;
}
}
//append results
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Channel %s\n", mLabel));
for (int b = 0; b < mBandCount; 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 testInBand(int b) {
if (b >= 0 && b < mBandCount && mPointsPerBand[b] > 0) {
if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] >
MIN_FRACTION_POINTS_IN_BAND) {
return true;
}
}
return false;
}
public boolean testAll() {
for (int b = 0; b < mBandCount; b++) {
if (!testInBand(b)) {
return false;
}
}
return true;
}
}
// /**
// * compute test results
// */
// private void computeTestResults(int testId) {
// String testName = getTestString(testId);
// appendResultsToScreen(testId, "test finished");
// }
//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_TONE:
appendResultsToScreen(str, mResultTestTone);
showToneRMS();
break;
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 recordTestResults(Results results) {
String channelLabel = "channel_" + results.mLabel;
for (int b = 0; b < mBands; b++) {
String bandLabel = String.format(channelLabel + "_%d", b);
getReportLog().addValue(
bandLabel + "_Level",
results.mAverageEnergyPerBand[b],
ResultType.HIGHER_BETTER,
ResultUnit.NONE);
getReportLog().addValue(
bandLabel + "_pointsinbound",
results.mInBoundPointsPerBand[b],
ResultType.HIGHER_BETTER,
ResultUnit.COUNT);
getReportLog().addValue(
bandLabel + "_pointstotal",
results.mPointsPerBand[b],
ResultType.NEUTRAL,
ResultUnit.COUNT);
}
getReportLog().addValues(channelLabel + "_magnitudeSpectrumLog",
results.mValuesLog,
ResultType.NEUTRAL,
ResultUnit.NONE);
Log.v(TAG, "Results Recorded");
}
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(AudioFrequencyUnprocessedActivity.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;
}
private void showToneRMS() {
String str = String.format("RMS: %.3f dBFS. Max RMS: %.3f dBFS",
20 * Math.log10(mRMSTone),
20 * Math.log10(mRMSMaxTone));
showMessage(TEST_TONE, str);
}
// ---------------------------------------------------------
// 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;
double rmsTempSum = 0;
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++;
}
rmsTempSum += value * value;
//fft stuff
mData.mData[i] = value;
}
double rms = Math.sqrt(rmsTempSum / samplesNeeded);
double alpha = 0.9;
double total_rms = rms * alpha + mRMS *(1-alpha);
mRMS = total_rms;
if (mRMS > mRMSMax) {
mRMSMax = mRMS;
}
//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_TONE: {
mFreqAverageTone.setData(halfMagnitude, false);
//Update realtime info on screen
mRMSTone = mRMS;
mRMSMaxTone = mRMSMax;
showToneRMS();
}
break;
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);
}
}
}
}