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