Because we all know that the “sound” library in processing for android garbles and distorts your sounds after a while,
and that the “cassette” library stops working after a few playbacks,
the only viable option is the native android MediaPlayer.
Especially when using APDE.
Because there we have to address different loading methods of the audio files for the preview or if it’s an apk…
So here is a MediaPlayer example that works in APDE and its preview.
Wrapped in convenient helper functions that work as drop in modules to save some headache.
If anyone wants to maybe make a class for even more convenience, be my guest.
I also added a extensive amount of comments for people who are new to this.
Here ya go:
// Necessary imports
import android.media.MediaPlayer;
import android.media.AudioAttributes;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer.OnCompletionListener;
import android.content.Context;
import android.app.Activity;
// The needed initializers
Context context;
Activity act;
// Only needed when direct access is wished
MediaPlayer bgm,sound = null;
// Make sure these or other files are in the data folder
String bgmMusic = "menu_music.mp3";
String sfxSound = "correct.mp3";
void setup()
{
// Need to assign these
act = this.getActivity();
context = act.getApplicationContext();
}
void draw ()
{
// Not really necessary in this example...
}
// Cleanup any declared MediaPlayers when
// closing the app, if there are.
public void stop()
{
super.stop();
// We have those in this example
bgm.release();
sound.release();
}
// We use mousePressed for demonstration
// as this on almost all devices available.
void mousePressed()
{
//- Use this for fire&forget playback
//- Best for small files to avoid long loading
playSfx(sfxSound);
//- For one-shoot sounds that stay around
//- to be played again at a later time.
// sound = playSound(sfxSound);
//- Or just load the file without playing.
// sound = loadSound(sfxSound);
//- Use this for playing (looping) music
// bgm = playLoop(bgmMusic);
//- Or just load the music file and play later
// bgm = loadLoop(bgmMusic);
//- To access its methods, assign it to a variable
//- we can also assign a new file while playing.
if (bgm == null)
{
// Load and play the file
bgm = playLoop(bgmMusic);
}
else
{
// It's loaded, so pause or play(start)...
// we can check if it's playing after the
// file was loaded, and everything else
// that you can do with the MediaPlayer
// But don't use stop! except you know what you're doing...
if (bgm.isPlaying())
{
bgm.pause();
} else
{
// I know that using start instead of play
// is confusing, but i didn't make the MediaPlayer...
bgm.start();
}
}
}
// Here the actual helper/wrapper functions
// that encapsulate the MediaPlayer creation stuff.
MediaPlayer playSfx(String file)
{
// This exists for convenience only
// and should be removed for production.
// This lets us set if we use the preview
// of APDE or if this is an apk for installation testing
Boolean isApk = false; //set false for preview and true for akp.
// First create a new MediaPlayer
MediaPlayer mp = new MediaPlayer();
// Set looping false for one-shot
mp.setLooping(false);
// The listener is only needed to release
// fire&forget sounds from memory.
// For looping or play/pause this has
// to be removed completely, otherwise
// we can't play the sound again.
// because with it the MediaPlayer goes
// into stop() mode which is basically a reset()
// and we would need to assign a datasource again
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
{
// Yes it has to be the com
@Override
public void onCompletion(MediaPlayer mp)
{
// Cleanup
mp.release();
mp = null;
}
}
);
// Now we try to assign the file to the
// datasource of the MediaPlayer
// It has to be a try because the
// MediaPlayer wants it so.
try
{
// Must first set the Attributes before loading.
// We don't explicitly need to set them, but better coding and so...
mp.setAudioAttributes( new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
// We check if its an apk or not
// BECAUSE the preview and apk
// load the file differently!
if (!isApk)
{
// For preview use this and simply
// set the datasource to our preview folder
// followed by the filename.
mp.setDataSource(sketchPath + "/" + file);
}
else
{
// An APK needs to load the file like this
// First create a new AssetFileDescriptor
// array. Somehow needs to be an array..whatever..
AssetFileDescriptor[] fd = new AssetFileDescriptor[1];
// Now we load the file and its infos
// into the descriptor
fd[0] = context.getAssets().openFd(file);
// And now we load that into the datasource.
mp.setDataSource(fd[0].getFileDescriptor(), fd[0].getStartOffset(), fd[0].getLength());
}
// For music files we don't use prepareAsync
mp.prepare();
// Now we can play the sound if we want
mp.start();
}
catch(IOException e) {
// For debug messages in case of an error.
// Has to be IOException for the MediaPlayer
println(e);
}
// Finally we return the MediaPlayer to a
// variable or nothingness for fire&forget...
return mp;
}
// The following functions are just for pure convenience...
// I know we could use a different creator method where
// we can pre-define its state but that's for you to do.
// Here's just bare metal examples...
MediaPlayer playSound(String file)
{
Boolean isApk = false; //set false for preview
MediaPlayer mp = new MediaPlayer();
mp.setLooping(false);
try
{
mp.setAudioAttributes( new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
if (!isApk)
{
mp.setDataSource(sketchPath +"/"+file);
}
else
{
AssetFileDescriptor[] fd = new AssetFileDescriptor[1];
fd[0] = context.getAssets().openFd(file);
mp.setDataSource(fd[0].getFileDescriptor(), fd[0].getStartOffset(), fd[0].getLength());
}
mp.prepare();
mp.start();
}
catch(IOException e) {
println(e);
}
return mp;
}
MediaPlayer playLoop(String file)
{
Boolean isApk = false; //set false for preview
MediaPlayer mp = new MediaPlayer();
mp.setLooping(true);
try
{
mp.setAudioAttributes( new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
if (!isApk)
{
mp.setDataSource(sketchPath +"/"+file);
}
else
{
AssetFileDescriptor[] fd = new AssetFileDescriptor[1];
fd[0] = context.getAssets().openFd(file);
mp.setDataSource(fd[0].getFileDescriptor(), fd[0].getStartOffset(), fd[0].getLength());
}
mp.prepare();
mp.start();
}
catch(IOException e) {
println(e);
}
return mp;
}
MediaPlayer loadSound(String file)
{
Boolean isApk = false; //set false for preview
MediaPlayer mp = new MediaPlayer();
mp.setLooping(false);
try
{
mp.setAudioAttributes( new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
if (!isApk)
{
mp.setDataSource(sketchPath +"/"+file);
}
else
{
AssetFileDescriptor[] fd = new AssetFileDescriptor[1];
fd[0] = context.getAssets().openFd(file);
mp.setDataSource(fd[0].getFileDescriptor(), fd[0].getStartOffset(), fd[0].getLength());
}
mp.prepare();
}
catch(IOException e) {
println(e);
}
return mp;
}
MediaPlayer loadLoop(String file)
{
Boolean isApk = false; //set false for preview
MediaPlayer mp = new MediaPlayer();
mp.setLooping(true);
try
{
mp.setAudioAttributes( new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
if (!isApk)
{
mp.setDataSource(sketchPath +"/"+file);
}
else
{
AssetFileDescriptor[] fd = new AssetFileDescriptor[1];
fd[0] = context.getAssets().openFd(file);
mp.setDataSource(fd[0].getFileDescriptor(), fd[0].getStartOffset(), fd[0].getLength());
}
mp.prepare();
}
catch(IOException e) {
println(e);
}
return mp;
}