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