• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.util.AndroidRuntimeException;
20 import android.util.Log;
21 import java.io.File;
22 import java.io.FileDescriptor;
23 import android.os.ParcelFileDescriptor;
24 import java.lang.ref.WeakReference;
25 import android.content.Context;
26 import android.content.res.AssetFileDescriptor;
27 import java.io.IOException;
28 
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Message;
32 
33 /**
34  * The SoundPool class manages and plays audio resources for applications.
35  *
36  * <p>A SoundPool is a collection of samples that can be loaded into memory
37  * from a resource inside the APK or from a file in the file system. The
38  * SoundPool library uses the MediaPlayer service to decode the audio
39  * into a raw 16-bit PCM mono or stereo stream. This allows applications
40  * to ship with compressed streams without having to suffer the CPU load
41  * and latency of decompressing during playback.</p>
42  *
43  * <p>In addition to low-latency playback, SoundPool can also manage the number
44  * of audio streams being rendered at once. When the SoundPool object is
45  * constructed, the maxStreams parameter sets the maximum number of streams
46  * that can be played at a time from this single SoundPool. SoundPool tracks
47  * the number of active streams. If the maximum number of streams is exceeded,
48  * SoundPool will automatically stop a previously playing stream based first
49  * on priority and then by age within that priority. Limiting the maximum
50  * number of streams helps to cap CPU loading and reducing the likelihood that
51  * audio mixing will impact visuals or UI performance.</p>
52  *
53  * <p>Sounds can be looped by setting a non-zero loop value. A value of -1
54  * causes the sound to loop forever. In this case, the application must
55  * explicitly call the stop() function to stop the sound. Any other non-zero
56  * value will cause the sound to repeat the specified number of times, e.g.
57  * a value of 3 causes the sound to play a total of 4 times.</p>
58  *
59  * <p>The playback rate can also be changed. A playback rate of 1.0 causes
60  * the sound to play at its original frequency (resampled, if necessary,
61  * to the hardware output frequency). A playback rate of 2.0 causes the
62  * sound to play at twice its original frequency, and a playback rate of
63  * 0.5 causes it to play at half its original frequency. The playback
64  * rate range is 0.5 to 2.0.</p>
65  *
66  * <p>Priority runs low to high, i.e. higher numbers are higher priority.
67  * Priority is used when a call to play() would cause the number of active
68  * streams to exceed the value established by the maxStreams parameter when
69  * the SoundPool was created. In this case, the stream allocator will stop
70  * the lowest priority stream. If there are multiple streams with the same
71  * low priority, it will choose the oldest stream to stop. In the case
72  * where the priority of the new stream is lower than all the active
73  * streams, the new sound will not play and the play() function will return
74  * a streamID of zero.</p>
75  *
76  * <p>Let's examine a typical use case: A game consists of several levels of
77  * play. For each level, there is a set of unique sounds that are used only
78  * by that level. In this case, the game logic should create a new SoundPool
79  * object when the first level is loaded. The level data itself might contain
80  * the list of sounds to be used by this level. The loading logic iterates
81  * through the list of sounds calling the appropriate SoundPool.load()
82  * function. This should typically be done early in the process to allow time
83  * for decompressing the audio to raw PCM format before they are needed for
84  * playback.</p>
85  *
86  * <p>Once the sounds are loaded and play has started, the application can
87  * trigger sounds by calling SoundPool.play(). Playing streams can be
88  * paused or resumed, and the application can also alter the pitch by
89  * adjusting the playback rate in real-time for doppler or synthesis
90  * effects.</p>
91  *
92  * <p>Note that since streams can be stopped due to resource constraints, the
93  * streamID is a reference to a particular instance of a stream. If the stream
94  * is stopped to allow a higher priority stream to play, the stream is no
95  * longer be valid. However, the application is allowed to call methods on
96  * the streamID without error. This may help simplify program logic since
97  * the application need not concern itself with the stream lifecycle.</p>
98  *
99  * <p>In our example, when the player has completed the level, the game
100  * logic should call SoundPool.release() to release all the native resources
101  * in use and then set the SoundPool reference to null. If the player starts
102  * another level, a new SoundPool is created, sounds are loaded, and play
103  * resumes.</p>
104  */
105 public class SoundPool
106 {
107     static { System.loadLibrary("soundpool"); }
108 
109     private final static String TAG = "SoundPool";
110     private final static boolean DEBUG = false;
111 
112     private int mNativeContext; // accessed by native methods
113 
114     private EventHandler mEventHandler;
115     private OnLoadCompleteListener mOnLoadCompleteListener;
116 
117     private final Object mLock;
118 
119     // SoundPool messages
120     //
121     // must match SoundPool.h
122     private static final int SAMPLE_LOADED = 1;
123 
124     /**
125      * Constructor. Constructs a SoundPool object with the following
126      * characteristics:
127      *
128      * @param maxStreams the maximum number of simultaneous streams for this
129      *                   SoundPool object
130      * @param streamType the audio stream type as described in AudioManager
131      *                   For example, game applications will normally use
132      *                   {@link AudioManager#STREAM_MUSIC}.
133      * @param srcQuality the sample-rate converter quality. Currently has no
134      *                   effect. Use 0 for the default.
135      * @return a SoundPool object, or null if creation failed
136      */
SoundPool(int maxStreams, int streamType, int srcQuality)137     public SoundPool(int maxStreams, int streamType, int srcQuality) {
138 
139         // do native setup
140         if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
141             throw new RuntimeException("Native setup failed");
142         }
143         mLock = new Object();
144     }
145 
146     /**
147      * Load the sound from the specified path.
148      *
149      * @param path the path to the audio file
150      * @param priority the priority of the sound. Currently has no effect. Use
151      *                 a value of 1 for future compatibility.
152      * @return a sound ID. This value can be used to play or unload the sound.
153      */
load(String path, int priority)154     public int load(String path, int priority)
155     {
156         // pass network streams to player
157         if (path.startsWith("http:"))
158             return _load(path, priority);
159 
160         // try local path
161         int id = 0;
162         try {
163             File f = new File(path);
164             ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
165             if (fd != null) {
166                 id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
167                 fd.close();
168             }
169         } catch (java.io.IOException e) {
170             Log.e(TAG, "error loading " + path);
171         }
172         return id;
173     }
174 
175     /**
176      * Load the sound from the specified APK resource.
177      *
178      * Note that the extension is dropped. For example, if you want to load
179      * a sound from the raw resource file "explosion.mp3", you would specify
180      * "R.raw.explosion" as the resource ID. Note that this means you cannot
181      * have both an "explosion.wav" and an "explosion.mp3" in the res/raw
182      * directory.
183      *
184      * @param context the application context
185      * @param resId the resource ID
186      * @param priority the priority of the sound. Currently has no effect. Use
187      *                 a value of 1 for future compatibility.
188      * @return a sound ID. This value can be used to play or unload the sound.
189      */
load(Context context, int resId, int priority)190     public int load(Context context, int resId, int priority) {
191         AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
192         int id = 0;
193         if (afd != null) {
194             id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
195             try {
196                 afd.close();
197             } catch (java.io.IOException ex) {
198                 //Log.d(TAG, "close failed:", ex);
199             }
200         }
201         return id;
202     }
203 
204     /**
205      * Load the sound from an asset file descriptor.
206      *
207      * @param afd an asset file descriptor
208      * @param priority the priority of the sound. Currently has no effect. Use
209      *                 a value of 1 for future compatibility.
210      * @return a sound ID. This value can be used to play or unload the sound.
211      */
load(AssetFileDescriptor afd, int priority)212     public int load(AssetFileDescriptor afd, int priority) {
213         if (afd != null) {
214             long len = afd.getLength();
215             if (len < 0) {
216                 throw new AndroidRuntimeException("no length for fd");
217             }
218             return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
219         } else {
220             return 0;
221         }
222     }
223 
224     /**
225      * Load the sound from a FileDescriptor.
226      *
227      * This version is useful if you store multiple sounds in a single
228      * binary. The offset specifies the offset from the start of the file
229      * and the length specifies the length of the sound within the file.
230      *
231      * @param fd a FileDescriptor object
232      * @param offset offset to the start of the sound
233      * @param length length of the sound
234      * @param priority the priority of the sound. Currently has no effect. Use
235      *                 a value of 1 for future compatibility.
236      * @return a sound ID. This value can be used to play or unload the sound.
237      */
load(FileDescriptor fd, long offset, long length, int priority)238     public int load(FileDescriptor fd, long offset, long length, int priority) {
239         return _load(fd, offset, length, priority);
240     }
241 
_load(String uri, int priority)242     private native final int _load(String uri, int priority);
243 
_load(FileDescriptor fd, long offset, long length, int priority)244     private native final int _load(FileDescriptor fd, long offset, long length, int priority);
245 
246     /**
247      * Unload a sound from a sound ID.
248      *
249      * Unloads the sound specified by the soundID. This is the value
250      * returned by the load() function. Returns true if the sound is
251      * successfully unloaded, false if the sound was already unloaded.
252      *
253      * @param soundID a soundID returned by the load() function
254      * @return true if just unloaded, false if previously unloaded
255      */
unload(int soundID)256     public native final boolean unload(int soundID);
257 
258     /**
259      * Play a sound from a sound ID.
260      *
261      * Play the sound specified by the soundID. This is the value
262      * returned by the load() function. Returns a non-zero streamID
263      * if successful, zero if it fails. The streamID can be used to
264      * further control playback. Note that calling play() may cause
265      * another sound to stop playing if the maximum number of active
266      * streams is exceeded. A loop value of -1 means loop forever,
267      * a value of 0 means don't loop, other values indicate the
268      * number of repeats, e.g. a value of 1 plays the audio twice.
269      * The playback rate allows the application to vary the playback
270      * rate (pitch) of the sound. A value of 1.0 means play back at
271      * the original frequency. A value of 2.0 means play back twice
272      * as fast, and a value of 0.5 means playback at half speed.
273      *
274      * @param soundID a soundID returned by the load() function
275      * @param leftVolume left volume value (range = 0.0 to 1.0)
276      * @param rightVolume right volume value (range = 0.0 to 1.0)
277      * @param priority stream priority (0 = lowest priority)
278      * @param loop loop mode (0 = no loop, -1 = loop forever)
279      * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
280      * @return non-zero streamID if successful, zero if failed
281      */
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)282     public native final int play(int soundID, float leftVolume, float rightVolume,
283             int priority, int loop, float rate);
284 
285     /**
286      * Pause a playback stream.
287      *
288      * Pause the stream specified by the streamID. This is the
289      * value returned by the play() function. If the stream is
290      * playing, it will be paused. If the stream is not playing
291      * (e.g. is stopped or was previously paused), calling this
292      * function will have no effect.
293      *
294      * @param streamID a streamID returned by the play() function
295      */
pause(int streamID)296     public native final void pause(int streamID);
297 
298     /**
299      * Resume a playback stream.
300      *
301      * Resume the stream specified by the streamID. This
302      * is the value returned by the play() function. If the stream
303      * is paused, this will resume playback. If the stream was not
304      * previously paused, calling this function will have no effect.
305      *
306      * @param streamID a streamID returned by the play() function
307      */
resume(int streamID)308     public native final void resume(int streamID);
309 
310     /**
311      * Pause all active streams.
312      *
313      * Pause all streams that are currently playing. This function
314      * iterates through all the active streams and pauses any that
315      * are playing. It also sets a flag so that any streams that
316      * are playing can be resumed by calling autoResume().
317      */
autoPause()318     public native final void autoPause();
319 
320     /**
321      * Resume all previously active streams.
322      *
323      * Automatically resumes all streams that were paused in previous
324      * calls to autoPause().
325      */
autoResume()326     public native final void autoResume();
327 
328     /**
329      * Stop a playback stream.
330      *
331      * Stop the stream specified by the streamID. This
332      * is the value returned by the play() function. If the stream
333      * is playing, it will be stopped. It also releases any native
334      * resources associated with this stream. If the stream is not
335      * playing, it will have no effect.
336      *
337      * @param streamID a streamID returned by the play() function
338      */
stop(int streamID)339     public native final void stop(int streamID);
340 
341     /**
342      * Set stream volume.
343      *
344      * Sets the volume on the stream specified by the streamID.
345      * This is the value returned by the play() function. The
346      * value must be in the range of 0.0 to 1.0. If the stream does
347      * not exist, it will have no effect.
348      *
349      * @param streamID a streamID returned by the play() function
350      * @param leftVolume left volume value (range = 0.0 to 1.0)
351      * @param rightVolume right volume value (range = 0.0 to 1.0)
352      */
setVolume(int streamID, float leftVolume, float rightVolume)353     public native final void setVolume(int streamID,
354             float leftVolume, float rightVolume);
355 
356     /**
357      * Change stream priority.
358      *
359      * Change the priority of the stream specified by the streamID.
360      * This is the value returned by the play() function. Affects the
361      * order in which streams are re-used to play new sounds. If the
362      * stream does not exist, it will have no effect.
363      *
364      * @param streamID a streamID returned by the play() function
365      */
setPriority(int streamID, int priority)366     public native final void setPriority(int streamID, int priority);
367 
368     /**
369      * Set loop mode.
370      *
371      * Change the loop mode. A loop value of -1 means loop forever,
372      * a value of 0 means don't loop, other values indicate the
373      * number of repeats, e.g. a value of 1 plays the audio twice.
374      * If the stream does not exist, it will have no effect.
375      *
376      * @param streamID a streamID returned by the play() function
377      * @param loop loop mode (0 = no loop, -1 = loop forever)
378      */
setLoop(int streamID, int loop)379     public native final void setLoop(int streamID, int loop);
380 
381     /**
382      * Change playback rate.
383      *
384      * The playback rate allows the application to vary the playback
385      * rate (pitch) of the sound. A value of 1.0 means playback at
386      * the original frequency. A value of 2.0 means playback twice
387      * as fast, and a value of 0.5 means playback at half speed.
388      * If the stream does not exist, it will have no effect.
389      *
390      * @param streamID a streamID returned by the play() function
391      * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
392      */
setRate(int streamID, float rate)393     public native final void setRate(int streamID, float rate);
394 
395     /**
396      * Interface definition for a callback to be invoked when all the
397      * sounds are loaded.
398      */
399     public interface OnLoadCompleteListener
400     {
401         /**
402          * Called when a sound has completed loading.
403          *
404          * @param soundPool SoundPool object from the load() method
405          * @param soundPool the sample ID of the sound loaded.
406          * @param status the status of the load operation (0 = success)
407          */
onLoadComplete(SoundPool soundPool, int sampleId, int status)408         public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
409     }
410 
411     /**
412      * Sets the callback hook for the OnLoadCompleteListener.
413      */
setOnLoadCompleteListener(OnLoadCompleteListener listener)414     public void setOnLoadCompleteListener(OnLoadCompleteListener listener)
415     {
416         synchronized(mLock) {
417             if (listener != null) {
418                 // setup message handler
419                 Looper looper;
420                 if ((looper = Looper.myLooper()) != null) {
421                     mEventHandler = new EventHandler(this, looper);
422                 } else if ((looper = Looper.getMainLooper()) != null) {
423                     mEventHandler = new EventHandler(this, looper);
424                 } else {
425                     mEventHandler = null;
426                 }
427             } else {
428                 mEventHandler = null;
429             }
430             mOnLoadCompleteListener = listener;
431         }
432     }
433 
434     private class EventHandler extends Handler
435     {
436         private SoundPool mSoundPool;
437 
EventHandler(SoundPool soundPool, Looper looper)438         public EventHandler(SoundPool soundPool, Looper looper) {
439             super(looper);
440             mSoundPool = soundPool;
441         }
442 
443         @Override
handleMessage(Message msg)444         public void handleMessage(Message msg) {
445             switch(msg.what) {
446             case SAMPLE_LOADED:
447                 if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
448                 synchronized(mLock) {
449                     if (mOnLoadCompleteListener != null) {
450                         mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
451                     }
452                 }
453                 break;
454             default:
455                 Log.e(TAG, "Unknown message type " + msg.what);
456                 return;
457             }
458         }
459     }
460 
461     // post event from native code to message handler
postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)462     private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
463     {
464         SoundPool soundPool = (SoundPool)((WeakReference)weakRef).get();
465         if (soundPool == null)
466             return;
467 
468         if (soundPool.mEventHandler != null) {
469             Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
470             soundPool.mEventHandler.sendMessage(m);
471         }
472     }
473 
474     /**
475      * Release the SoundPool resources.
476      *
477      * Release all memory and native resources used by the SoundPool
478      * object. The SoundPool can no longer be used and the reference
479      * should be set to null.
480      */
release()481     public native final void release();
482 
native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality)483     private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
484 
finalize()485     protected void finalize() { release(); }
486 }
487