1 /* 2 * Copyright (C) 2007 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; 18 19 import java.io.File; 20 import java.io.FileDescriptor; 21 import java.lang.ref.WeakReference; 22 23 import android.app.ActivityThread; 24 import android.app.AppOpsManager; 25 import android.content.Context; 26 import android.content.res.AssetFileDescriptor; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.ParcelFileDescriptor; 32 import android.os.Process; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.SystemProperties; 36 import android.util.AndroidRuntimeException; 37 import android.util.Log; 38 39 import com.android.internal.app.IAppOpsService; 40 41 42 /** 43 * The SoundPool class manages and plays audio resources for applications. 44 * 45 * <p>A SoundPool is a collection of samples that can be loaded into memory 46 * from a resource inside the APK or from a file in the file system. The 47 * SoundPool library uses the MediaPlayer service to decode the audio 48 * into a raw 16-bit PCM mono or stereo stream. This allows applications 49 * to ship with compressed streams without having to suffer the CPU load 50 * and latency of decompressing during playback.</p> 51 * 52 * <p>In addition to low-latency playback, SoundPool can also manage the number 53 * of audio streams being rendered at once. When the SoundPool object is 54 * constructed, the maxStreams parameter sets the maximum number of streams 55 * that can be played at a time from this single SoundPool. SoundPool tracks 56 * the number of active streams. If the maximum number of streams is exceeded, 57 * SoundPool will automatically stop a previously playing stream based first 58 * on priority and then by age within that priority. Limiting the maximum 59 * number of streams helps to cap CPU loading and reducing the likelihood that 60 * audio mixing will impact visuals or UI performance.</p> 61 * 62 * <p>Sounds can be looped by setting a non-zero loop value. A value of -1 63 * causes the sound to loop forever. In this case, the application must 64 * explicitly call the stop() function to stop the sound. Any other non-zero 65 * value will cause the sound to repeat the specified number of times, e.g. 66 * a value of 3 causes the sound to play a total of 4 times.</p> 67 * 68 * <p>The playback rate can also be changed. A playback rate of 1.0 causes 69 * the sound to play at its original frequency (resampled, if necessary, 70 * to the hardware output frequency). A playback rate of 2.0 causes the 71 * sound to play at twice its original frequency, and a playback rate of 72 * 0.5 causes it to play at half its original frequency. The playback 73 * rate range is 0.5 to 2.0.</p> 74 * 75 * <p>Priority runs low to high, i.e. higher numbers are higher priority. 76 * Priority is used when a call to play() would cause the number of active 77 * streams to exceed the value established by the maxStreams parameter when 78 * the SoundPool was created. In this case, the stream allocator will stop 79 * the lowest priority stream. If there are multiple streams with the same 80 * low priority, it will choose the oldest stream to stop. In the case 81 * where the priority of the new stream is lower than all the active 82 * streams, the new sound will not play and the play() function will return 83 * a streamID of zero.</p> 84 * 85 * <p>Let's examine a typical use case: A game consists of several levels of 86 * play. For each level, there is a set of unique sounds that are used only 87 * by that level. In this case, the game logic should create a new SoundPool 88 * object when the first level is loaded. The level data itself might contain 89 * the list of sounds to be used by this level. The loading logic iterates 90 * through the list of sounds calling the appropriate SoundPool.load() 91 * function. This should typically be done early in the process to allow time 92 * for decompressing the audio to raw PCM format before they are needed for 93 * playback.</p> 94 * 95 * <p>Once the sounds are loaded and play has started, the application can 96 * trigger sounds by calling SoundPool.play(). Playing streams can be 97 * paused or resumed, and the application can also alter the pitch by 98 * adjusting the playback rate in real-time for doppler or synthesis 99 * effects.</p> 100 * 101 * <p>Note that since streams can be stopped due to resource constraints, the 102 * streamID is a reference to a particular instance of a stream. If the stream 103 * is stopped to allow a higher priority stream to play, the stream is no 104 * longer be valid. However, the application is allowed to call methods on 105 * the streamID without error. This may help simplify program logic since 106 * the application need not concern itself with the stream lifecycle.</p> 107 * 108 * <p>In our example, when the player has completed the level, the game 109 * logic should call SoundPool.release() to release all the native resources 110 * in use and then set the SoundPool reference to null. If the player starts 111 * another level, a new SoundPool is created, sounds are loaded, and play 112 * resumes.</p> 113 */ 114 public class SoundPool { 115 private final SoundPoolDelegate mImpl; 116 117 /** 118 * Constructor. Constructs a SoundPool object with the following 119 * characteristics: 120 * 121 * @param maxStreams the maximum number of simultaneous streams for this 122 * SoundPool object 123 * @param streamType the audio stream type as described in AudioManager 124 * For example, game applications will normally use 125 * {@link AudioManager#STREAM_MUSIC}. 126 * @param srcQuality the sample-rate converter quality. Currently has no 127 * effect. Use 0 for the default. 128 * @return a SoundPool object, or null if creation failed 129 * @deprecated use {@link SoundPool.Builder} instead to create and configure a 130 * SoundPool instance 131 */ SoundPool(int maxStreams, int streamType, int srcQuality)132 public SoundPool(int maxStreams, int streamType, int srcQuality) { 133 this(maxStreams, 134 new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); 135 } 136 SoundPool(int maxStreams, AudioAttributes attributes)137 private SoundPool(int maxStreams, AudioAttributes attributes) { 138 if (SystemProperties.getBoolean("config.disable_media", false)) { 139 mImpl = new SoundPoolStub(); 140 } else { 141 mImpl = new SoundPoolImpl(this, maxStreams, attributes); 142 } 143 } 144 145 /** 146 * Builder class for {@link SoundPool} objects. 147 */ 148 public static class Builder { 149 private int mMaxStreams = 1; 150 private AudioAttributes mAudioAttributes; 151 152 /** 153 * Constructs a new Builder with the defaults format values. 154 * If not provided, the maximum number of streams is 1 (see {@link #setMaxStreams(int)} to 155 * change it), and the audio attributes have a usage value of 156 * {@link AudioAttributes#USAGE_MEDIA} (see {@link #setAudioAttributes(AudioAttributes)} to 157 * change them). 158 */ Builder()159 public Builder() { 160 } 161 162 /** 163 * Sets the maximum of number of simultaneous streams that can be played simultaneously. 164 * @param maxStreams a value equal to 1 or greater. 165 * @return the same Builder instance 166 * @throws IllegalArgumentException 167 */ setMaxStreams(int maxStreams)168 public Builder setMaxStreams(int maxStreams) throws IllegalArgumentException { 169 if (maxStreams <= 0) { 170 throw new IllegalArgumentException( 171 "Strictly positive value required for the maximum number of streams"); 172 } 173 mMaxStreams = maxStreams; 174 return this; 175 } 176 177 /** 178 * Sets the {@link AudioAttributes}. For examples, game applications will use attributes 179 * built with usage information set to {@link AudioAttributes#USAGE_GAME}. 180 * @param attributes a non-null 181 * @return 182 */ setAudioAttributes(AudioAttributes attributes)183 public Builder setAudioAttributes(AudioAttributes attributes) 184 throws IllegalArgumentException { 185 if (attributes == null) { 186 throw new IllegalArgumentException("Invalid null AudioAttributes"); 187 } 188 mAudioAttributes = attributes; 189 return this; 190 } 191 build()192 public SoundPool build() { 193 if (mAudioAttributes == null) { 194 mAudioAttributes = new AudioAttributes.Builder() 195 .setUsage(AudioAttributes.USAGE_MEDIA).build(); 196 } 197 return new SoundPool(mMaxStreams, mAudioAttributes); 198 } 199 } 200 201 /** 202 * Load the sound from the specified path. 203 * 204 * @param path the path to the audio file 205 * @param priority the priority of the sound. Currently has no effect. Use 206 * a value of 1 for future compatibility. 207 * @return a sound ID. This value can be used to play or unload the sound. 208 */ load(String path, int priority)209 public int load(String path, int priority) { 210 return mImpl.load(path, priority); 211 } 212 213 /** 214 * Load the sound from the specified APK resource. 215 * 216 * Note that the extension is dropped. For example, if you want to load 217 * a sound from the raw resource file "explosion.mp3", you would specify 218 * "R.raw.explosion" as the resource ID. Note that this means you cannot 219 * have both an "explosion.wav" and an "explosion.mp3" in the res/raw 220 * directory. 221 * 222 * @param context the application context 223 * @param resId the resource ID 224 * @param priority the priority of the sound. Currently has no effect. Use 225 * a value of 1 for future compatibility. 226 * @return a sound ID. This value can be used to play or unload the sound. 227 */ load(Context context, int resId, int priority)228 public int load(Context context, int resId, int priority) { 229 return mImpl.load(context, resId, priority); 230 } 231 232 /** 233 * Load the sound from an asset file descriptor. 234 * 235 * @param afd an asset file descriptor 236 * @param priority the priority of the sound. Currently has no effect. Use 237 * a value of 1 for future compatibility. 238 * @return a sound ID. This value can be used to play or unload the sound. 239 */ load(AssetFileDescriptor afd, int priority)240 public int load(AssetFileDescriptor afd, int priority) { 241 return mImpl.load(afd, priority); 242 } 243 244 /** 245 * Load the sound from a FileDescriptor. 246 * 247 * This version is useful if you store multiple sounds in a single 248 * binary. The offset specifies the offset from the start of the file 249 * and the length specifies the length of the sound within the file. 250 * 251 * @param fd a FileDescriptor object 252 * @param offset offset to the start of the sound 253 * @param length length of the sound 254 * @param priority the priority of the sound. Currently has no effect. Use 255 * a value of 1 for future compatibility. 256 * @return a sound ID. This value can be used to play or unload the sound. 257 */ load(FileDescriptor fd, long offset, long length, int priority)258 public int load(FileDescriptor fd, long offset, long length, int priority) { 259 return mImpl.load(fd, offset, length, priority); 260 } 261 262 /** 263 * Unload a sound from a sound ID. 264 * 265 * Unloads the sound specified by the soundID. This is the value 266 * returned by the load() function. Returns true if the sound is 267 * successfully unloaded, false if the sound was already unloaded. 268 * 269 * @param soundID a soundID returned by the load() function 270 * @return true if just unloaded, false if previously unloaded 271 */ unload(int soundID)272 public final boolean unload(int soundID) { 273 return mImpl.unload(soundID); 274 } 275 276 /** 277 * Play a sound from a sound ID. 278 * 279 * Play the sound specified by the soundID. This is the value 280 * returned by the load() function. Returns a non-zero streamID 281 * if successful, zero if it fails. The streamID can be used to 282 * further control playback. Note that calling play() may cause 283 * another sound to stop playing if the maximum number of active 284 * streams is exceeded. A loop value of -1 means loop forever, 285 * a value of 0 means don't loop, other values indicate the 286 * number of repeats, e.g. a value of 1 plays the audio twice. 287 * The playback rate allows the application to vary the playback 288 * rate (pitch) of the sound. A value of 1.0 means play back at 289 * the original frequency. A value of 2.0 means play back twice 290 * as fast, and a value of 0.5 means playback at half speed. 291 * 292 * @param soundID a soundID returned by the load() function 293 * @param leftVolume left volume value (range = 0.0 to 1.0) 294 * @param rightVolume right volume value (range = 0.0 to 1.0) 295 * @param priority stream priority (0 = lowest priority) 296 * @param loop loop mode (0 = no loop, -1 = loop forever) 297 * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0) 298 * @return non-zero streamID if successful, zero if failed 299 */ play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)300 public final int play(int soundID, float leftVolume, float rightVolume, 301 int priority, int loop, float rate) { 302 return mImpl.play( 303 soundID, leftVolume, rightVolume, priority, loop, rate); 304 } 305 306 /** 307 * Pause a playback stream. 308 * 309 * Pause the stream specified by the streamID. This is the 310 * value returned by the play() function. If the stream is 311 * playing, it will be paused. If the stream is not playing 312 * (e.g. is stopped or was previously paused), calling this 313 * function will have no effect. 314 * 315 * @param streamID a streamID returned by the play() function 316 */ pause(int streamID)317 public final void pause(int streamID) { 318 mImpl.pause(streamID); 319 } 320 321 /** 322 * Resume a playback stream. 323 * 324 * Resume the stream specified by the streamID. This 325 * is the value returned by the play() function. If the stream 326 * is paused, this will resume playback. If the stream was not 327 * previously paused, calling this function will have no effect. 328 * 329 * @param streamID a streamID returned by the play() function 330 */ resume(int streamID)331 public final void resume(int streamID) { 332 mImpl.resume(streamID); 333 } 334 335 /** 336 * Pause all active streams. 337 * 338 * Pause all streams that are currently playing. This function 339 * iterates through all the active streams and pauses any that 340 * are playing. It also sets a flag so that any streams that 341 * are playing can be resumed by calling autoResume(). 342 */ autoPause()343 public final void autoPause() { 344 mImpl.autoPause(); 345 } 346 347 /** 348 * Resume all previously active streams. 349 * 350 * Automatically resumes all streams that were paused in previous 351 * calls to autoPause(). 352 */ autoResume()353 public final void autoResume() { 354 mImpl.autoResume(); 355 } 356 357 /** 358 * Stop a playback stream. 359 * 360 * Stop the stream specified by the streamID. This 361 * is the value returned by the play() function. If the stream 362 * is playing, it will be stopped. It also releases any native 363 * resources associated with this stream. If the stream is not 364 * playing, it will have no effect. 365 * 366 * @param streamID a streamID returned by the play() function 367 */ stop(int streamID)368 public final void stop(int streamID) { 369 mImpl.stop(streamID); 370 } 371 372 /** 373 * Set stream volume. 374 * 375 * Sets the volume on the stream specified by the streamID. 376 * This is the value returned by the play() function. The 377 * value must be in the range of 0.0 to 1.0. If the stream does 378 * not exist, it will have no effect. 379 * 380 * @param streamID a streamID returned by the play() function 381 * @param leftVolume left volume value (range = 0.0 to 1.0) 382 * @param rightVolume right volume value (range = 0.0 to 1.0) 383 */ setVolume(int streamID, float leftVolume, float rightVolume)384 public final void setVolume(int streamID, 385 float leftVolume, float rightVolume) { 386 mImpl.setVolume(streamID, leftVolume, rightVolume); 387 } 388 389 /** 390 * Similar, except set volume of all channels to same value. 391 * @hide 392 */ setVolume(int streamID, float volume)393 public void setVolume(int streamID, float volume) { 394 setVolume(streamID, volume, volume); 395 } 396 397 /** 398 * Change stream priority. 399 * 400 * Change the priority of the stream specified by the streamID. 401 * This is the value returned by the play() function. Affects the 402 * order in which streams are re-used to play new sounds. If the 403 * stream does not exist, it will have no effect. 404 * 405 * @param streamID a streamID returned by the play() function 406 */ setPriority(int streamID, int priority)407 public final void setPriority(int streamID, int priority) { 408 mImpl.setPriority(streamID, priority); 409 } 410 411 /** 412 * Set loop mode. 413 * 414 * Change the loop mode. A loop value of -1 means loop forever, 415 * a value of 0 means don't loop, other values indicate the 416 * number of repeats, e.g. a value of 1 plays the audio twice. 417 * If the stream does not exist, it will have no effect. 418 * 419 * @param streamID a streamID returned by the play() function 420 * @param loop loop mode (0 = no loop, -1 = loop forever) 421 */ setLoop(int streamID, int loop)422 public final void setLoop(int streamID, int loop) { 423 mImpl.setLoop(streamID, loop); 424 } 425 426 /** 427 * Change playback rate. 428 * 429 * The playback rate allows the application to vary the playback 430 * rate (pitch) of the sound. A value of 1.0 means playback at 431 * the original frequency. A value of 2.0 means playback twice 432 * as fast, and a value of 0.5 means playback at half speed. 433 * If the stream does not exist, it will have no effect. 434 * 435 * @param streamID a streamID returned by the play() function 436 * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0) 437 */ setRate(int streamID, float rate)438 public final void setRate(int streamID, float rate) { 439 mImpl.setRate(streamID, rate); 440 } 441 442 public interface OnLoadCompleteListener { 443 /** 444 * Called when a sound has completed loading. 445 * 446 * @param soundPool SoundPool object from the load() method 447 * @param sampleId the sample ID of the sound loaded. 448 * @param status the status of the load operation (0 = success) 449 */ onLoadComplete(SoundPool soundPool, int sampleId, int status)450 public void onLoadComplete(SoundPool soundPool, int sampleId, int status); 451 } 452 453 /** 454 * Sets the callback hook for the OnLoadCompleteListener. 455 */ setOnLoadCompleteListener(OnLoadCompleteListener listener)456 public void setOnLoadCompleteListener(OnLoadCompleteListener listener) { 457 mImpl.setOnLoadCompleteListener(listener); 458 } 459 460 /** 461 * Release the SoundPool resources. 462 * 463 * Release all memory and native resources used by the SoundPool 464 * object. The SoundPool can no longer be used and the reference 465 * should be set to null. 466 */ release()467 public final void release() { 468 mImpl.release(); 469 } 470 471 /** 472 * Interface for SoundPool implementations. 473 * SoundPool is statically referenced and unconditionally called from all 474 * over the framework, so we can't simply omit the class or make it throw 475 * runtime exceptions, as doing so would break the framework. Instead we 476 * now select either a real or no-op impl object based on whether media is 477 * enabled. 478 * 479 * @hide 480 */ 481 /* package */ interface SoundPoolDelegate { load(String path, int priority)482 public int load(String path, int priority); load(Context context, int resId, int priority)483 public int load(Context context, int resId, int priority); load(AssetFileDescriptor afd, int priority)484 public int load(AssetFileDescriptor afd, int priority); load( FileDescriptor fd, long offset, long length, int priority)485 public int load( 486 FileDescriptor fd, long offset, long length, int priority); unload(int soundID)487 public boolean unload(int soundID); play( int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)488 public int play( 489 int soundID, float leftVolume, float rightVolume, 490 int priority, int loop, float rate); pause(int streamID)491 public void pause(int streamID); resume(int streamID)492 public void resume(int streamID); autoPause()493 public void autoPause(); autoResume()494 public void autoResume(); stop(int streamID)495 public void stop(int streamID); setVolume(int streamID, float leftVolume, float rightVolume)496 public void setVolume(int streamID, float leftVolume, float rightVolume); setVolume(int streamID, float volume)497 public void setVolume(int streamID, float volume); setPriority(int streamID, int priority)498 public void setPriority(int streamID, int priority); setLoop(int streamID, int loop)499 public void setLoop(int streamID, int loop); setRate(int streamID, float rate)500 public void setRate(int streamID, float rate); setOnLoadCompleteListener(OnLoadCompleteListener listener)501 public void setOnLoadCompleteListener(OnLoadCompleteListener listener); release()502 public void release(); 503 } 504 505 506 /** 507 * Real implementation of the delegate interface. This was formerly the 508 * body of SoundPool itself. 509 */ 510 /* package */ static class SoundPoolImpl implements SoundPoolDelegate { 511 static { System.loadLibrary("soundpool"); } 512 513 private final static String TAG = "SoundPool"; 514 private final static boolean DEBUG = false; 515 516 private long mNativeContext; // accessed by native methods 517 518 private EventHandler mEventHandler; 519 private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener; 520 private SoundPool mProxy; 521 522 private final Object mLock; 523 private final AudioAttributes mAttributes; 524 private final IAppOpsService mAppOps; 525 526 // SoundPool messages 527 // 528 // must match SoundPool.h 529 private static final int SAMPLE_LOADED = 1; 530 SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr)531 public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) { 532 533 // do native setup 534 if (native_setup(new WeakReference(this), maxStreams, attr) != 0) { 535 throw new RuntimeException("Native setup failed"); 536 } 537 mLock = new Object(); 538 mProxy = proxy; 539 mAttributes = attr; 540 IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); 541 mAppOps = IAppOpsService.Stub.asInterface(b); 542 } 543 load(String path, int priority)544 public int load(String path, int priority) 545 { 546 // pass network streams to player 547 if (path.startsWith("http:")) 548 return _load(path, priority); 549 550 // try local path 551 int id = 0; 552 try { 553 File f = new File(path); 554 ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); 555 if (fd != null) { 556 id = _load(fd.getFileDescriptor(), 0, f.length(), priority); 557 fd.close(); 558 } 559 } catch (java.io.IOException e) { 560 Log.e(TAG, "error loading " + path); 561 } 562 return id; 563 } 564 load(Context context, int resId, int priority)565 public int load(Context context, int resId, int priority) { 566 AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId); 567 int id = 0; 568 if (afd != null) { 569 id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority); 570 try { 571 afd.close(); 572 } catch (java.io.IOException ex) { 573 //Log.d(TAG, "close failed:", ex); 574 } 575 } 576 return id; 577 } 578 load(AssetFileDescriptor afd, int priority)579 public int load(AssetFileDescriptor afd, int priority) { 580 if (afd != null) { 581 long len = afd.getLength(); 582 if (len < 0) { 583 throw new AndroidRuntimeException("no length for fd"); 584 } 585 return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority); 586 } else { 587 return 0; 588 } 589 } 590 load(FileDescriptor fd, long offset, long length, int priority)591 public int load(FileDescriptor fd, long offset, long length, int priority) { 592 return _load(fd, offset, length, priority); 593 } 594 _load(String uri, int priority)595 private native final int _load(String uri, int priority); 596 _load(FileDescriptor fd, long offset, long length, int priority)597 private native final int _load(FileDescriptor fd, long offset, long length, int priority); 598 unload(int soundID)599 public native final boolean unload(int soundID); 600 play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)601 public final int play(int soundID, float leftVolume, float rightVolume, 602 int priority, int loop, float rate) { 603 if (isRestricted()) { 604 leftVolume = rightVolume = 0; 605 } 606 return _play(soundID, leftVolume, rightVolume, priority, loop, rate); 607 } 608 _play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)609 public native final int _play(int soundID, float leftVolume, float rightVolume, 610 int priority, int loop, float rate); 611 isRestricted()612 private boolean isRestricted() { 613 try { 614 final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, 615 mAttributes.getUsage(), 616 Process.myUid(), ActivityThread.currentPackageName()); 617 return mode != AppOpsManager.MODE_ALLOWED; 618 } catch (RemoteException e) { 619 return false; 620 } 621 } 622 pause(int streamID)623 public native final void pause(int streamID); 624 resume(int streamID)625 public native final void resume(int streamID); 626 autoPause()627 public native final void autoPause(); 628 autoResume()629 public native final void autoResume(); 630 stop(int streamID)631 public native final void stop(int streamID); 632 setVolume(int streamID, float leftVolume, float rightVolume)633 public final void setVolume(int streamID, float leftVolume, float rightVolume) { 634 if (isRestricted()) { 635 return; 636 } 637 _setVolume(streamID, leftVolume, rightVolume); 638 } 639 _setVolume(int streamID, float leftVolume, float rightVolume)640 private native final void _setVolume(int streamID, float leftVolume, float rightVolume); 641 setVolume(int streamID, float volume)642 public void setVolume(int streamID, float volume) { 643 setVolume(streamID, volume, volume); 644 } 645 setPriority(int streamID, int priority)646 public native final void setPriority(int streamID, int priority); 647 setLoop(int streamID, int loop)648 public native final void setLoop(int streamID, int loop); 649 setRate(int streamID, float rate)650 public native final void setRate(int streamID, float rate); 651 setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)652 public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) 653 { 654 synchronized(mLock) { 655 if (listener != null) { 656 // setup message handler 657 Looper looper; 658 if ((looper = Looper.myLooper()) != null) { 659 mEventHandler = new EventHandler(mProxy, looper); 660 } else if ((looper = Looper.getMainLooper()) != null) { 661 mEventHandler = new EventHandler(mProxy, looper); 662 } else { 663 mEventHandler = null; 664 } 665 } else { 666 mEventHandler = null; 667 } 668 mOnLoadCompleteListener = listener; 669 } 670 } 671 672 private class EventHandler extends Handler 673 { 674 private SoundPool mSoundPool; 675 EventHandler(SoundPool soundPool, Looper looper)676 public EventHandler(SoundPool soundPool, Looper looper) { 677 super(looper); 678 mSoundPool = soundPool; 679 } 680 681 @Override handleMessage(Message msg)682 public void handleMessage(Message msg) { 683 switch(msg.what) { 684 case SAMPLE_LOADED: 685 if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded"); 686 synchronized(mLock) { 687 if (mOnLoadCompleteListener != null) { 688 mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2); 689 } 690 } 691 break; 692 default: 693 Log.e(TAG, "Unknown message type " + msg.what); 694 return; 695 } 696 } 697 } 698 699 // post event from native code to message handler postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)700 private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj) 701 { 702 SoundPoolImpl soundPoolImpl = (SoundPoolImpl)((WeakReference)weakRef).get(); 703 if (soundPoolImpl == null) 704 return; 705 706 if (soundPoolImpl.mEventHandler != null) { 707 Message m = soundPoolImpl.mEventHandler.obtainMessage(msg, arg1, arg2, obj); 708 soundPoolImpl.mEventHandler.sendMessage(m); 709 } 710 } 711 release()712 public native final void release(); 713 native_setup(Object weakRef, int maxStreams, Object attributes)714 private native final int native_setup(Object weakRef, int maxStreams, 715 Object/*AudioAttributes*/ attributes); 716 finalize()717 protected void finalize() { release(); } 718 } 719 720 /** 721 * No-op implementation of SoundPool. 722 * Used when media is disabled by the system. 723 * @hide 724 */ 725 /* package */ static class SoundPoolStub implements SoundPoolDelegate { SoundPoolStub()726 public SoundPoolStub() { } 727 load(String path, int priority)728 public int load(String path, int priority) { 729 return 0; 730 } 731 load(Context context, int resId, int priority)732 public int load(Context context, int resId, int priority) { 733 return 0; 734 } 735 load(AssetFileDescriptor afd, int priority)736 public int load(AssetFileDescriptor afd, int priority) { 737 return 0; 738 } 739 load(FileDescriptor fd, long offset, long length, int priority)740 public int load(FileDescriptor fd, long offset, long length, int priority) { 741 return 0; 742 } 743 unload(int soundID)744 public final boolean unload(int soundID) { 745 return true; 746 } 747 play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)748 public final int play(int soundID, float leftVolume, float rightVolume, 749 int priority, int loop, float rate) { 750 return 0; 751 } 752 pause(int streamID)753 public final void pause(int streamID) { } 754 resume(int streamID)755 public final void resume(int streamID) { } 756 autoPause()757 public final void autoPause() { } 758 autoResume()759 public final void autoResume() { } 760 stop(int streamID)761 public final void stop(int streamID) { } 762 setVolume(int streamID, float leftVolume, float rightVolume)763 public final void setVolume(int streamID, 764 float leftVolume, float rightVolume) { } 765 setVolume(int streamID, float volume)766 public void setVolume(int streamID, float volume) { 767 } 768 setPriority(int streamID, int priority)769 public final void setPriority(int streamID, int priority) { } 770 setLoop(int streamID, int loop)771 public final void setLoop(int streamID, int loop) { } 772 setRate(int streamID, float rate)773 public final void setRate(int streamID, float rate) { } 774 setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)775 public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) { 776 } 777 release()778 public final void release() { } 779 } 780 } 781