1 /* 2 * Copyright (C) 2015 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 android.media.cts; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.media.AudioDeviceInfo; 23 import android.media.AudioManager; 24 25 import com.android.compatibility.common.util.CtsAndroidTestCase; 26 27 public class AudioNativeTest extends CtsAndroidTestCase { 28 public static final int MAX_CHANNEL_COUNT = 2; 29 public static final int MAX_INDEX_MASK = (1 << MAX_CHANNEL_COUNT) - 1; 30 31 private static final int CHANNEL_INDEX_MASK_MAGIC = 0x80000000; 32 33 public void testAppendixBBufferQueue() { 34 nativeAppendixBBufferQueue(); 35 } 36 37 public void testAppendixBRecording() { 38 // better to detect presence of microphone here. 39 if (!hasMicrophone()) { 40 return; 41 } 42 nativeAppendixBRecording(); 43 } 44 45 public void testStereo16Playback() { 46 assertTrue(AudioTrackNative.test( 47 2 /* numChannels */, 48000 /* sampleRate */, false /* useFloat */, 48 20 /* msecPerBuffer */, 8 /* numBuffers */)); 49 } 50 51 public void testStereo16Record() { 52 if (!hasMicrophone()) { 53 return; 54 } 55 assertTrue(AudioRecordNative.test( 56 2 /* numChannels */, 48000 /* sampleRate */, false /* useFloat */, 57 20 /* msecPerBuffer */, 8 /* numBuffers */)); 58 } 59 60 public void testPlayStreamData() throws Exception { 61 final String TEST_NAME = "testPlayStreamData"; 62 final boolean TEST_FLOAT_ARRAY[] = { 63 false, 64 true, 65 }; 66 // due to downmixer algorithmic latency, source channels greater than 2 may 67 // sound shorter in duration at 4kHz sampling rate. 68 final int TEST_SR_ARRAY[] = { 69 /* 4000, */ // below limit of OpenSL ES 70 12345, // irregular sampling rate 71 44100, 72 48000, 73 96000, 74 192000, 75 }; 76 final int TEST_CHANNELS_ARRAY[] = { 77 1, 78 2, 79 3, 80 4, 81 5, 82 6, 83 7, 84 // 8 // can fail due to memory issues 85 }; 86 final float TEST_SWEEP = 0; // sine wave only 87 final int TEST_TIME_IN_MSEC = 300; 88 final int TOLERANCE_MSEC = 20; 89 final boolean TEST_IS_LOW_RAM_DEVICE = isLowRamDevice(); 90 final long TEST_END_SLEEP_MSEC = TEST_IS_LOW_RAM_DEVICE ? 200 : 50; 91 92 for (boolean TEST_FLOAT : TEST_FLOAT_ARRAY) { 93 double frequency = 400; // frequency changes for each test 94 for (int TEST_SR : TEST_SR_ARRAY) { 95 for (int TEST_CHANNELS : TEST_CHANNELS_ARRAY) { 96 // OpenSL ES BUG: we run out of AudioTrack memory for this config on MNC 97 // Log.d(TEST_NAME, "open channels:" + TEST_CHANNELS + " sr:" + TEST_SR); 98 if (TEST_IS_LOW_RAM_DEVICE && (TEST_CHANNELS > 4 || TEST_SR > 96000)) { 99 continue; 100 } 101 if (TEST_FLOAT == true && TEST_CHANNELS >= 6 && TEST_SR >= 192000) { 102 continue; 103 } 104 AudioTrackNative track = new AudioTrackNative(); 105 assertTrue(TEST_NAME, 106 track.open(TEST_CHANNELS, TEST_SR, TEST_FLOAT, 1 /* numBuffers */)); 107 assertTrue(TEST_NAME, track.start()); 108 109 final int sourceSamples = 110 (int)((long)TEST_SR * TEST_TIME_IN_MSEC * TEST_CHANNELS / 1000); 111 final double testFrequency = frequency / TEST_CHANNELS; 112 if (TEST_FLOAT) { 113 float data[] = AudioHelper.createSoundDataInFloatArray( 114 sourceSamples, TEST_SR, 115 testFrequency, TEST_SWEEP); 116 assertEquals(sourceSamples, 117 track.write(data, 0 /* offset */, sourceSamples, 118 AudioTrackNative.WRITE_FLAG_BLOCKING)); 119 } else { 120 short data[] = AudioHelper.createSoundDataInShortArray( 121 sourceSamples, TEST_SR, 122 testFrequency, TEST_SWEEP); 123 assertEquals(sourceSamples, 124 track.write(data, 0 /* offset */, sourceSamples, 125 AudioTrackNative.WRITE_FLAG_BLOCKING)); 126 } 127 128 while (true) { 129 // OpenSL ES BUG: getPositionInMsec returns 0 after a data underrun. 130 131 long position = track.getPositionInMsec(); 132 //Log.d(TEST_NAME, "position: " + position[0]); 133 if (position >= (long)(TEST_TIME_IN_MSEC - TOLERANCE_MSEC)) { 134 break; 135 } 136 137 // It is safer to use a buffer count of 0 to determine termination 138 if (track.getBuffersPending() == 0) { 139 break; 140 } 141 Thread.sleep(5 /* millis */); 142 } 143 track.stop(); 144 Thread.sleep(TEST_END_SLEEP_MSEC); 145 track.close(); 146 Thread.sleep(TEST_END_SLEEP_MSEC); // put a gap in the tone sequence 147 frequency += 50; // increment test tone frequency 148 } 149 } 150 } 151 } 152 153 private boolean isLowRamDevice() { 154 return ((ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE) 155 ).isLowRamDevice(); 156 } 157 158 public void testRecordStreamData() throws Exception { 159 if (!hasMicrophone()) { 160 return; 161 } 162 final String TEST_NAME = "testRecordStreamData"; 163 final boolean TEST_FLOAT_ARRAY[] = { 164 false, 165 true, 166 }; 167 final int TEST_SR_ARRAY[] = { 168 //4000, // below limit of OpenSL ES 169 12345, // irregular sampling rate 170 44100, 171 48000, 172 96000, 173 192000, 174 }; 175 final int TEST_CHANNELS_ARRAY[] = { 176 1, 177 2, 178 3, 179 4, 180 5, 181 6, 182 7, 183 8, 184 }; 185 final int SEGMENT_DURATION_IN_MSEC = 20; 186 final int NUMBER_SEGMENTS = 10; 187 188 for (boolean TEST_FLOAT : TEST_FLOAT_ARRAY) { 189 for (int TEST_SR : TEST_SR_ARRAY) { 190 for (int TEST_CHANNELS : TEST_CHANNELS_ARRAY) { 191 // OpenSL ES BUG: we run out of AudioTrack memory for this config on MNC 192 if (TEST_FLOAT == true && TEST_CHANNELS >= 8 && TEST_SR >= 192000) { 193 continue; 194 } 195 AudioRecordNative record = new AudioRecordNative(); 196 doRecordTest(record, TEST_CHANNELS, TEST_SR, TEST_FLOAT, 197 SEGMENT_DURATION_IN_MSEC, NUMBER_SEGMENTS); 198 } 199 } 200 } 201 } 202 203 public void testRecordAudit() throws Exception { 204 if (!hasMicrophone()) { 205 return; 206 } 207 AudioRecordNative record = new AudioHelper.AudioRecordAuditNative(); 208 doRecordTest(record, 4 /* numChannels */, 44100 /* sampleRate */, false /* useFloat */, 209 1000 /* segmentDurationMs */, 10 /* numSegments */); 210 } 211 212 public void testOutputChannelMasks() { 213 if (!hasAudioOutput()) { 214 return; 215 } 216 AudioTrackNative track = new AudioTrackNative(); 217 218 int maxOutputChannels = 2; 219 int validIndexMask = (1 << maxOutputChannels) - 1; 220 221 for (int mask = 0; mask <= MAX_INDEX_MASK; ++mask) { 222 int channelCount = Long.bitCount(mask); 223 boolean expectSuccess = (channelCount > 0) 224 && ((mask & validIndexMask) != 0); 225 226 // TODO: uncomment this line when b/27484181 is fixed. 227 // expectSuccess &&= ((mask & ~validIndexMask) == 0); 228 229 boolean ok = track.open(channelCount, 230 mask | CHANNEL_INDEX_MASK_MAGIC, 48000, false, 2); 231 track.close(); 232 assertEquals(expectSuccess, ok); 233 } 234 } 235 236 public void testInputChannelMasks() { 237 if (!hasMicrophone()) { 238 return; 239 } 240 241 AudioManager audioManager = 242 (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 243 AudioRecordNative recorder = new AudioRecordNative(); 244 245 int maxInputChannels = 0; 246 for (AudioDeviceInfo deviceInfo : 247 audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) { 248 for (int channels : deviceInfo.getChannelCounts()) { 249 maxInputChannels = Math.max(channels, maxInputChannels); 250 } 251 } 252 253 int validIndexMask = (1 << maxInputChannels) - 1; 254 255 for (int mask = 0; mask <= MAX_INDEX_MASK; ++mask) { 256 int channelCount = Long.bitCount(mask); 257 boolean expectSuccess = (channelCount > 0) 258 && ((mask & validIndexMask) != 0); 259 260 // TODO: uncomment this line when b/27484181 is fixed. 261 // expectSuccess &&= ((mask & ~validIndexMask) == 0); 262 263 boolean ok = recorder.open(channelCount, 264 mask | CHANNEL_INDEX_MASK_MAGIC, 48000, false, 2); 265 recorder.close(); 266 assertEquals(expectSuccess, ok); 267 } 268 } 269 270 static { 271 System.loadLibrary("audio_jni"); 272 } 273 274 private static final String TAG = "AudioNativeTest"; 275 276 private void doRecordTest(AudioRecordNative record, 277 int numChannels, int sampleRate, boolean useFloat, 278 int segmentDurationMs, int numSegments) { 279 final String TEST_NAME = "doRecordTest"; 280 try { 281 // Log.d(TEST_NAME, "open numChannels:" + numChannels + " sampleRate:" + sampleRate); 282 assertTrue(TEST_NAME, record.open(numChannels, sampleRate, useFloat, 283 numSegments /* numBuffers */)); 284 assertTrue(TEST_NAME, record.start()); 285 286 final int sourceSamples = 287 (int)((long)sampleRate * segmentDurationMs * numChannels / 1000); 288 289 if (useFloat) { 290 float data[] = new float[sourceSamples]; 291 for (int i = 0; i < numSegments; ++i) { 292 assertEquals(sourceSamples, 293 record.read(data, 0 /* offset */, sourceSamples, 294 AudioRecordNative.READ_FLAG_BLOCKING)); 295 } 296 } else { 297 short data[] = new short[sourceSamples]; 298 for (int i = 0; i < numSegments; ++i) { 299 assertEquals(sourceSamples, 300 record.read(data, 0 /* offset */, sourceSamples, 301 AudioRecordNative.READ_FLAG_BLOCKING)); 302 } 303 } 304 assertTrue(TEST_NAME, record.stop()); 305 } finally { 306 record.close(); 307 } 308 } 309 310 private boolean hasMicrophone() { 311 return getContext().getPackageManager().hasSystemFeature( 312 PackageManager.FEATURE_MICROPHONE); 313 } 314 315 private boolean hasAudioOutput() { 316 return getContext().getPackageManager().hasSystemFeature( 317 PackageManager.FEATURE_AUDIO_OUTPUT); 318 } 319 320 private static native void nativeAppendixBBufferQueue(); 321 private static native void nativeAppendixBRecording(); 322 } 323