1 /* 2 * Copyright (c) 2009, Google Inc. 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 com.android.soundpooltest; 18 19 import android.app.Activity; 20 import android.widget.LinearLayout; 21 import android.os.Bundle; 22 import android.view.ViewGroup; 23 import android.widget.Button; 24 import android.view.View; 25 import android.view.View.OnClickListener; 26 import android.view.KeyEvent; 27 import android.media.AudioSystem; 28 import android.media.AudioManager; 29 import android.media.SoundPool; 30 import android.media.SoundPool.OnLoadCompleteListener; 31 import android.util.Config; 32 import android.util.Log; 33 import java.util.HashMap; 34 import java.lang.Math; 35 36 import com.android.soundpooltest.R; 37 38 public class SoundPoolTest extends Activity 39 { 40 private static final String LOG_TAG = "SoundPoolTest"; 41 private static final boolean DEBUG = true; 42 private static final boolean VERBOSE = false; 43 private TestThread mThread; 44 45 private static final int[] mTestFiles = new int[] { 46 R.raw.organ441, 47 R.raw.sine441, 48 R.raw.test1, 49 R.raw.test2, 50 R.raw.test3, 51 R.raw.test4, 52 R.raw.test5 53 }; 54 55 private final static float SEMITONE = 1.059463094f; 56 private final static float DEFAULT_VOLUME = 0.707f; 57 private final static float MAX_VOLUME = 1.0f; 58 private final static float MIN_VOLUME = 0.01f; 59 private final static int LOW_PRIORITY = 1000; 60 private final static int NORMAL_PRIORITY = 2000; 61 private final static int HIGH_PRIORITY = 3000; 62 private final static int DEFAULT_LOOP = -1; 63 private final static int DEFAULT_SRC_QUALITY = 0; 64 private final static double PI_OVER_2 = Math.PI / 2.0; 65 66 public SoundPoolTest() {} 67 68 private final class TestThread extends java.lang.Thread { 69 private boolean mRunning; 70 private SoundPool mSoundPool = null; 71 private int mLastSample; 72 private int mMaxStreams; 73 private int mLoadStatus; 74 private int[] mSounds; 75 private float mScale[]; 76 77 TestThread() { 78 super("SoundPool.TestThread"); 79 } 80 81 private final class LoadCompleteCallback implements 82 android.media.SoundPool.OnLoadCompleteListener { 83 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { 84 synchronized(mSoundPool) { 85 if (DEBUG) Log.d(LOG_TAG, "Sample " + sampleId + " load status = " + status); 86 if (status != 0) { 87 mLoadStatus = status; 88 } 89 if (sampleId == mLastSample) { 90 mSoundPool.notify(); 91 } 92 } 93 } 94 } 95 96 private int loadSound(int resId, int priority) { 97 int id = mSoundPool.load(getApplicationContext(), resId, priority); 98 if (id == 0) { 99 Log.e(LOG_TAG, "Unable to open resource"); 100 } 101 return id; 102 } 103 104 private int initSoundPool(int numStreams) throws java.lang.InterruptedException { 105 106 if (mSoundPool != null) { 107 if ((mMaxStreams == numStreams) && (mLoadStatus == 0)) return mLoadStatus; 108 mSoundPool.release(); 109 mSoundPool = null; 110 } 111 112 // create sound pool 113 mLoadStatus = 0; 114 mMaxStreams = numStreams; 115 mSoundPool = new SoundPool(numStreams, AudioSystem.STREAM_MUSIC, 0); 116 mSoundPool.setOnLoadCompleteListener(new LoadCompleteCallback()); 117 int numSounds = mTestFiles.length; 118 mSounds = new int[numSounds]; 119 120 // load sounds 121 synchronized(mSoundPool) { 122 for (int index = 0; index < numSounds; index++) { 123 mSounds[index] = loadSound(mTestFiles[index], NORMAL_PRIORITY); 124 mLastSample = mSounds[index]; 125 } 126 mSoundPool.wait(); 127 } 128 return mLoadStatus; 129 } 130 131 private boolean TestSounds() throws java.lang.InterruptedException { 132 if (DEBUG) Log.d(LOG_TAG, "Begin sounds test"); 133 int count = mSounds.length; 134 for (int index = 0; index < count; index++) { 135 int id = mSoundPool.play(mSounds[index], DEFAULT_VOLUME, DEFAULT_VOLUME, 136 NORMAL_PRIORITY, DEFAULT_LOOP, 1.0f); 137 if (DEBUG) Log.d(LOG_TAG, "Start note " + id); 138 if (id == 0) { 139 Log.e(LOG_TAG, "Error occurred starting note"); 140 return false; 141 } 142 sleep(450); 143 mSoundPool.stop(id); 144 if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); 145 sleep(50); 146 } 147 if (DEBUG) Log.d(LOG_TAG, "End scale test"); 148 return true; 149 } 150 151 private boolean TestScales() throws java.lang.InterruptedException { 152 if (DEBUG) Log.d(LOG_TAG, "Begin scale test"); 153 154 // interate through pitch table 155 int count = mScale.length; 156 for (int step = 0; step < count; step++) { 157 int id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 158 NORMAL_PRIORITY, DEFAULT_LOOP, mScale[step]); 159 if (DEBUG) Log.d(LOG_TAG, "Start note " + id); 160 if (id == 0) { 161 Log.e(LOG_TAG, "Error occurred starting note"); 162 return false; 163 } 164 sleep(450); 165 mSoundPool.stop(id); 166 if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); 167 sleep(50); 168 } 169 if (DEBUG) Log.d(LOG_TAG, "End sounds test"); 170 return true; 171 } 172 173 private boolean TestRates() throws java.lang.InterruptedException { 174 if (DEBUG) Log.d(LOG_TAG, "Begin rate test"); 175 176 // start the note 177 int count = mScale.length; 178 int id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 179 NORMAL_PRIORITY, DEFAULT_LOOP, mScale[0]); 180 if (DEBUG) Log.d(LOG_TAG, "Start note " + id); 181 if (id == 0) { 182 Log.e(LOG_TAG, "Test failed - exiting"); 183 return false; 184 } 185 186 // modify the pitch 187 for (int step = 1; step < count; step++) { 188 sleep(250); 189 mSoundPool.setRate(id, mScale[step]); 190 if (DEBUG) Log.d(LOG_TAG, "Change rate " + mScale[step]); 191 } 192 mSoundPool.stop(id); 193 if (DEBUG) Log.d(LOG_TAG, "End rate test"); 194 return true; 195 } 196 197 private boolean TestPriority() throws java.lang.InterruptedException { 198 if (DEBUG) Log.d(LOG_TAG, "Begin priority test"); 199 boolean result = true; 200 201 // play a normal priority looping sound 202 int normalId = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 203 NORMAL_PRIORITY, DEFAULT_LOOP, 1.0f); 204 if (DEBUG) Log.d(LOG_TAG, "Start note " + normalId); 205 if (normalId == 0) { 206 Log.e(LOG_TAG, "Error occurred starting note"); 207 return false; 208 } 209 sleep(250); 210 211 // play a low priority sound 212 int id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 213 LOW_PRIORITY, DEFAULT_LOOP, 1.0f); 214 if (id > 0) { 215 Log.e(LOG_TAG, "Normal > Low priority test failed"); 216 result = false; 217 mSoundPool.stop(id); 218 } else { 219 Log.e(LOG_TAG, "Normal > Low priority test passed"); 220 } 221 sleep(250); 222 223 // play a high priority sound 224 id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 225 HIGH_PRIORITY, DEFAULT_LOOP, 1.0f); 226 if (id == 0) { 227 Log.e(LOG_TAG, "High > Normal priority test failed"); 228 result = false; 229 } else { 230 Log.e(LOG_TAG, "High > Normal priority test passed"); 231 } 232 sleep(250); 233 mSoundPool.stop(id); 234 235 // stop normal note 236 mSoundPool.stop(normalId); 237 238 if (DEBUG) Log.d(LOG_TAG, "End priority test"); 239 return result; 240 } 241 242 private boolean TestPauseResume() throws java.lang.InterruptedException { 243 if (DEBUG) Log.d(LOG_TAG, "Begin pause/resume test"); 244 boolean result = true; 245 246 // play a normal priority looping sound 247 int id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 248 NORMAL_PRIORITY, DEFAULT_LOOP, 1.0f); 249 if (DEBUG) Log.d(LOG_TAG, "Start note " + id); 250 if (id == 0) { 251 Log.e(LOG_TAG, "Error occurred starting note"); 252 return false; 253 } 254 sleep(250); 255 256 // pause and resume sound a few times 257 for (int count = 0; count < 5; count++) { 258 mSoundPool.pause(id); 259 sleep(250); 260 mSoundPool.resume(id); 261 sleep(250); 262 } 263 264 mSoundPool.stop(id); 265 266 // play 5 sounds, forces one to be stolen 267 int ids[] = new int[5]; 268 for (int i = 0; i < 5; i++) { 269 ids[i] = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, 270 NORMAL_PRIORITY, DEFAULT_LOOP, mScale[i]); 271 if (DEBUG) Log.d(LOG_TAG, "Start note " + ids[i]); 272 if (ids[i] == 0) { 273 Log.e(LOG_TAG, "Error occurred starting note"); 274 return false; 275 } 276 sleep(250); 277 } 278 279 // pause and resume sound a few times 280 for (int count = 0; count < 5; count++) { 281 mSoundPool.autoPause(); 282 sleep(250); 283 mSoundPool.autoResume(); 284 sleep(250); 285 } 286 287 for (int i = 0; i < 5; i++) { 288 mSoundPool.stop(ids[i]); 289 } 290 291 if (DEBUG) Log.d(LOG_TAG, "End pause/resume test"); 292 return result; 293 } 294 295 private boolean TestVolume() throws java.lang.InterruptedException { 296 if (DEBUG) Log.d(LOG_TAG, "Begin volume test"); 297 298 // start the note 299 int id = mSoundPool.play(mSounds[0], 0.0f, 1.0f, NORMAL_PRIORITY, DEFAULT_LOOP, mScale[0]); 300 if (DEBUG) Log.d(LOG_TAG, "Start note " + id); 301 if (id == 0) { 302 Log.e(LOG_TAG, "Test failed - exiting"); 303 return false; 304 } 305 306 // pan from left to right 307 for (int count = 0; count < 101; count++) { 308 sleep(20); 309 double radians = PI_OVER_2 * count / 100.0; 310 float leftVolume = (float) Math.sin(radians); 311 float rightVolume = (float) Math.cos(radians); 312 mSoundPool.setVolume(id, leftVolume, rightVolume); 313 if (DEBUG) Log.d(LOG_TAG, "Change volume (" + leftVolume + "," + rightVolume + ")"); 314 } 315 316 mSoundPool.stop(id); 317 if (DEBUG) Log.d(LOG_TAG, "End volume test"); 318 return true; 319 } 320 321 public void run() { 322 if (DEBUG) Log.d(LOG_TAG, "Test thread running"); 323 324 // initialize 325 mRunning = true; 326 int failures = 0; 327 328 // initialize pitch table 329 float pitch = 0.5f; 330 mScale = new float[13]; 331 for (int i = 0; i < 13; ++i) { 332 mScale[i] = pitch; 333 pitch *= SEMITONE; 334 } 335 336 try { 337 338 // do single stream tests 339 initSoundPool(1); 340 if (!TestSounds()) failures = failures + 1; 341 if (!TestScales()) failures = failures + 1; 342 if (!TestRates()) failures = failures + 1; 343 if (!TestPriority()) failures = failures + 1; 344 if (!TestVolume()) failures = failures + 1; 345 346 // do multiple stream tests 347 initSoundPool(4); 348 if (!TestPauseResume()) failures = failures + 1; 349 350 } catch (java.lang.InterruptedException e) { 351 if (DEBUG) Log.d(LOG_TAG, "Test interrupted"); 352 failures = failures + 1; 353 } finally { 354 mRunning = false; 355 } 356 357 // release sound pool 358 if (mSoundPool != null) { 359 mSoundPool.release(); 360 mSoundPool = null; 361 } 362 363 // output stats 364 if (DEBUG) Log.d(LOG_TAG, "Test thread exit"); 365 if (failures == 0) { 366 Log.i(LOG_TAG, "All tests passed"); 367 } else { 368 Log.i(LOG_TAG, failures + " tests failed"); 369 } 370 } 371 372 public void quit() { 373 if (DEBUG) Log.d(LOG_TAG, "interrupt"); 374 interrupt(); 375 while (mRunning) { 376 try { 377 sleep(20); 378 } catch (java.lang.InterruptedException e) { } 379 } 380 if (DEBUG) Log.d(LOG_TAG, "quit"); 381 } 382 } 383 384 private void startTests() { 385 mThread = new TestThread(); 386 mThread.start(); 387 } 388 389 protected void onPause() 390 { 391 Log.v(LOG_TAG, "onPause"); 392 super.onPause(); 393 mThread.quit(); 394 mThread = null; 395 } 396 397 protected void onResume() 398 { 399 Log.v(LOG_TAG, "onResume"); 400 super.onResume(); 401 startTests(); 402 } 403 404 public void onCreate(Bundle icicle) 405 { 406 super.onCreate(icicle); 407 setVolumeControlStream(AudioManager.STREAM_MUSIC); 408 } 409 } 410 411