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