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