Updates based on user feedback
- Checks that the playback volume is not zero.
- Experiments which generate multiple recordings (just GainLinearityExperiment
currently) now attach their recordings to the results e-mail, just like
single recording experiments. This adds another four attachments to the
e-mail, but is required to investigate gain linearity problems.
- Clipping test renamed to Overflow test to conform with Diagnostic Suite
terminology.
- Updated README with additional supported loudspeaker and better instructions.
- Fixed bug which caused the experiments list to be populated more than once
under some circumstances.
Change-Id: I6ad05c4657194d04d7d99b9f86ef157942459de4
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 0c9b198..12211ee 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -107,7 +107,7 @@
<string name="aq_spectrum_shape_exp">Spectrum shape test</string>
<string name="aq_glitch_exp">Glitch test</string>
<string name="aq_linearity_exp">Gain linearity test</string>
- <string name="aq_clipping_exp">Clipping check</string>
+ <string name="aq_overflow_exp">Overflow check</string>
<string name="aq_bias_exp">Bias measurement</string>
<!-- Experiment outcomes -->
@@ -118,10 +118,10 @@
<!-- Experiment reports -->
<string name="aq_loopback_report">Experiment ran successfully.</string>
<string name="aq_bias_report">Mean = %1$.3g, tolerance = +/- %2$.0f\nRMS = %3$.0f, duration = %4$.1fs</string>
- <string name="aq_clipping_report_error">Overflow check unsuccessful</string>
- <string name="aq_clipping_report_short">Insufficient tone detected.\nExpected %1$.1fs tone; observed %2$.1fs</string>
- <string name="aq_clipping_report_fail">"Clipping check failed due to discontinuities.\nObserved %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
- <string name="aq_clipping_report_pass">"Observed %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
+ <string name="aq_overflow_report_error">Overflow check unsuccessful</string>
+ <string name="aq_overflow_report_short">Insufficient tone detected.\nExpected %1$.1fs tone; observed %2$.1fs</string>
+ <string name="aq_overflow_report_fail">"Overflow check failed due to discontinuities.\nObserved %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
+ <string name="aq_overflow_report_pass">"Observed %1$d bad frames\nTone duration %2$.1fs\nMin peak = %3$.0f, max = %4$.0f</string>
<string name="aq_linearity_report_error">Experiment failed, error code %1$g</string>
<string name="aq_linearity_report_normal">Deviation from linearity = %1$.3g dB\nMax allowed = %2$.1f dB</string>
<string name="aq_glitch_report_error">Error performing Glitch test.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
index 9487a50..8879a0f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/AudioQualityVerifierActivity.java
@@ -26,7 +26,9 @@
import android.graphics.Color;
import android.graphics.Typeface;
import android.media.AudioFormat;
+import android.media.AudioManager;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -50,6 +52,7 @@
public static final int SAMPLE_RATE = 16000;
public static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
public static final int BYTES_PER_SAMPLE = 2;
+ public static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC;
// Intent Extra definitions, which must match those in
// com.google.android.voicesearch.speechservice.RecognitionController
@@ -127,12 +130,26 @@
fillAdapter();
mList.setAdapter(mAdapter);
mList.setOnItemClickListener(this);
+ checkNotSilent();
}
@Override
public void onResume() {
super.onResume();
mAdapter.notifyDataSetChanged(); // Update List UI
+ checkNotSilent();
+ }
+
+ private void checkNotSilent() {
+ AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ mgr.setStreamMute(PLAYBACK_STREAM, false);
+ int volume = mgr.getStreamVolume(PLAYBACK_STREAM);
+ int max = mgr.getStreamMaxVolume(PLAYBACK_STREAM);
+ Log.i(TAG, "Volume " + volume + ", max " + max);
+ if (volume <= max / 10) {
+ // Volume level is silent or very quiet; increase to two-thirds
+ mgr.setStreamVolume(PLAYBACK_STREAM, (max * 2) / 3, AudioManager.FLAG_SHOW_UI);
+ }
}
// Called when an experiment has completed
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java
index 067a3d5..e22d596 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/BackgroundAudio.java
@@ -17,7 +17,6 @@
package com.android.cts.verifier.audioquality;
import android.media.AudioFormat;
-import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
@@ -57,7 +56,7 @@
// Start playback:
Log.i(TAG, "Looping " + data.length + " bytes of audio, buffer size " + mBufferSize);
- mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+ mAudioTrack = new AudioTrack(AudioQualityVerifierActivity.PLAYBACK_STREAM,
AudioQualityVerifierActivity.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
AudioQualityVerifierActivity.AUDIO_FORMAT, mBufferSize, AudioTrack.MODE_STREAM);
if (mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java
index 18727f4..6f2aa83 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Experiment.java
@@ -16,6 +16,8 @@
package com.android.cts.verifier.audioquality;
+import java.util.ArrayList;
+import java.util.List;
import com.android.cts.verifier.R;
import android.content.Context;
@@ -32,7 +34,7 @@
private String mName;
private String mScore;
private String mReport;
- private String mAudioFileName;
+ private List<String> mAudioFileNames;
enum Status { NotStarted, Running, Stopped, Completed }
private Status mStatus;
@@ -68,7 +70,7 @@
mStatus = Status.NotStarted;
mScore = "";
mReport = "";
- mAudioFileName = null;
+ mAudioFileNames = new ArrayList<String>();
}
public void start() {
@@ -94,15 +96,21 @@
}
public void setRecording(byte[] data) {
- // Save captured data to file
- mAudioFileName = Utils.getExternalDir(mContext, this) + "/"
- + Utils.cleanString(getName()) + ".raw";
- Log.i(TAG, "Saving recorded data to " + mAudioFileName);
- Utils.saveFile(mAudioFileName, data);
+ setRecording(data, -1);
}
- public String getAudioFileName() {
- return mAudioFileName;
+ public void setRecording(byte[] data, int num) {
+ // Save captured data to file
+ String filename = Utils.getExternalDir(mContext, this) + "/"
+ + Utils.cleanString(getName())
+ + (num == -1 ? "" : "_" + String.valueOf(num)) + ".raw";
+ Log.i(TAG, "Saving recorded data to " + filename);
+ Utils.saveFile(filename, data);
+ mAudioFileNames.add(filename);
+ }
+
+ public List<String> getAudioFileNames() {
+ return mAudioFileNames;
}
// Timeout in seconds
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt
new file mode 100644
index 0000000..3f9a59c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/README.txt
@@ -0,0 +1,96 @@
+Android Audio Quality Verifier App
+==================================
+
+This app runs a set of audio quality tests on an Android device,
+to verify the end-end sound recording path.
+
+If any of these tests fail, the device is probably unsuitable
+for demanding audio tasks such as speech recognition.
+If all the tests pass, the device audio is of a good standard.
+Note that not all possible audio defects can be detected by this
+test suite, so passing does not guarantee ideal audio quality.
+
+Hardware setup
+--------------
+
+The required physical set-up consists of a powered speaker,
+connected to the Android's headphone output by a standard
+audio cable.
+
+For loudspeakers which come in pairs, you only need to use
+one speaker (typically the powered or master speaker); you
+can leave the second speaker disconnected.
+If the speakers are stereo within a single unit (sometimes
+with speakers facing in opposite directions), place the phone
+in front of either of them.
+Speakers with multiple drivers per channel (e.g. a tweeter
+and a woofer) are not suitable.
+
+The phone should be placed in front of the centre of the
+speaker cone. The distance from the speaker will be adjusted
+during calibration; typically you could expect it to be around
+3cm or so.
+Use a supporting platform such as a stack of books to raise
+the phone to the correct height to line up with the speaker.
+
+Bluetooth connection is possible but cable connection is
+usually preferable.
+
+Recommended loudspeakers
+------------------------
+
+Using suitable loudspeakers ensures that test failures highlight
+problems with the Android device under test, and not limitations
+of the loudspeakers. The following loudspeakers work well for this
+purpose:
+
+1. Yamaha NX-B02
+
+Use on AC power, not batteries.
+This speaker works well with Bluetooth as well as a wired connection.
+Note that it's not uncommon for the devices to exhibit different
+bugs under Bluetooth.
+
+2. Cakewalk MA-7A (Edirol / Roland)
+
+The "Bass Enhancer" feature MUST be switched off.
+Note that it turns itself on again every time the speakers are
+powered on, so it is easy to forget to switch it off!
+
+Software setup
+--------------
+
+1. Build the application's apk.
+2. Install the apk using adb.
+3. Run the app.
+4. Click "Calibrate". Position the phone as described in
+ Hardware setup above, with the microphone facing the speaker,
+ and adjust the volume of the speaker until the status message
+ indicates it is correct.
+5. Click on any test in the list to run it, or "Run All" to run
+ each test in sequence.
+6. Click "Results" to view the outcomes. A correctly functioning
+ device should pass all tests.
+7. Click "Send by email" from the results page to send the
+ results to an e-mail address of your choice. The recordings
+ made are also attached as raw 16 bit, 16 kHz audio files to
+ help you diagnose any failed tests.
+
+Q&A
+---
+
+Q. What if the sound level check fails?
+A. Go back to the calibration step before running any other test.
+ Make sure the device has not been moved.
+ We also recommend that once the setup is calibrated there are no
+ moving objects or people near the device under test, since these
+ will change the acoustic properties of the environment from the
+ calibrated state.
+
+Q. Some of the tests sound very loud. Is this normal?
+A. The clipping test will generally be very loud indeed;
+ the others should be at a moderate volume.
+
+Q. What sort of room should the tests be performed in?
+A. Any, as long as the background noise levels are kept low, to
+ avoid interference with the test recordings.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java
index a65373c..885e18c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/Utils.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.media.AudioFormat;
-import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Environment;
import android.util.Log;
@@ -307,7 +306,7 @@
public static void playRaw(byte[] data) {
Log.i(TAG, "Playing " + data.length + " bytes of pre-recorded audio");
- AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, AudioQualityVerifierActivity.SAMPLE_RATE,
+ AudioTrack at = new AudioTrack(AudioQualityVerifierActivity.PLAYBACK_STREAM, AudioQualityVerifierActivity.SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO, AudioQualityVerifierActivity.AUDIO_FORMAT,
data.length, AudioTrack.MODE_STREAM);
writeAudio(at, data);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java
index f0f0aa8..f800907 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/VerifierExperiments.java
@@ -17,7 +17,7 @@
package com.android.cts.verifier.audioquality;
import com.android.cts.verifier.audioquality.experiments.BiasExperiment;
-import com.android.cts.verifier.audioquality.experiments.ClippingExperiment;
+import com.android.cts.verifier.audioquality.experiments.OverflowExperiment;
import com.android.cts.verifier.audioquality.experiments.GainLinearityExperiment;
import com.android.cts.verifier.audioquality.experiments.GlitchExperiment;
import com.android.cts.verifier.audioquality.experiments.SoundLevelExperiment;
@@ -42,7 +42,7 @@
mExperiments = new ArrayList<Experiment>();
mExperiments.add(new SoundLevelExperiment());
mExperiments.add(new BiasExperiment());
- mExperiments.add(new ClippingExperiment());
+ mExperiments.add(new OverflowExperiment());
mExperiments.add(new GainLinearityExperiment());
mExperiments.add(new SpectrumShapeExperiment());
mExperiments.add(new GlitchExperiment(0));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java
index 87901b2..6563335 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/ViewResultsActivity.java
@@ -30,6 +30,7 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.List;
/**
* This Activity allows the user to examine the results of the
@@ -79,8 +80,8 @@
ArrayList<Parcelable> attachments = new ArrayList<Parcelable>();
for (Experiment exp : mExperiments) {
- String filename = exp.getAudioFileName();
- if (filename != null) {
+ List<String> filenames = exp.getAudioFileNames();
+ for (String filename : filenames) {
attachments.add(Uri.fromFile(new File(filename)));
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ClippingExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java
similarity index 86%
rename from apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ClippingExperiment.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java
index deb3709..71deac8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/ClippingExperiment.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/OverflowExperiment.java
@@ -32,7 +32,7 @@
* by checking if the signal has any discontinuities (which might indicate
* wraparound, for example).
*/
-public class ClippingExperiment extends LoopbackExperiment {
+public class OverflowExperiment extends LoopbackExperiment {
private static final float FREQ = 250.0f;
private static final float AMPL = 32768.0f * 1.1f * CalibrateVolumeActivity.OUTPUT_AMPL
/ CalibrateVolumeActivity.TARGET_AMPL;
@@ -40,13 +40,13 @@
private static final float MIN_DURATION = DURATION * 0.9f;
private static final float RAMP = 0.01f;
- public ClippingExperiment() {
+ public OverflowExperiment() {
super(true);
}
@Override
protected String lookupName(Context context) {
- return context.getString(R.string.aq_clipping_exp);
+ return context.getString(R.string.aq_overflow_exp);
}
@Override
@@ -68,18 +68,18 @@
if (error < 0.0f) {
setScore(getString(R.string.aq_fail));
- setReport(getString(R.string.aq_clipping_report_error));
+ setReport(getString(R.string.aq_overflow_report_error));
} else if (duration < MIN_DURATION) {
setScore(getString(R.string.aq_fail));
- setReport(String.format(getString(R.string.aq_clipping_report_short),
+ setReport(String.format(getString(R.string.aq_overflow_report_short),
DURATION, duration));
} else if (numDeltas > 0) {
setScore(getString(R.string.aq_fail));
- setReport(String.format(getString(R.string.aq_clipping_report_fail),
+ setReport(String.format(getString(R.string.aq_overflow_report_fail),
numDeltas, duration, minPeak, maxPeak));
} else {
setScore(getString(R.string.aq_pass));
- setReport(String.format(getString(R.string.aq_clipping_report_pass),
+ setReport(String.format(getString(R.string.aq_overflow_report_pass),
numDeltas, duration, minPeak, maxPeak));
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java
index 26a9a1d..0a3846c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audioquality/experiments/SequenceExperiment.java
@@ -54,6 +54,7 @@
for (int trial = 0; trial < n; trial++) {
playbackData[trial] = getStim(mContext, trial);
recordedData[trial] = loopback(playbackData[trial]);
+ setRecording(recordedData[trial], trial);
}
compare(playbackData, recordedData);
mTerminator.terminate(false);