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