In this mini-project, we will play a sound on an Android device. We will see how we use the SoundPool class to load a sound from a file and then play it. This is another significant step on the journey to build a complete game.
Create a new project
Create a new Android Studio project, call it Sound FX Demo with a Empty Activity called SoundFXDemoActivity. Delete all the code in SoundFXDemoActivity.java and enter the code below. I have listed all the import... code. Remember you can either type this in now or wait until we use the appropriate class and press the Alt|Enter keyboard combination to enter the import... line automatically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; import android.util.Log; import android.app.Activity; import java.io.IOException; public class SoundFXDemoActivity extends Activity { // This is the entry point to our game @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Our sound FX code will go here } } |
As you can see in the code above we have a typical project, much like our first-ever blank project. Notice that we don’t call setContentView... to set the view to the screen. This is because this app won’t display anything, it will just play a sound.
Adding a sound file to the project
We need a sound FX file that we want to play. You can obtain them from free download sites like freesound.org or you can get a really cool free sound-generating software package called BFXR.  Alternatively, you can download my warp file which I generated using BFX. Right-click this link and select Save as…
Here is what you will hear when the project is finished.
When you have your preferred sound effect make sure it is either .wav or .ogg format and name it warp.wav/warp.ogg. Using your operating system’s file browser go to the app\src\main folder of the project and add a folder called assets. Add your sound file to this folder. Now we are ready to write the sound FX code.
The sound code
We will split up the sound code into chunks and explain it as we go. Here are the first three lines of our sound code. First, we declare a int variable called soundID. This int will hold a reference number that Android will use to identify the sound effect we want to play. For now, we initialize it to -1. Nex,t we create a new SoundPool object. And on the third new line of code, we initialize it. The parameters used to initialize it are as follows.
1 is the number of simultaneous sound FX we might want to play.
AudioManager.STREAM_MUSIC is the type of FX we specify for our specific sound.
is the quality.
Enter the sound FX code below the comment that indicates where it should go.
1 2 3 4 5 6 |
SoundPool soundPool; int soundID = -1; // This SoundPool is deprecated but don't worry soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC,0); |
If you are wondering why there is an unusual-looking strikethrough on the word
SoundPool in the line where we initialize the object; it is because the class is deprecated. What this means is that there is a newer, slightly more correct way to initialize the
SoundPool object. We do it this way because the newer code only works on the very latest devices and yet this older code works on every Android device ever made. It is true that one day this code will stop working but that is not likely to happen for some years.
Next, we load the sound file into a format that is ready to play. We do this in a few steps. First, we create two objects an AssetManager and an AssetFileDescriptor. These two objects do all the complex work that is required behind the scenes.
Next, we see something new. We have a try/ catch block. The design of the AssetManager class means we must wrap our code in a try/ catch block. The code in the try part is what we want to do, the code in the catch part is what to do if it fails. We can think of this as a kind of mandatory if/ else block. In the try part, we initialize descriptor using the openFd method and passing in the name of our sound file. Then we use the load method to load the contents of descriptor (our sound FX) into memory.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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("warp.ogg"); soundID = soundPool.load(descriptor, 0); }catch(IOException e){ // Print an error message to the console Log.e("error", "failed to load sound files"); } |
Finally, we can play a sound using the play method like this. The parameters are as follows from left to right: Our soundID ID, left volume, right volume, priority (in case we are playing more sounds than we said when we initialized soundPool to play), the number of loops to play, and the rate or frequency. Enter the last line of code.
1 |
soundPool.play(soundID, 1, 1, 0, 0, 1); |
You can now run the app to hear the sound being played.
As we saw the vast majority of the code we wrote was setting up the sound ready to be played. In a real game, we would load all the sound FX that we need, each with a different id like, explodeID, shootID etc at the start of the game. Then whenever an appropriate event takes place in the game we will simply use the play method with the appropriate id.
SoundFXDemoActivity code listing
Here is the complete code listing for your convenience.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.gamecodeschool.soundfxdemo; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; import android.util.Log; import android.app.Activity; import java.io.IOException; public class SoundFXDemoActivity extends Activity { // This is the entry point to our game @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SoundPool soundPool; int soundID = -1; // This SoundPool is deprecated but don't worry soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC,0); 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("warp.ogg"); soundID = soundPool.load(descriptor, 0); }catch(IOException e){ // Print an error message to the console Log.e("error", "failed to load sound files"); } soundPool.play(soundID, 1, 1, 0, 0, 1); } } |
I’m loving this series – it’s been a real eye opener. Today I’ve come across my first problem – I can’t get the sound to play – I get an error message:
11-02 20:33:14.455 4590-4590/com.example.owner.soundfxdemo W/SoundPool: sample 1 not READY
Any thoughts?
Hi Roger,
A few possible reasons. The sound file is misspelt(in code or actual filename), is in the wrong folder or Android simply doesn’t load it fast enough.
To explain the last of these possibilities a bit better: Typically we wouldn’t load and play a sound file in the same method because the loading takes place in a thread and if it doesn’t happen quick enough the call to play might occur before it is loaded, hence the error.
So if your device or the emulator is running slow this problem might be terminal. So try a different device or try the “breakout (arkanoid)” demo http://gamecodeschool.com/android/coding-a-breakout-game-for-android/ after the “simple game engine” demo which plays sounds on specific events long after the sound file is guaranteed to be loaded.
Good luck and thanks for the comment
Thanks for the advice – it may well be that it’s loading too slowly – I have Windows 7 so cannot use the intel x86 aceelerator, so my virtual phone is running on the arm cpu. I’ll give the other game a try – thanks for the support!
Yeah I had the same problem as you. Adding this line before the soundPool.play(…); statement at the end did the trick: android.os.SystemClock.sleep(1000);
Thanks for the tutorials John!
Hi Josh,
Glad you got it working. Even better solution for long sound files (perhaps music) is to use a class that streams the sound rather than preloading completely. Have a look at this http://stackoverflow.com/questions/16336960/android-playing-music-in-background.
The only thing I can’t figure out is why my sound file is cut off after about 4 seconds. The file I used is 33 sec long.
It didn’t work as is. Many experiments showed that sound wasn’t loaded that fast.
I made sound play start on Button tap — now it plays (after a second past from activity start).
So solution is to initiate soundPool and load sound in onCreate, but play is after a few seconds.
Hello John,
i was following your tutorial and i did everything step by step but the sound never play
what am i doing wrong??
Thanks Josh ,
android.os.SystemClock.sleep(1000);
This line of code solved the problem .
Thanks for solving this Josh.