The SoundPool
class allows us to hold and manipulate a collection of sound FX; literally, a pool of sounds. The class handles everything from decompressing a sound file such as a .wav
or a .ogg
, keeping an identifying reference to it via an integer id
and, of course, playing the sound. When the sound is played, it is done so in a non-blocking manner (using a thread behind the scenes) that does not interfere with the smooth running of our app or our user's interaction with it.
The first thing we need to do is add the sound effects to a folder called assets
in the main
folder of the game project. We will do this for real shortly.
Next, in our Java code, declare an object of the SoundPool
type and an int
identifier for each and every sound effect we intend to use. We also need to declare another int
called nowPlaying,
which we can use to track which sound is currently playing. We will see how we do this shortly:
// create an identifier SoundPool sp; int nowPlaying =-1; int idFX1 = -1; float volume = 1;// Volumes rage from 0 through 1
Now, we will look at the two different ways we initialize a SoundPool,
depending upon the version of Android the device is using. This is the perfect opportunity to use our method of writing different code for different versions of Android.
The new way of initializing SoundPool involves us using an AudioAttributes
object to set the attributes of the pool of sound we want.
The first block of code uses chaining and calls four separate methods on one object that initializes our AudioAttributes
object (audioAttributes
). Also note that it is fine to have comments in-between parts of the chained method calls as these are ignored entirely by the compiler:
// Instantiate a SoundPool dependent on Android version if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // The new way // Build an AudioAttributes object AudioAttributes audioAttributes = // First method call new AudioAttributes.Builder() // Second method call .setUsage (AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) // Third method call .setContentType (AudioAttributes.CONTENT_TYPE_SONIFICATION) // Fourth method call .build();// Yay! A semicolon // Initialize the SoundPool sp = new SoundPool.Builder() .setMaxStreams(5) .setAudioAttributes(audioAttributes) .build(); }
In the preceding code, we used chaining and the Builder
method of this class to initialize an AudioAttributes
object to let it know that it will be used for user interface interaction with USAGE_ASSISTANCE_SONIFICATION
.
We also used CONTENT_TYPE_SONIFICATION,
which lets the class know it is for responsive sounds, for example, button clicks, a collision, or similar.
Now, we can initialize the SoundPool
(sp
) itself by passing in the AudioAttributes
object (audioAttributes
) and the maximum number of simultaneous sounds we are likely to want to play.
The second block of code chains another four methods to initialize sp,
including a call to setAudioAttributes
that uses the audioAttributes
object that we initialized in the earlier block of chained methods.
Now, we can write an else
block of code that will, of course, have the code for the old way of doing things.
There is no need for an AudioAttributes
object. Simply initialize the SoundPool
(sp
) by passing in the number of simultaneous sounds. The final parameter is for sound quality, and passing zero is all we need to do. This is much simpler than the new way, but also less flexible regarding the choices we can make:
else { // The old way sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); }
We could use the old way, and the newer versions of Android would handle this. However, we get a warning about using deprecated methods. This is what the official documentation says about it:
Furthermore, the new way gives access to more features, as we saw previously. And anyway, it's a good excuse to look at some simple code to handle different versions of Android.
Now, we can go ahead and load up (decompress) the sound files into our SoundPool
.
As with our thread control, we are required to wrap our code in try
-catch
blocks. This makes sense because reading a file can fail for reasons beyond our control, but also because we are forced to because the method that we use throws an exception and the code we write will not compile otherwise.
Inside the try
block, we declare and initialize objects of the AssetManager
and AssetFileDescriptor types
.
The AssetFileDescriptor
is initialized by using the openFd
method of the AssetManager
object that actually decompresses the sound file. We then initialize our id (idFX1
) at the same time as we load the contents of the AssetFileDescriptor
into our SoundPool
.
The catch
block simply outputs a message to the console to let us know whether something has gone wrong. Note that this code is the same, regardless of the Android version:
try{ // Create objects of the 2 required classes AssetManager assetManager = this.getAssets(); AssetFileDescriptor descriptor; // Load our fx in memory ready for use descriptor = assetManager.openFd("fx1.ogg"); idFX1 = sp.load(descriptor, 0); }catch(IOException e){ // Print an error message to the console Log.d("error", "failed to load sound files"); }
We are ready to make some noise.
At this point, there is a sound effect in our SoundPool
, and we have an id by which we can refer to it.
This code is the same regardless of how we built the SoundPool
object, and this is how we play the sound. Notice in the following line of code that we initialize the nowPlaying
variable with the return value from the same method that actually plays the sound.
The following code therefore simultaneously plays a sound and loads the value of the id that is being played into nowPlaying
:
nowPlaying = sp.play(idFX1, volume, volume, 0, repeats, 1);
The parameters of the play
method are as follows:
There's just one more thing we need to do before we make the sound demo app.
It is also very trivial to stop a sound when it is still playing with the stop
method. Note that there might be more than one sound effect playing at any given time, so the stop
method needs the ID of the sound effect to stop:
sp.stop(nowPlaying);
When you call play,
you only need to store the ID of the currently playing sound if you want to track it so that you can interact with it at a later time.
Now, we can make the Sound Demo app.
3.147.55.42