1 /* 2 * Copyright (C) 2016 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.audio.cts; 18 19 import android.media.AudioAttributes; 20 import android.media.AudioFormat; 21 import android.media.AudioManager; 22 import android.media.AudioTimestamp; 23 import android.media.AudioTrack; 24 import android.media.cts.AudioHelper; 25 import android.platform.test.annotations.AppModeFull; 26 import android.util.Log; 27 28 import com.android.compatibility.common.util.CtsAndroidTestCase; 29 import com.android.compatibility.common.util.NonMainlineTest; 30 31 // Test the Java AudioTrack low latency related features: 32 // 33 // setBufferSizeInFrames() 34 // getBufferCapacityInFrames() 35 // ASSUME getMinBufferSize in frames is significantly lower than getBufferCapacityInFrames. 36 // This gives us room to adjust the sizes. 37 // 38 // getUnderrunCount() 39 // ASSUME normal track will underrun with setBufferSizeInFrames(0). 40 // 41 // AudioAttributes.FLAG_LOW_LATENCY 42 // ASSUME FLAG_LOW_LATENCY reduces output latency by more than 10 msec. 43 // Warns if not. This can happen if there is no Fast Mixer or if a FastTrack 44 // is not available. 45 46 @NonMainlineTest 47 @AppModeFull(reason = "The APIs would either work correctly or not at all for instant apps") 48 public class AudioTrackLatencyTest extends CtsAndroidTestCase { 49 private String TAG = "AudioTrackLatencyTest"; 50 private final static long NANOS_PER_MILLISECOND = 1000000L; 51 private final static int MILLIS_PER_SECOND = 1000; 52 private final static long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND; 53 log(String testName, String message)54 private void log(String testName, String message) { 55 Log.i(TAG, "[" + testName + "] " + message); 56 } 57 logw(String testName, String message)58 private void logw(String testName, String message) { 59 Log.w(TAG, "[" + testName + "] " + message); 60 } 61 loge(String testName, String message)62 private void loge(String testName, String message) { 63 Log.e(TAG, "[" + testName + "] " + message); 64 } 65 testSetBufferSize()66 public void testSetBufferSize() throws Exception { 67 // constants for PCM track test 68 final String TEST_PCM_NAME = "testSetBufferSizeTrackPcm"; 69 final int TEST_PCM_SAMPLE_RATE = 44100; 70 final int TEST_PCM_CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_STEREO; 71 final int TEST_PCM_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 72 73 // constants for AC3 track test 74 final String TEST_AC3_NAME = "testSetBufferSizeTrackAc3"; 75 final int TEST_AC3_SAMPLE_RATE = 48000; 76 final int TEST_AC3_CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_STEREO; 77 final int TEST_AC3_ENCODING = AudioFormat.ENCODING_AC3; 78 79 // -------- PCM track test -------------- 80 AudioTrack trackPcm = null; 81 try { 82 trackPcm = createStreamAudioTrack( 83 TEST_PCM_SAMPLE_RATE, 84 TEST_PCM_CHANNEL_CONFIG, 85 TEST_PCM_ENCODING); 86 testSetBufferSizeOnTrack(trackPcm, TEST_PCM_NAME); 87 } catch (Exception exception) { 88 // this is unexpected, PCM should be supported on all devices 89 fail("Couldn't create PCM audio track!"); 90 } finally { 91 if (trackPcm != null) { 92 trackPcm.release(); 93 } 94 } 95 96 97 // -------- AC3 track test -------------- 98 AudioTrack trackAc3 = null; 99 try { 100 trackAc3 = createStreamAudioTrack( 101 TEST_AC3_SAMPLE_RATE, 102 TEST_AC3_CHANNEL_CONFIG, 103 TEST_AC3_ENCODING); 104 testSetBufferSizeOnTrack(trackAc3, TEST_AC3_NAME); 105 } catch (UnsupportedOperationException exception) { 106 // this is expected, not all devices can create AC3 tracks 107 // for the devices that can create AC3 tracks (like HDMI connected ATV devices), 108 // they should allow for setBufferSize 109 log(TEST_AC3_NAME, "Couldn't create AC3 audio track for testSetBufferSize"); 110 } finally { 111 if (trackAc3 != null) { 112 trackAc3.release(); 113 } 114 } 115 } 116 createStreamAudioTrack(int sampleRate, int channelConfig, int encoding)117 private AudioTrack createStreamAudioTrack(int sampleRate, int channelConfig, int encoding) { 118 // Start with buffer twice as large as needed. 119 int bufferSizeBytes = 2 * AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding); 120 AudioAttributes attributes = new AudioAttributes.Builder() 121 .setUsage(AudioAttributes.USAGE_MEDIA) 122 .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) 123 .build(); 124 AudioFormat format = new AudioFormat.Builder() 125 .setSampleRate(sampleRate) 126 .setChannelMask(channelConfig) 127 .setEncoding(encoding) 128 .build(); 129 AudioTrack track = new AudioTrack.Builder() 130 .setAudioAttributes(attributes) 131 .setAudioFormat(format) 132 .setTransferMode(AudioTrack.MODE_STREAM) 133 .setBufferSizeInBytes(bufferSizeBytes) 134 .build(); 135 return track; 136 } 137 testSetBufferSizeOnTrack(AudioTrack track, String testName)138 private void testSetBufferSizeOnTrack(AudioTrack track, String testName) { 139 // Initial values 140 int bufferCapacity = track.getBufferCapacityInFrames(); 141 int initialBufferSize = track.getBufferSizeInFrames(); 142 assertTrue(testName, bufferCapacity > 0); 143 assertTrue(testName, initialBufferSize > 0); 144 assertTrue(testName, initialBufferSize <= bufferCapacity); 145 146 // set to various values 147 int resultNegative = track.setBufferSizeInFrames(-1); 148 assertEquals(testName + ": negative size", AudioTrack.ERROR_BAD_VALUE, resultNegative); 149 assertEquals(testName + ": should be unchanged", 150 initialBufferSize, track.getBufferSizeInFrames()); 151 152 int resultZero = track.setBufferSizeInFrames(0); 153 assertTrue(testName + ": should be >0, but got " + resultZero, resultZero > 0); 154 assertTrue(testName + ": zero size < original, but got " + resultZero, 155 resultZero < initialBufferSize); 156 assertEquals(testName + ": should match resultZero", 157 resultZero, track.getBufferSizeInFrames()); 158 159 int resultMax = track.setBufferSizeInFrames(Integer.MAX_VALUE); 160 assertTrue(testName + ": set MAX_VALUE, >", resultMax > resultZero); 161 assertTrue(testName + ": set MAX_VALUE, <=", resultMax <= bufferCapacity); 162 assertEquals(testName + ": should match resultMax", 163 resultMax, track.getBufferSizeInFrames()); 164 165 int resultMiddle = track.setBufferSizeInFrames(bufferCapacity / 2); 166 assertTrue(testName + ": set middle, >", resultMiddle > resultZero); 167 assertTrue(testName + ": set middle, <=", resultMiddle < resultMax); 168 assertEquals(testName + ": should match resultMiddle", 169 resultMiddle, track.getBufferSizeInFrames()); 170 } 171 172 // Helper class for tests 173 private static class TestSetup { 174 public int sampleRate = 48000; 175 public int samplesPerFrame = 2; 176 public int bytesPerSample = 2; 177 public int config = AudioFormat.CHANNEL_OUT_STEREO; 178 public int format = AudioFormat.ENCODING_PCM_16BIT; 179 public int mode = AudioTrack.MODE_STREAM; 180 public int streamType = AudioManager.STREAM_MUSIC; 181 public int framesPerBuffer = 256; 182 public double amplitude = 0.5; 183 184 private AudioTrack mTrack; 185 private short[] mData; 186 private int mActualSizeInFrames; 187 createTrack()188 AudioTrack createTrack() { 189 mData = AudioHelper.createSineWavesShort(framesPerBuffer, 190 samplesPerFrame, 1, amplitude); 191 int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, config, format); 192 // Create a buffer that is 3/2 times bigger than the minimum. 193 // This gives me room to cut it in half and play without glitching. 194 // This is an arbitrary scaling factor. 195 int bufferSize = (minBufferSize * 3) / 2; 196 mTrack = new AudioTrack(streamType, sampleRate, config, format, 197 bufferSize, mode); 198 199 // Calculate and use a smaller buffer size 200 int smallBufferSize = bufferSize / 2; // arbitrary, smaller might underflow 201 int smallBuffSizeInFrames = smallBufferSize / (samplesPerFrame * bytesPerSample); 202 mActualSizeInFrames = mTrack.setBufferSizeInFrames(smallBuffSizeInFrames); 203 return mTrack; 204 205 } 206 primeAudioTrack(String testName)207 int primeAudioTrack(String testName) { 208 // Prime the buffer. 209 int samplesWrittenTotal = 0; 210 int samplesWritten; 211 do{ 212 samplesWritten = mTrack.write(mData, 0, mData.length); 213 if (samplesWritten > 0) { 214 samplesWrittenTotal += samplesWritten; 215 } 216 } while (samplesWritten == mData.length); 217 int framesWrittenTotal = samplesWrittenTotal / samplesPerFrame; 218 assertTrue(testName + ": framesWrittenTotal = " + framesWrittenTotal 219 + ", size = " + mActualSizeInFrames, 220 framesWrittenTotal >= mActualSizeInFrames); 221 return framesWrittenTotal; 222 } 223 224 /** 225 * @param seconds 226 */ writeSeconds(double seconds)227 public void writeSeconds(double seconds) throws InterruptedException { 228 long msecEnd = System.currentTimeMillis() + (long)(seconds * 1000); 229 while (System.currentTimeMillis() < msecEnd) { 230 // Use non-blocking mode in case the track is hung. 231 int samplesWritten = mTrack.write(mData, 0, mData.length, AudioTrack.WRITE_NON_BLOCKING); 232 if (samplesWritten < mData.length) { 233 int samplesRemaining = mData.length - samplesWritten; 234 int framesRemaining = samplesRemaining / samplesPerFrame; 235 int millis = (framesRemaining * 1000) / sampleRate; 236 Thread.sleep(millis); 237 } 238 } 239 } 240 } 241 242 // Try to play an AudioTrack when the initial size is less than capacity. 243 // We want to make sure the track starts properly and is not stuck. testPlaySmallBuffer()244 public void testPlaySmallBuffer() throws Exception { 245 final String TEST_NAME = "testPlaySmallBuffer"; 246 TestSetup setup = new TestSetup(); 247 AudioTrack track = setup.createTrack(); 248 249 // Prime the buffer. 250 int framesWrittenTotal = setup.primeAudioTrack(TEST_NAME); 251 252 // Start playing and let it drain. 253 int position1 = track.getPlaybackHeadPosition(); 254 assertEquals(TEST_NAME + ": initial position", 0, position1); 255 track.play(); 256 257 // Make sure it starts within a reasonably short time. 258 final long MAX_TIME_TO_START_MSEC = 500; // arbitrary 259 long giveUpAt = System.currentTimeMillis() + MAX_TIME_TO_START_MSEC; 260 int position2 = track.getPlaybackHeadPosition(); 261 while ((position1 == position2) 262 && (System.currentTimeMillis() < giveUpAt)) { 263 Thread.sleep(20); // arbitrary interval 264 position2 = track.getPlaybackHeadPosition(); 265 } 266 assertTrue(TEST_NAME + ": did it start?, position after start = " + position2, 267 position2 > position1); 268 269 // Make sure it finishes playing the data. 270 // Wait several times longer than it should take to play the data. 271 final int several = 3; // arbitrary 272 // Even though the read head has advanced, it may stall a while waiting 273 // for the device to "warm up". 274 final int WARM_UP_TIME_MSEC = 300; // arbitrary 275 final long sleepTimeMSec = WARM_UP_TIME_MSEC 276 + (several * framesWrittenTotal * MILLIS_PER_SECOND / setup.sampleRate); 277 Thread.sleep(sleepTimeMSec); 278 position2 = track.getPlaybackHeadPosition(); 279 assertEquals(TEST_NAME + ": did it play all the data?", 280 framesWrittenTotal, position2); 281 282 track.release(); 283 } 284 285 // Try to play and pause an AudioTrack when the initial size is less than capacity. 286 // We want to make sure the track starts properly and is not stuck. testPlayPauseSmallBuffer()287 public void testPlayPauseSmallBuffer() throws Exception { 288 final String TEST_NAME = "testPlayPauseSmallBuffer"; 289 TestSetup setup = new TestSetup(); 290 AudioTrack track = setup.createTrack(); 291 292 // Prime the buffer. 293 setup.primeAudioTrack(TEST_NAME); 294 295 // Start playing then pause and play in a loop. 296 int position1 = track.getPlaybackHeadPosition(); 297 assertEquals(TEST_NAME + ": initial position", 0, position1); 298 track.play(); 299 // try pausing several times to see if it fails 300 final int several = 4; // arbitrary 301 for (int i = 0; i < several; i++) { 302 // write data in non-blocking mode for a few seconds 303 setup.writeSeconds(2.0); // arbitrary, long enough for audio to get to the device 304 // Did position advance as we were playing? Or was the track stuck? 305 int position2 = track.getPlaybackHeadPosition(); 306 int delta = position2 - position1; // safe from wrapping 307 assertTrue(TEST_NAME + ": [" + i + "] did it advance? p1 = " + position1 308 + ", p2 = " + position2, delta > 0); 309 position1 = position2; 310 // pause for a second 311 track.pause(); 312 Thread.sleep(MILLIS_PER_SECOND); 313 track.play(); 314 } 315 316 track.release(); 317 } 318 319 // Create a track with or without FLAG_LOW_LATENCY createCustomAudioTrack(boolean lowLatency)320 private AudioTrack createCustomAudioTrack(boolean lowLatency) { 321 final String TEST_NAME = "createCustomAudioTrack"; 322 final int TEST_SR = 48000; 323 final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO; 324 final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 325 final int TEST_CONTENT_TYPE = AudioAttributes.CONTENT_TYPE_MUSIC; 326 327 // Start with buffer twice as large as needed. 328 int bufferSizeBytes = 2 * AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT); 329 AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder() 330 .setContentType(TEST_CONTENT_TYPE); 331 if (lowLatency) { 332 attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY); 333 } 334 AudioAttributes attributes = attributesBuilder.build(); 335 336 // Do not specify the sample rate so we get the optimal rate. 337 AudioFormat format = new AudioFormat.Builder() 338 .setEncoding(TEST_FORMAT) 339 .setChannelMask(TEST_CONF) 340 .build(); 341 AudioTrack track = new AudioTrack.Builder() 342 .setAudioAttributes(attributes) 343 .setAudioFormat(format) 344 .setBufferSizeInBytes(bufferSizeBytes) 345 .build(); 346 347 assertTrue(track != null); 348 log(TEST_NAME, "Track sample rate = " + track.getSampleRate() + " Hz"); 349 return track; 350 } 351 352 checkOutputLowLatency(boolean lowLatency)353 private int checkOutputLowLatency(boolean lowLatency) throws Exception { 354 // constants for test 355 final String TEST_NAME = "checkOutputLowLatency"; 356 final int TEST_SAMPLES_PER_FRAME = 2; 357 final int TEST_BYTES_PER_SAMPLE = 2; 358 final int TEST_NUM_SECONDS = 4; 359 final int TEST_FRAMES_PER_BUFFER = 128; 360 final double TEST_AMPLITUDE = 0.5; 361 362 final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER, 363 TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE); 364 365 // -------- initialization -------------- 366 AudioTrack track = createCustomAudioTrack(lowLatency); 367 assertTrue(TEST_NAME + " actual SR", track.getSampleRate() > 0); 368 369 // -------- test -------------- 370 // Play some audio for a few seconds. 371 int numSeconds = TEST_NUM_SECONDS; 372 int numBuffers = numSeconds * track.getSampleRate() / TEST_FRAMES_PER_BUFFER; 373 long framesWritten = 0; 374 boolean isPlaying = false; 375 for (int i = 0; i < numBuffers; i++) { 376 track.write(data, 0, data.length); 377 framesWritten += TEST_FRAMES_PER_BUFFER; 378 // prime the buffer a bit before playing 379 if (!isPlaying) { 380 track.play(); 381 isPlaying = true; 382 } 383 } 384 385 // Estimate the latency from the timestamp. 386 long timeWritten = System.nanoTime(); 387 AudioTimestamp timestamp = new AudioTimestamp(); 388 boolean result = track.getTimestamp(timestamp); 389 // FIXME failing LOW_LATENCY case! b/26413951 390 assertTrue(TEST_NAME + " did not get a timestamp, lowLatency = " 391 + lowLatency, result); 392 393 // Calculate when the last frame written is going to be rendered. 394 long framesPending = framesWritten - timestamp.framePosition; 395 long timeDelta = framesPending * NANOS_PER_SECOND / track.getSampleRate(); 396 long timePresented = timestamp.nanoTime + timeDelta; 397 long latencyNanos = timePresented - timeWritten; 398 int latencyMillis = (int) (latencyNanos / NANOS_PER_MILLISECOND); 399 assertTrue(TEST_NAME + " got latencyMillis <= 0 == " 400 + latencyMillis, latencyMillis > 0); 401 402 // -------- cleanup -------------- 403 track.release(); 404 405 return latencyMillis; 406 } 407 408 // Compare output latency with and without FLAG_LOW_LATENCY. testOutputLowLatency()409 public void testOutputLowLatency() throws Exception { 410 final String TEST_NAME = "testOutputLowLatency"; 411 412 int highLatencyMillis = checkOutputLowLatency(false); 413 log(TEST_NAME, "High latency = " + highLatencyMillis + " msec"); 414 415 int lowLatencyMillis = checkOutputLowLatency(true); 416 log(TEST_NAME, "Low latency = " + lowLatencyMillis + " msec"); 417 418 // We are not guaranteed to get a FAST track. Some platforms 419 // do not even have a FastMixer. So just warn and not fail. 420 if (highLatencyMillis <= (lowLatencyMillis + 10)) { 421 logw(TEST_NAME, "high latency should be much higher, " 422 + highLatencyMillis 423 + " vs " + lowLatencyMillis); 424 } 425 } 426 427 // Verify that no underruns when buffer is >= getMinBufferSize(). 428 // Verify that we get underruns with buffer at smallest possible size. testGetUnderrunCount()429 public void testGetUnderrunCount() throws Exception { 430 // constants for test 431 final String TEST_NAME = "testGetUnderrunCount"; 432 final int TEST_SR = 44100; 433 final int TEST_SAMPLES_PER_FRAME = 2; 434 final int TEST_BYTES_PER_SAMPLE = 2; 435 final int TEST_NUM_SECONDS = 2; 436 final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO; 437 final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 438 final int TEST_MODE = AudioTrack.MODE_STREAM; 439 final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC; 440 final int TEST_FRAMES_PER_BUFFER = 256; 441 final int TEST_FRAMES_PER_BLIP = TEST_SR / 8; 442 final int TEST_CYCLES_PER_BLIP = 700 * TEST_FRAMES_PER_BLIP / TEST_SR; 443 final double TEST_AMPLITUDE = 0.5; 444 445 final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER, 446 TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE); 447 final short[] blip = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BLIP, 448 TEST_SAMPLES_PER_FRAME, TEST_CYCLES_PER_BLIP, TEST_AMPLITUDE); 449 450 // -------- initialization -------------- 451 int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT); 452 // Start with buffer twice as large as needed. 453 AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 454 minBuffSize * 2, TEST_MODE); 455 456 // -------- test -------------- 457 // Initial values 458 int bufferCapacity = track.getBufferCapacityInFrames(); 459 int initialBufferSize = track.getBufferSizeInFrames(); 460 int minBuffSizeInFrames = minBuffSize / (TEST_SAMPLES_PER_FRAME * TEST_BYTES_PER_SAMPLE); 461 assertTrue(TEST_NAME, bufferCapacity > 0); 462 assertTrue(TEST_NAME, initialBufferSize > 0); 463 assertTrue(TEST_NAME, initialBufferSize <= bufferCapacity); 464 465 // Play with initial size. 466 int underrunCount1 = track.getUnderrunCount(); 467 assertEquals(TEST_NAME + ": initially no underruns", 0, underrunCount1); 468 469 // Prime the buffer. 470 while (track.write(data, 0, data.length) == data.length); 471 472 // Start playing 473 track.play(); 474 int numBuffers = TEST_SR / TEST_FRAMES_PER_BUFFER; 475 for (int i = 0; i < numBuffers; i++) { 476 track.write(data, 0, data.length); 477 } 478 int underrunCountBase = track.getUnderrunCount(); 479 int numSeconds = TEST_NUM_SECONDS; 480 numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER; 481 track.write(blip, 0, blip.length); 482 for (int i = 0; i < numBuffers; i++) { 483 track.write(data, 0, data.length); 484 } 485 underrunCount1 = track.getUnderrunCount(); 486 assertEquals(TEST_NAME + ": no more underruns after initial", 487 underrunCountBase, underrunCount1); 488 489 // Play with getMinBufferSize() size. 490 int resultMin = track.setBufferSizeInFrames(minBuffSizeInFrames); 491 assertTrue(TEST_NAME + ": set minBuff, >", resultMin > 0); 492 assertTrue(TEST_NAME + ": set minBuff, <=", resultMin <= initialBufferSize); 493 track.write(blip, 0, blip.length); 494 for (int i = 0; i < numBuffers; i++) { 495 track.write(data, 0, data.length); 496 } 497 track.write(blip, 0, blip.length); 498 underrunCount1 = track.getUnderrunCount(); 499 assertEquals(TEST_NAME + ": no more underruns at min", underrunCountBase, underrunCount1); 500 501 // Play with ridiculously small size. We want to get underruns so we know that an app 502 // can get to the edge of underrunning. 503 int resultZero = track.setBufferSizeInFrames(0); 504 assertTrue(TEST_NAME + ": should return > 0, got " + resultZero, resultZero > 0); 505 assertTrue(TEST_NAME + ": zero size < original", resultZero < initialBufferSize); 506 numSeconds = TEST_NUM_SECONDS / 2; // cuz test takes longer when underflowing 507 numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER; 508 // Play for a few seconds or until we get some new underruns. 509 for (int i = 0; (i < numBuffers) && ((underrunCount1 - underrunCountBase) < 10); i++) { 510 track.write(data, 0, data.length); 511 underrunCount1 = track.getUnderrunCount(); 512 } 513 assertTrue(TEST_NAME + ": underruns at zero", underrunCount1 > underrunCountBase); 514 int underrunCount2 = underrunCount1; 515 // Play for a few seconds or until we get some new underruns. 516 for (int i = 0; (i < numBuffers) && ((underrunCount2 - underrunCount1) < 10); i++) { 517 track.write(data, 0, data.length); 518 underrunCount2 = track.getUnderrunCount(); 519 } 520 assertTrue(TEST_NAME + ": underruns still accumulating", underrunCount2 > underrunCount1); 521 522 // Restore buffer to good size 523 numSeconds = TEST_NUM_SECONDS; 524 numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER; 525 int resultMax = track.setBufferSizeInFrames(bufferCapacity); 526 track.write(blip, 0, blip.length); 527 for (int i = 0; i < numBuffers; i++) { 528 track.write(data, 0, data.length); 529 } 530 // Should have stopped by now. 531 underrunCount1 = track.getUnderrunCount(); 532 track.write(blip, 0, blip.length); 533 for (int i = 0; i < numBuffers; i++) { 534 track.write(data, 0, data.length); 535 } 536 // Counts should match. 537 underrunCount2 = track.getUnderrunCount(); 538 assertEquals(TEST_NAME + ": underruns should stop happening", 539 underrunCount1, underrunCount2); 540 541 // -------- tear down -------------- 542 track.release(); 543 } 544 545 // Verify that we get underruns if we stop writing to the buffer. testGetUnderrunCountSleep()546 public void testGetUnderrunCountSleep() throws Exception { 547 // constants for test 548 final String TEST_NAME = "testGetUnderrunCountSleep"; 549 final int TEST_SR = 48000; 550 final int TEST_SAMPLES_PER_FRAME = 2; 551 final int TEST_BYTES_PER_SAMPLE = 2; 552 final int TEST_NUM_SECONDS = 2; 553 final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO; 554 final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 555 final int TEST_MODE = AudioTrack.MODE_STREAM; 556 final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC; 557 final int TEST_FRAMES_PER_BUFFER = 256; 558 final int TEST_FRAMES_PER_BLIP = TEST_SR / 8; 559 final int TEST_CYCLES_PER_BLIP = 700 * TEST_FRAMES_PER_BLIP / TEST_SR; 560 final double TEST_AMPLITUDE = 0.5; 561 562 final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER, 563 TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE); 564 final short[] blip = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BLIP, 565 TEST_SAMPLES_PER_FRAME, TEST_CYCLES_PER_BLIP, TEST_AMPLITUDE); 566 567 // -------- initialization -------------- 568 int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT); 569 // Start with buffer twice as large as needed. 570 AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 571 minBuffSize * 2, TEST_MODE); 572 573 // -------- test -------------- 574 // Initial values 575 int minBuffSizeInFrames = minBuffSize / (TEST_SAMPLES_PER_FRAME * TEST_BYTES_PER_SAMPLE); 576 577 int underrunCount1 = track.getUnderrunCount(); 578 assertEquals(TEST_NAME + ": initially no underruns", 0, underrunCount1); 579 580 // Prime the buffer. 581 while (track.write(data, 0, data.length) == data.length); 582 583 // Start playing 584 track.play(); 585 int numBuffers = TEST_SR / TEST_FRAMES_PER_BUFFER; 586 for (int i = 0; i < numBuffers; i++) { 587 track.write(data, 0, data.length); 588 } 589 int underrunCountBase = track.getUnderrunCount(); 590 int numSeconds = TEST_NUM_SECONDS; 591 numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER; 592 track.write(blip, 0, blip.length); 593 for (int i = 0; i < numBuffers; i++) { 594 track.write(data, 0, data.length); 595 } 596 underrunCount1 = track.getUnderrunCount(); 597 assertEquals(TEST_NAME + ": no more underruns after initial", 598 underrunCountBase, underrunCount1); 599 600 // Sleep and force underruns. 601 track.write(blip, 0, blip.length); 602 for (int i = 0; i < 10; i++) { 603 track.write(data, 0, data.length); 604 Thread.sleep(500); // ========================= SLEEP! =========== 605 } 606 track.write(blip, 0, blip.length); 607 underrunCount1 = track.getUnderrunCount(); 608 assertTrue(TEST_NAME + ": expect underruns after sleep, #ur=" 609 + underrunCount1, 610 underrunCountBase < underrunCount1); 611 612 track.write(blip, 0, blip.length); 613 for (int i = 0; i < numBuffers; i++) { 614 track.write(data, 0, data.length); 615 } 616 617 // Should have stopped by now. 618 underrunCount1 = track.getUnderrunCount(); 619 track.write(blip, 0, blip.length); 620 for (int i = 0; i < numBuffers; i++) { 621 track.write(data, 0, data.length); 622 } 623 // Counts should match. 624 int underrunCount2 = track.getUnderrunCount(); 625 assertEquals(TEST_NAME + ": underruns should stop happening", 626 underrunCount1, underrunCount2); 627 628 // -------- tear down -------------- 629 track.release(); 630 } 631 632 static class TrackBufferSizeChecker { 633 private final static String TEST_NAME = "testTrackBufferSize"; 634 private final static int TEST_SR = 48000; 635 private final static int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO; 636 private final static int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 637 private final static int TEST_MODE = AudioTrack.MODE_STREAM; 638 private final static int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC; 639 private final static int FRAME_SIZE = 2 * 2; // stereo 16-bit PCM 640 getFrameSize()641 public static int getFrameSize() { 642 return FRAME_SIZE; 643 } 644 getMinBufferSize()645 public static int getMinBufferSize() { 646 return AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT); 647 } 648 createAudioTrack(int bufferSize)649 public static AudioTrack createAudioTrack(int bufferSize) { 650 return new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 651 bufferSize, TEST_MODE); 652 } 653 checkBadSize(int bufferSize)654 public static void checkBadSize(int bufferSize) { 655 AudioTrack track = null; 656 try { 657 track = TrackBufferSizeChecker.createAudioTrack(bufferSize); 658 assertTrue(TEST_NAME + ": should not have survived size " + bufferSize, false); 659 } catch(IllegalArgumentException e) { 660 // expected 661 } finally { 662 if (track != null) { 663 track.release(); 664 } 665 } 666 } 667 checkSmallSize(int bufferSize)668 public static void checkSmallSize(int bufferSize) { 669 AudioTrack track = null; 670 try { 671 track = TrackBufferSizeChecker.createAudioTrack(bufferSize); 672 assertEquals(TEST_NAME + ": should still be initialized with small size " + bufferSize, 673 AudioTrack.STATE_INITIALIZED, track.getState()); 674 } finally { 675 if (track != null) { 676 track.release(); 677 } 678 } 679 } 680 } 681 682 /** 683 * Test various values for bufferSizeInBytes. 684 * 685 * According to the latest documentation, any positive bufferSize that is a multiple 686 * of the frameSize is legal. Small sizes will be rounded up to the minimum size. 687 * 688 * Negative sizes, zero, or any non-multiple of the frameSize is illegal. 689 * 690 * @throws Exception 691 */ testTrackBufferSize()692 public void testTrackBufferSize() throws Exception { 693 TrackBufferSizeChecker.checkBadSize(0); 694 TrackBufferSizeChecker.checkBadSize(17); 695 TrackBufferSizeChecker.checkBadSize(18); 696 TrackBufferSizeChecker.checkBadSize(-9); 697 int frameSize = TrackBufferSizeChecker.getFrameSize(); 698 TrackBufferSizeChecker.checkBadSize(-4 * frameSize); 699 for (int i = 1; i < 8; i++) { 700 TrackBufferSizeChecker.checkSmallSize(i * frameSize); 701 TrackBufferSizeChecker.checkBadSize(3 + (i * frameSize)); 702 } 703 } 704 } 705