Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 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.media.cts.R;
     20 
     21 
     22 import android.content.Context;
     23 import android.content.res.AssetFileDescriptor;
     24 import android.media.AudioAttributes;
     25 import android.media.AudioManager;
     26 import android.media.SoundPool;
     27 import android.platform.test.annotations.AppModeFull;
     28 import android.test.AndroidTestCase;
     29 
     30 import java.io.File;
     31 import java.io.FileDescriptor;
     32 import java.io.FileOutputStream;
     33 import java.io.InputStream;
     34 import java.util.Arrays;
     35 import java.util.concurrent.atomic.AtomicInteger;
     36 
     37 @AppModeFull(reason = "TODO: evaluate and port to instant")
     38 abstract class SoundPoolTest extends AndroidTestCase {
     39 
     40     private static final int SOUNDPOOL_STREAMS = 4;
     41     private static final int PRIORITY = 1;
     42     private static final int LOUD = 20;
     43     private static final int QUIET = LOUD / 2;
     44     private static final int SILENT = 0;
     45     private File mFile;
     46     private SoundPool mSoundPool;
     47 
     48     /**
     49      * function to return resource ID for A4 sound.
     50      * should be implemented by child class
     51      * @return resource ID
     52      */
     53     protected abstract int getSoundA();
     54 
     55     protected abstract int getSoundCs();
     56 
     57     protected abstract int getSoundE();
     58 
     59     protected abstract int getSoundB();
     60 
     61     protected abstract int getSoundGs();
     62 
     63     protected abstract String getFileName();
     64 
     65     private int[] getSounds() {
     66         int[] sounds = { getSoundA(),
     67                          getSoundCs(),
     68                          getSoundE(),
     69                          getSoundB(),
     70                          getSoundGs() };
     71         return sounds;
     72     }
     73 
     74     protected AudioAttributes getAudioAttributes() {
     75         return new AudioAttributes.Builder()
     76                 .setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
     77     }
     78 
     79     @Override
     80     protected void setUp() throws Exception {
     81         super.setUp();
     82         mFile = new File(mContext.getFilesDir(), getFileName());
     83     }
     84 
     85     @Override
     86     protected void tearDown() throws Exception {
     87         super.tearDown();
     88         if (mFile.exists()) {
     89             mFile.delete();
     90         }
     91         if (mSoundPool != null) {
     92             mSoundPool.release();
     93             mSoundPool = null;
     94             return;
     95         }
     96     }
     97 
     98     public void testLoad() throws Exception {
     99         mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
    100                 .setAudioAttributes(getAudioAttributes()).build();
    101         int sampleId1 = mSoundPool.load(mContext, getSoundA(), PRIORITY);
    102         waitUntilLoaded(sampleId1);
    103         // should return true, but returns false
    104         mSoundPool.unload(sampleId1);
    105 
    106         AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(getSoundCs());
    107         int sampleId2;
    108         sampleId2 = mSoundPool.load(afd, PRIORITY);
    109         waitUntilLoaded(sampleId2);
    110         mSoundPool.unload(sampleId2);
    111 
    112         FileDescriptor fd = afd.getFileDescriptor();
    113         long offset = afd.getStartOffset();
    114         long length = afd.getLength();
    115         int sampleId3;
    116         sampleId3 = mSoundPool.load(fd, offset, length, PRIORITY);
    117         waitUntilLoaded(sampleId3);
    118         mSoundPool.unload(sampleId3);
    119 
    120         String path = mFile.getAbsolutePath();
    121         createSoundFile(mFile);
    122         int sampleId4;
    123         sampleId4 = mSoundPool.load(path, PRIORITY);
    124         waitUntilLoaded(sampleId4);
    125         mSoundPool.unload(sampleId4);
    126     }
    127 
    128     private void createSoundFile(File f) throws Exception {
    129         FileOutputStream fOutput = null;
    130         try {
    131             fOutput = new FileOutputStream(f);
    132             InputStream is = mContext.getResources().openRawResource(getSoundA());
    133             byte[] buffer = new byte[1024];
    134             int length = is.read(buffer);
    135             while (length != -1) {
    136                 fOutput.write(buffer, 0, length);
    137                 length = is.read(buffer);
    138             }
    139         } finally {
    140             if (fOutput != null) {
    141                 fOutput.flush();
    142                 fOutput.close();
    143             }
    144         }
    145     }
    146 
    147     public void testSoundPoolOp() throws Exception {
    148         mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
    149                 .setAudioAttributes(getAudioAttributes()).build();
    150         int sampleID = loadSampleSync(getSoundA(), PRIORITY);
    151 
    152         int waitMsec = 1000;
    153         float leftVolume = SILENT;
    154         float rightVolume = LOUD;
    155         int priority = 1;
    156         int loop = 0;
    157         float rate = 1f;
    158         int streamID = mSoundPool.play(sampleID, leftVolume, rightVolume, priority, loop, rate);
    159         assertTrue(streamID != 0);
    160         Thread.sleep(waitMsec);
    161         rate = 1.4f;
    162         mSoundPool.setRate(streamID, rate);
    163         Thread.sleep(waitMsec);
    164         mSoundPool.setRate(streamID, 1f);
    165         Thread.sleep(waitMsec);
    166         mSoundPool.pause(streamID);
    167         Thread.sleep(waitMsec);
    168         mSoundPool.resume(streamID);
    169         Thread.sleep(waitMsec);
    170         mSoundPool.stop(streamID);
    171 
    172         streamID = mSoundPool.play(sampleID, leftVolume, rightVolume, priority, loop, rate);
    173         assertTrue(streamID != 0);
    174         loop = -1;// loop forever
    175         mSoundPool.setLoop(streamID, loop);
    176         Thread.sleep(waitMsec);
    177         leftVolume = SILENT;
    178         rightVolume = SILENT;
    179         mSoundPool.setVolume(streamID, leftVolume, rightVolume);
    180         Thread.sleep(waitMsec);
    181         rightVolume = LOUD;
    182         mSoundPool.setVolume(streamID, leftVolume, rightVolume);
    183         priority = 0;
    184         mSoundPool.setPriority(streamID, priority);
    185         Thread.sleep(waitMsec * 10);
    186         mSoundPool.stop(streamID);
    187         mSoundPool.unload(sampleID);
    188     }
    189 
    190     public void testMultiSound() throws Exception {
    191         mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
    192                 .setAudioAttributes(getAudioAttributes()).build();
    193         int sampleID1 = loadSampleSync(getSoundA(), PRIORITY);
    194         int sampleID2 = loadSampleSync(getSoundCs(), PRIORITY);
    195         long waitMsec = 1000;
    196         Thread.sleep(waitMsec);
    197 
    198         // play sounds one at a time
    199         int streamID1 = mSoundPool.play(sampleID1, LOUD, QUIET, PRIORITY, -1, 1);
    200         assertTrue(streamID1 != 0);
    201         Thread.sleep(waitMsec * 4);
    202         mSoundPool.stop(streamID1);
    203         int streamID2 = mSoundPool.play(sampleID2, QUIET, LOUD, PRIORITY, -1, 1);
    204         assertTrue(streamID2 != 0);
    205         Thread.sleep(waitMsec * 4);
    206         mSoundPool.stop(streamID2);
    207 
    208         // play both at once repeating the first, but not the second
    209         streamID1 = mSoundPool.play(sampleID1, LOUD, QUIET, PRIORITY, 1, 1);
    210         streamID2 = mSoundPool.play(sampleID2, QUIET, LOUD, PRIORITY, 0, 1);
    211         assertTrue(streamID1 != 0);
    212         assertTrue(streamID2 != 0);
    213         Thread.sleep(4000);
    214         // both streams should have stopped by themselves; no way to check
    215 
    216         mSoundPool.release();
    217         mSoundPool = null;
    218     }
    219 
    220     public void testLoadMore() throws Exception {
    221         mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
    222                 .setAudioAttributes(getAudioAttributes()).build();
    223         int[] sounds = getSounds();
    224         int[] soundIds = new int[sounds.length];
    225         int[] streamIds = new int[sounds.length];
    226         for (int i = 0; i < sounds.length; i++) {
    227             soundIds[i] = loadSampleSync(sounds[i], PRIORITY);
    228             System.out.println("load: " + soundIds[i]);
    229         }
    230         for (int i = 0; i < soundIds.length; i++) {
    231             streamIds[i] = mSoundPool.play(soundIds[i], LOUD, LOUD, PRIORITY, -1, 1);
    232         }
    233         Thread.sleep(3000);
    234         for (int stream : streamIds) {
    235             assertTrue(stream != 0);
    236             mSoundPool.stop(stream);
    237         }
    238         for (int sound : soundIds) {
    239             mSoundPool.unload(sound);
    240         }
    241         mSoundPool.release();
    242     }
    243 
    244     public void testAutoPauseResume() throws Exception {
    245         // The number of possible SoundPool streams simultaneously active is limited by
    246         // track resources. Generally this is no greater than 32, but the actual
    247         // amount may be less depending on concurrently running applications.
    248         // Here we attempt to create more streams than what is normally possible;
    249         // SoundPool should gracefully degrade to play those streams it can.
    250         //
    251         // Try to keep the maxStreams less than the number required to be active
    252         // and certainly less than 20 to be cooperative to other applications.
    253         final int TEST_STREAMS = 40;
    254         SoundPool soundPool = null;
    255         try {
    256             soundPool = new SoundPool.Builder()
    257                     .setAudioAttributes(getAudioAttributes())
    258                     .setMaxStreams(TEST_STREAMS)
    259                     .build();
    260 
    261             // get our sounds
    262             final int[] sounds = getSounds();
    263 
    264             // set our completion listener
    265             final int[] loadIds = new int[TEST_STREAMS];
    266             final Object done = new Object();
    267             final int[] loaded = new int[1]; // used as a "pointer" to an integer
    268             final SoundPool fpool = soundPool; // final reference in scope of try block
    269             soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
    270                     @Override
    271                     public void onLoadComplete(SoundPool pool, int sampleId, int status) {
    272                         assertEquals(fpool, pool);
    273                         assertEquals(0 /* success */, status);
    274                         synchronized(done) {
    275                             loadIds[loaded[0]++] = sampleId;
    276                             if (loaded[0] == loadIds.length) {
    277                                 done.notify();
    278                             }
    279                         }
    280                     }
    281                 });
    282 
    283             // initiate loading
    284             final int[] soundIds = new int[TEST_STREAMS];
    285             for (int i = 0; i < soundIds.length; i++) {
    286                 soundIds[i] = soundPool.load(mContext, sounds[i % sounds.length], PRIORITY);
    287             }
    288 
    289             // wait for all sounds to load,
    290             // it usually takes about 33 seconds and 50 seconds is used to have some headroom.
    291             final long LOAD_TIMEOUT_IN_MS = 50000;
    292             final long startTime = System.currentTimeMillis();
    293             synchronized(done) {
    294                 while (loaded[0] != soundIds.length) {
    295                     final long waitTime =
    296                             LOAD_TIMEOUT_IN_MS - (System.currentTimeMillis() - startTime);
    297                     assertTrue(waitTime > 0);
    298                     done.wait(waitTime);
    299                 }
    300             }
    301 
    302             // verify the Ids match (actually does sorting too)
    303             Arrays.sort(loadIds);
    304             Arrays.sort(soundIds);
    305             assertTrue(Arrays.equals(loadIds, soundIds));
    306 
    307             // play - should hear the following:
    308             // 1 second of sound
    309             // 1 second of silence
    310             // 1 second of sound.
    311             int[] streamIds = new int[soundIds.length];
    312             for (int i = 0; i < soundIds.length; i++) {
    313                 streamIds[i] = soundPool.play(soundIds[i],
    314                         0.5f /* leftVolume */, 0.5f /* rightVolume */, PRIORITY,
    315                         -1 /* loop (infinite) */, 1.0f /* rate */);
    316             }
    317             Thread.sleep(1000 /* millis */);
    318             soundPool.autoPause();
    319             Thread.sleep(1000 /* millis */);
    320             soundPool.autoResume();
    321             Thread.sleep(1000 /* millis */);
    322 
    323             // clean up
    324             for (int stream : streamIds) {
    325                 assertTrue(stream != 0);
    326                 soundPool.stop(stream);
    327             }
    328             for (int sound : soundIds) {
    329                 assertEquals(true, soundPool.unload(sound));
    330             }
    331             // check to see we're really unloaded
    332             for (int sound : soundIds) {
    333                 assertEquals(false, soundPool.unload(sound));
    334             }
    335         } finally {
    336             if (soundPool != null) {
    337                 soundPool.release();
    338                 soundPool = null;
    339             }
    340         }
    341     }
    342 
    343     /**
    344      * Load a sample and wait until it is ready to be played.
    345      * @return The sample ID.
    346      * @throws InterruptedException
    347      */
    348     private int loadSampleSync(int sampleId, int prio) throws InterruptedException {
    349         int sample = mSoundPool.load(mContext, sampleId, prio);
    350         waitUntilLoaded(sample);
    351         return sample;
    352     }
    353 
    354     /**
    355      * Wait until the specified sample is loaded.
    356      * @param sampleId The sample ID.
    357      * @throws InterruptedException
    358      */
    359     private void waitUntilLoaded(int sampleId) throws InterruptedException {
    360         int stream = 0;
    361         while (stream == 0) {
    362             Thread.sleep(500);
    363             stream = mSoundPool.play(sampleId, SILENT, SILENT, 1, 0, 1);
    364         }
    365         mSoundPool.stop(stream);
    366     }
    367 }
    368