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