Welcome to “Android Evolution”

preface

Android SDK provides 3 sets of audio playback API, respectively: MediaPlayer, SoundPool, AudioTrack, this article focuses on SoundPool.

Like MediaPlayer, SoundPool can also be used to play audio files. However, unlike MediaPlayer, SoundPool is better suited to fast sound effects rather than longer audio files that need to be streamed.

See SoundPool’s official documentation for the following key features:

1. Compare MediaPlayer with low latency;

The SoundPool library uses the MediaPlayer service to decode the audio to the original 16-bit PCM mono or stereo stream. This allows the application to ship the compressed stream without the CPU load and the delay of decompression during playback.

2, can support multiple audio playback at the same time;

When constructing a SoundPool object, the maxStreams parameter sets the maximum number of streams that a single SoundPool can play at one time. SoundPool tracks the number of active streams. If the maximum number of streams is exceeded, SoundPool will automatically stop previously played streams. Limiting the maximum number of streams helps limit CPU loading and reduces the possibility of audio remixes, based on priority first

3, can support infinite loop playback;

You can loop the sound by setting a non-zero loop value. A value of -1 causes the sound to loop forever. In this case, the application must stop the sound by calling the stop() function. Any other non-zero value will cause the sound to repeat the specified number of times, for example, a value of 3 will cause the sound to be played a total of 4 times.

4, suitable for short audio playback (such as game scene prompt sound);

The load logic iterates through the list of sounds and calls the corresponding soundPool.load () function. This should usually be done early in the process to allow time to decompress the audio into the original PCM format before it needs to be played back.

If you haveSVGAPlayer, to understand the implementation of its internal playback animation prompt sound is the use of SoundPool, interested can see, here but more about.

1. Use of SoundPool

1.1. Prepare audio resources

Place the prepared audio files in the Assets folder or the RAW folder under RES:

  • Under Assets, you can create a new folder for batch loading, while raw can only store a single load at the same level.
  • Bugs may exist when a single asset file exceeds 1 MB. However, bugs do not exist in the raw resource directory.
  • SoundPool audio file size can not be more than 1M and time longer than 5-6 seconds may cause errors.
1.2 SoundPool construction method

SoundPool(int maxStreams, int streamType, int srcQuality)

  • The maxStreams parameter specifies the maximum number of sounds that can be supported simultaneously in a SoundPool object. If this value is too large, an error will be reported: -12, no sound can be heard, can be set according to requirements
  • The streamType argument specifies the sound type. You can define STREAM_VOICE_CALL, STREAM_SYSTEM, STREAM_RING,STREAM_MUSIC, and STREAM_ALARM in AudioManager.
  • The srcQuality parameter specifies the sound quality (sampling rate conversion quality), usually set to 0!

Application mode:

private static final int MAX_SOUNDS = 1; private SoundPool soundPool; SoundPool = new soundPool (MAX_SOUNDS, Audiomanager.stream_music, 0);Copy the code

But the above constructor is deprecated in API 21. Create SoundPool object instance with soundpool. Builder after API 21:

private static final int MAX_SOUNDS = 1; private SoundPool soundPool; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { soundPool = new SoundPool.Builder().setMaxStreams(MAX_SOUNDS).build(); } else {soundPool = new soundPool (MAX_SOUNDS, Audiomanager.stream_music, 0); }Copy the code

1.3. Load audio Resources

int load(Context context, int resId, int priority)

Load the sound from the specified APK resource.

Load from APK resources,resId: such as audio file R.raw.xxx

int soundID = soundPool.load(appContext, R.raw.tip, 1);
Copy the code
int load(String path, int priority)

Load the sound from the specified path. Load from the audio file path

int load(AssetFileDescriptor afd, int priority)

Load the sound from an asset file descriptor.

Load from the asset file

 AssetFileDescriptor fileDescriptor = assetManager.openFd("sounds/tip.mp3");
 int soundId = soundPool.load(fileDescriptor, 1);
Copy the code
int load(FileDescriptor fd, long offset, long length, int priority)

Load the sound from a FileDescriptor.

For example, an audio FileDescriptor stored on an SD card is loaded from a file

1.4. Play audio resources

play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)

Usage:

Int streamId = soundPool. Play (soundID, // Sounid 1, // Left channel: 0.0f ~ 1.0f 1, // Right channel: 0.0f ~ 1.0f 1, // Playback priority: 0 indicates the lowest priority repeatTime, // cycle mode: 0 indicates the cycle once, -1 indicates the cycle all the time, and other numbers +1 indicates the cycle times corresponding to the current number 1); // Playback speed: 1 is normal and ranges from 0 to 2Copy the code
Several important methods about playback control
// suspend specified audio playback by streamID final void pause(int streamID) // resume specified audio playback final void resume(int streamID) // stop specified audio playback final void Stop (int streamID) // Unlocks specified audio final Boolean unload(int soundID) // Pauses all audio playback final void autoPause() // Resumes all paused audio playback final Void autoResum() final void setLoop(int streamID, int loop) void setLoop(int streamID, int loop) After the completion of the play) void setOnLoadCompleteListener (SoundPool OnLoadCompleteListener listener) / / set the priority (play number more than the maximum value at the same time, Final void setPriority(int streamID, int priority) // Set the playback rate of the specified audio, from 0.5 to 2.0(rate>1: speeds up playback, Void setRate(int streamID, float rate) // Release all resourcesCopy the code

SoundPool package

SoundPool encapsulation
  • To make our code clearer, we’ll create a class that loads and plays our sound outside of the Activity/Fragment.
  • First, we create a class called SoundPlayer. We need some way to hide the logic of accessing and loading sound files from external classes.

This is the implementation of SoundPlayer. You can change/add some code as needed, but that’s the basic point.

The complete code is as follows:

import android.content.Context; import android.media.AudioManager; import android.media.SoundPool; import android.os.Build; import android.util.Log; import java.util.HashMap; * @author: heiyulong * @date: 2021/4/16 */ public class SoundPlayer { private static final String TAG = "SoundPlayer"; private static final int MAX_SOUNDS = 3; private Context appContext; private SoundPool soundPool; private HashMap<Integer,Integer> soundMap = new HashMap<>(); Public SoundPlayer(Context appContext) {// version compatible this.appContext = appContext; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { soundPool = new SoundPool.Builder().setMaxStreams(MAX_SOUNDS).build(); } else {soundPool = new soundPool (MAX_SOUNDS, Audiomanager.stream_music, 0); @param resId r.raw. XXX @param repeatTime: 0 means loop once, -1 means loop all the time, Public void play(int resId, int repeatTime) {int soundID = soundPool. Load (appContext, resId, 1); / / the method to prevent the sample not ready error soundPool. SetOnLoadCompleteListener ((soundPool sampleId, Status) -> {int streamId = soundPool. Play (soundID, // Sounid 1, // Left channel: 0.0f ~ 1.0f 1, // right channel: 0.0f ~ 1.0f 1, // Playback priority: 0 indicates the lowest priority repeatTime, // cycle mode: 0 indicates the cycle once, -1 indicates the cycle all the time, and other numbers +1 indicates the cycle times corresponding to the current number 1); Soundmap. put(resId,streamId); // Playback speed: 1 is normal and ranges from 0 to 2 soundmap. put(resId,streamId); }); Public void play(int resId) {int soundID = soundPool. Load (appContext); resId, 1); / / the method to prevent the sample not ready error soundPool. SetOnLoadCompleteListener ((soundPool sampleId, Status) -> {int streamId = soundPool. Play (soundID, // Sounid 1, // Left channel: 0.0f ~ 1.0f 1, // right channel: 0.0f ~ 1.0f 1, // Playback priority: 0 indicates the lowest priority 0, // Cycle mode: 0 indicates the cycle once, -1 indicates the cycle all the time, and other numbers +1 indicates the cycle times corresponding to the current number 1); Soundmap. put(resId,streamId); // Playback speed: 1 is normal and ranges from 0 to 2 soundmap. put(resId,streamId); }); } @param resId public void pause(int resId) {if (soundPool! = null) { Integer mStreamID = soundMap.get(resId); if(mStreamID ! = null){ soundPool.pause(mStreamID); @param resId public void resume(int resId) {if (soundPool! = null) { Integer mStreamID = soundMap.get(resId); if(mStreamID ! = null){ soundPool.resume(mStreamID); Public void stop(int resId) {if (soundPool! = null) { Integer mStreamID = soundMap.get(resId); if(mStreamID ! = null){ soundPool.stop(mStreamID); Public void release() {log.d (TAG, "Cleaning resources.." ); if (soundPool ! = null) { soundPool.autoPause(); soundPool.release(); }}}Copy the code

The core components of this class include:

  • The constructor used to build the SoodPool
  • SoundPool is used to play our sound files.
  • SoundPool is used to control the pause & resource release method
Note:
  1. You’ll also notice the constant :MAX_SOUNDS. It specifies the maximum number of sounds our SoundPool allows to play simultaneously.
  2. SoundPool will be constructed once our class is instantiated. Play the specified audio resource via plyaer(int resId).
  3. Remember to release the resource Release () when the page is destroyed.

Welcome to “Android Evolution”