Home | History | Annotate | Download | only in cts
      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 
     56     private void log(String testName, String message) {
     57         Log.i(TAG, "[" + testName + "] " + message);
     58     }
     59 
     60     private void logw(String testName, String message) {
     61         Log.w(TAG, "[" + testName + "] " + message);
     62     }
     63 
     64     private void loge(String testName, String message) {
     65         Log.e(TAG, "[" + testName + "] " + message);
     66     }
     67 
     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 
    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 
    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          */
    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.
    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.
    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
    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 
    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.
    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.
    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.
    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 
    583         public static int getFrameSize() {
    584             return FRAME_SIZE;
    585         }
    586 
    587         public static int getMinBufferSize() {
    588             return AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
    589         }
    590 
    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 
    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 
    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      */
    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