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