I have a .wav file that I'd like to use across my game, currently I am loading the sound in onCreate() of each activity in the game.
soundCount = soundpool.load(this,R.raw.count, 1);
The sound will be played once the activity starts.
soundpool.play(soundCount, 0.9f, 0.9f, 1, -1, 1f);
Problem is at times I will hit the error "sample x not ready".
Is it possible to load the .wav file once upon starting the game and keep it in memory and use it later across the game? Or is it possible to wait for 1-2 seconds for the sound to load finish?
You'll need to wait for it to finish by adding a listener via SoundPool.setOnLoadCompleteListener.
Since my project is compatible with Android 1.5 and I couldn't use setOnLoadCompleteListener, I resolved making the play of sound delayed.
My source code follows:
playSound_Delayed(soundId, 100);
// (..)
private void playSound_Delayed (final int soundId, final long millisec) {
// TIMER
final Handler mHandler = new Handler();
final Runnable mDelayedTimeTask = new Runnable() {
int counter = 0;
public void run() {
counter++;
if (counter == 1) {
boolean ret = mHandler.postDelayed(this, millisec);
if (ret==false) Log.w("playSound_Delayed", "mHandler.postAtTime FAILED!");
} else {
playSound(soundId);
}
}
};
mDelayedTimeTask.run();
}
you should use setOnLoadCompleteListener if possible ... if not, wrap a while loop around your call to 'play'. something like:
int waitLimit = 1000;
int waitCounter = 0;
int throttle = 10;
while(soundPool.play(soundId, 1.f, 1.f, 1, 0, 1.f) == 0 && waitCounter < waitLimit)
{waitCounter++; SystemClock.sleep(throttle);}
this will retry 'play' 1000 times on a 10ms interval. this should be run on a non-ui thread of course, and is still not ideal. but maybe a little stronger than waiting an arbitrary time and expecting the pool to be ready.
The SoundPool library uses the MediaPlayer service to decode the audio into a raw 16-bit PCM mono or stereo stream which we simply can call preparation of Stream, that takes some times depending on size of sound file. And if we try to play the sound before that process is over, we get "sample x not ready" error.
There are two solutions for this
implement setOnLoadCompleteListener or
wait arbitrary amount of time so that preparation is over
and then play it
Note that there is no exception for this condition or application is not crashing without any try-catch
private SoundPool soundPool;
private int my_sound;
boolean loaded = false;
// In the constructor
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
my_sound = soundPool.load(this, R.raw.blast_sound, 1);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
public void onLoadComplete(SoundPool soundPool, int sampleId,int status) {
loaded = true;
}
});
// then where ever you want to play the sound, type
if (loaded) {
soundPool.play(my_sound, 0.9f, 0.9f, 1, 0, 1f);
}
I solved with problem with simple do-while cicle. The method play() return non-zero streamID if successful, zero if failed. So, it's sufficient to check the return value.
int streamID = -1;
do {
streamID = soundPool.play(soundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
} while(streamID==0);
This would work for you definitely !
public void playWavFile() {
Thread streamThread = new Thread(new Runnable() {
#Override
public void run() {
SoundPool soundPool;
final int wav;
String path = "/mnt/sdcard/AudioRecorder/record.wav";
soundPool = new SoundPool(5,AudioManager.STREAM_MUSIC, 0);
wav = soundPool.load(path, 1);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
// TODO Auto-generated method stub
soundPool.play(wav,100, 100, 0, 0, 1f);
}
});
}
});
streamThread.start();
}
This sounds crazy, but don't play sounds right away. Give your app a couple of seconds to initialize the SoundPool. In my app, this was exactly the issue; I added a fake "loading" screen of ~3 seconds, and then everything worked. (I didn't even need to preload the sounds.)
Related
I want to play a generated sound that is shorter than 1 second. However, the minBufferSize of the AudioTrack always seems to be 1 second or longer. On some devices I can set the bufferSize smaller than the value evaluated with AudioTrack.getMinBufferSize, however this is not possible on all devices. I'd like to know wether it's possible to generate a shorter sound for the AudioTrack. I'm currently using this code (it contains some smoothing, because I'm getting constantly new frequences):
int buffSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, buffSize,
AudioTrack.MODE_STREAM);
short samples[] = new short[buffSize];
int amp = 10000;
double twopi = 8. * Math.atan(1.);
double phase = 0.0;
audioTrack.play();
double currentFrequency = getFrequency();
double smoothing = 300;
double deltaTime = buffSize / 500;
while (playing && PreferenceManager.getDefaultSharedPreferences(
MainActivity.this).getBoolean("effect_switch", true))
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
currentFrequency += deltaTime * (newFrequency - currentFrequency) / smoothing;
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * currentFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
audioTrack.stop();
audioTrack.release();
In fact, I want the sounds to be updated more frequently, which is the reason for me needing shorter samples.
I think I have a solution for you. Since my min buffer seems to be much smaller than 1 sec, I simulated your problem by loading a buffer with 5 sec of data but only play 0.5 sec of it immediately followed by another frequency. This tone I also created 5 sec of data but only played 0.5 sec & repeated this for several tones. It all works for me.
Also, since I jammed this into a current project I'm working on, it's difficult for me to just cut and paste my code. While I've tested my solution, what I've posted here is not tested exactly as written. Some of it is cut & paste, some pseudocode.
The key feature is using the OnPlaybackPositionUpdateListener.
private AudioTrack.OnPlaybackPositionUpdateListener audioTrackListener = new AudioTrack.OnPlaybackPositionUpdateListener() {
#Override
public void onMarkerReached(AudioTrack audioTrack) {
int marker = audioTrack.getNotificationMarkerPosition();
// I just used 8 tones of 0.5 sec each to determine when to stop but you could make
// the condition based on a button click or whatever is best for you
if(marker < MAX_FRAME_POSITION) {
audioTrack.pause();
newSamples();
audioTrack.play();
} else {
audioTrack.stop();
}
audioTrack.setNotificationMarkerPosition(marker + FRAME_MARKER);
Log.d(TAG, "MarkerReached");
}
#Override
public void onPeriodicNotification(AudioTrack audioTrack) {
int position = audioTrack.getPlaybackHeadPosition();
if(position < MAX_FRAME_POSITION) {
audioTrack.pause();
newSamples();
audioTrack.play();
} else {
audioTrack.stop();
}
Log.d(TAG, "PeriodNotification");
}
};
Then
audioTrack.setPlaybackPositionUpdateListener(AudioTrackListener);
I used the marker (which has to be re-initialized repeatedly) for my tests...
audioTrack.setNotificationMarkerPosition(MARKER_FRAMES);
but you should be able to use the periodic notification too.
audioTrack.setPositionNotificationPeriod(PERIODIC_FRAMES);
And the newSamples() method called from the listener
public void newSamples() {
/*
* generate buffer, I'm doing similar to you, without the smoothing
*/
// AudioTrack write is a blocking operation so I've moved it off to it's own Thread.
// Could also be done with an AsyncTask.
Thread thread = new Thread(writeSamples);
thread.start();
}
private Runnable writeSamples = new Runnable() {
#Override
public void run() {
audioTrack.write(samples, 0, buffSize);
}
};
I use Soundpool in my app, so far it works good, but I do have a wav file which is 10 secs. Unfortunately, soundpool plays only the first 5 secs. How to make soundpool to play the whole track? I have converted wav to -- ogg and mp3 still the same issue. It plays only the first 5 secs. Any help would be much appreciated.
//set up audio player
mSoundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0);
//load fx
mSoundPoolMap.put(RAW_1_1, mSoundPool.load(this, R.raw.loop1, 1));
//playing soundpool
case R.id.button1:
mSoundPool.stop(mStream1);
mStream1= mSoundPool.play(mSoundPoolMap.get(RAW_1_1), streamVolume, streamVolume, 1, LOOP_1_TIME, 1f);
UPD Last: Maybe someone will find it here and read it. Seems soundpool cant play more then 5 secs. It is his maximum, for more longer sounds use MediaPlayer. I hope you will not spend so much of your time like i did)
So I think you reached the 1M limit in SoundPool.
SoundPool is hard code the buffer size as 1M, for all loaded file, store in pcm format.
So it do not care of ogg or wav.
We have solved similar problem by decreasing in our *.ogg effects sample rate. Our initial sample rate was 44 kHz ~ and only 10 sec of sound played, decreasing to 16 kHz increase playability to 30 seconds
Solution was found in this discussion
SoundPool designed to play short sound effects. To play music (big audio files) you need to use MediaPlayer
// R.raw.audio_file - res/raw/audio_file.mp3
mediaPlayer = MediaPlayer.create(this, R.raw.audio_file);
mediaPlayer.start();
This activity is works for me
public class MainActivity extends Activity {
private Integer soundID;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Load the sound
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
float actualVolume = (float) audioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
float maxVolume = (float) audioManager
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
final float volume = actualVolume / maxVolume;
SoundPool soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
soundPool.play(soundID, volume, volume, 1, 0, 1f);
try {
Thread.sleep(5000); // play twice
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
soundPool.play(soundID, volume, volume, 1, 0, 1f);
}
});
}
});
soundID = soundPool.load(this, R.raw.sound2, 1);
//load fx
//playing soundpool
}
}
You must load you sound asynchrony and check audio cache - it can be overflow.
Read this article
http://www.google.by/url?http://www.vogella.com/articles/AndroidMedia/article.html
Its very heedful
Your file is probably too large. Try to change from stereo to mono, if you do not need stereo anyway.
Or try to downsample your file.
Reducing the filesice worked in my case.
Now I can play a music in android. But I want to play this sound in a random amount of time between 2 to 8 seconds.How Can I randomly play it that for example the first time, it plays for 2 seconds, the next time 7 sec and so on? Can anybody help me?
Go through these links:
Random number Generation
Media Player
Timer
you will get an idea.
Sound in android is played like this :
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int mSoundID = mSoundPool.load(this, R.raw.sound1, 1);
float lActualVolume = (float) audioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
float lMaxVolume = (float) audioManager
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float lVolume = lActualVolume / lMaxVolume;
// Is the sound loaded already?
if (mSoundIsLoaded) {
mSoundPool.play(mSoundID, lVolume, lVolume, 1, 0, 1f);
I think you have been given plenty of help to figure the random number part out.
You will have to put the sound file in your assets/raw directory.
edit:
I forgot to mention where the mSoundIsLoaded parameter came from.
I set it when my sound has been loaded. I do this in my onCreate method. when the sound is loaded I set the boolean field called mSoundIsLoaded. I do this to prevent NullPointerExceptions when playing the sound
the loading of the sound looks like this:
mSoundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
mSoundIsLoaded = true;
}
});
mSoundID = mSoundPool.load(this, R.raw.sound1, 1);
I'm using a SoundPool to play sounds for my Android app. All of the sounds are loaded up at the beginning of the app to prevent any lagging during the app itself. All sounds play fine as long as they are not set to looping. When i set looping to be true (set as -1) then the sound does not play.
There are 6 different sounds that are stopping and starting depending on the user's input. It plays the first sound fine but then sounds afterwards fail to play.
I have tried various ways around it, such as pausing rather than stopping, setting volume to 0 and loops to 0 rather than pausing, setting loops to an arbitrary large-ish number rather than true repeating, making the sounds much shorter and none of these have worked.
My code is as follows:
public int loadSound(int soundFile)
{
int soundID;
try
{
if (m_loadedSounds.containsKey(soundFile))
{
soundID = m_loadedSounds.get(soundFile);
}
else
{
soundID = m_soundPool.load(m_context, soundFile, 1);
m_loadedSounds.put(soundFile, soundID);
}
}
catch (Exception e)
{
soundID = -1;
}
return soundID;
}
public void playSound(int soundFile, boolean loop)
{
// Grab it from the map
int soundID = loadSound(soundFile);
int loops = loop ? -1 : 0;
float streamVolume = m_audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
streamVolume /= m_audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
// If succeeded then play the sound
if (soundID != -1)
{
if (m_soundStreams.containsKey(soundID))
{
int streamID = m_soundStreams.get(soundID);
m_soundPool.setLoop(streamID, loops);
m_soundPool.resume(streamID);
}
else
{
int streamID = m_soundPool.play(soundID, streamVolume, streamVolume, 1, loops, 1.0f);
Log.d("SOUNDS", "Sound played! ID: " + soundID + " At volume: " + streamVolume + " Looping: " + (loop ? "Yes": "No") + " Success: " + (streamID != 0 ? "Yes" : "No"));
if (streamID != 0)
{
m_soundStreams.put(soundID, streamID);
}
}
}
}
public void stopSound(int soundFile)
{
int soundID = loadSound(soundFile);
Integer streamID = m_soundStreams.get(soundID);
if (streamID != null)
{
m_soundPool.pause(streamID);
}
}
And the error given in LogCat at run time is:
01-03 16:03:20.142: ERROR/AudioFlinger(2359): not enough memory for AudioTrack size=670096
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): AudioTrack (0x389a0, size=1048576)
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): 0: 0005b090 | 0x00000000 | 0x00010080 | F
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): 1: 0006db58 | 0x00010080 | 0x0007B8A0 | A
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): 2: 0005af70 | 0x0008B920 | 0x0005C280 | A
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): 3: 000752c0 | 0x000E7BA0 | 0x00018460 | F
01-03 16:03:20.142: DEBUG/MemoryDealer(2359): size allocated: 883488 (862 KB)
01-03 16:03:20.142: ERROR/AudioTrack(11584): AudioFlinger could not create track, status: -12
01-03 16:03:20.142: ERROR/SoundPool(11584): Error creating AudioTrack
Does anybody know of any solutions to this annoying problem, or any workarounds that I haven't tried?
The best solution I could come up with is to put a special case in for the sounds that are doing this and use a MediaPlayer for these sounds. Then I have to stop & release them when they're not used and then reload them when I want to play them again. Has a noticeable, yet slight, loading time when the sounds change but it's the best I can do. Any other answers would still be much appreciated.
Make sure you are releasing your SoundPool resources in onPause() to prevent leaking memory. You should be loading your sounds in onResume() also.
I had the same problem. When you are begining to play the sound you want to be looped, set priority of sound to 99 and lower the rest. The heigher value the heigher priority. However I have a big problem of latency when playing sounds. If you have any solution please notify me.(I'm using soundpool too and int array as ID pool.)
Which I mean:
int streamID = m_soundPool.play(soundID, streamVolume, streamVolume, 99, loops, 1.0f);
SoundPool soundPool;
int soundID;
boolean plays = false, loaded = false;
float actVolume, maxVolume, volume;
AudioManager audioManager;
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
actVolume = (float) audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
maxVolume = (float) audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
volume = actVolume / maxVolume;
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
}
});
soundID = soundPool.load(this, R.raw.audiofile, 1);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
soundPool.play(soundID, volume, volume, 1, -1, 1f);
}
},1000);
Try this solution. its should work.
I'm using MediaPlayer to play some sounds files, which at times overlap. I notice that in the LogCat window I keep getting this message:
android max instances of component OMX.TI.ACC. Decode already created.
It seems to have no effect on my application as the sounds continue to play just fine. Does anyone know what this message means, and do I need to worry about it?
SoundPool may be a better option for playing multiple, short sounds.
Creating SoundPool
public static final int SOUND_1 = 1;
public static final int SOUND_2 = 2;
SoundPool mSoundPool;
HashMap<Integer, Integer> mHashMap;
#Override
public void onCreate(Bundle savedInstanceState){
mSoundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
mSoundMap = new HashMap<Integer, Integer>();
if(mSoundPool != null){
mSoundMap.put(SOUND_1, mSoundPool.load(this, R.raw.sound1, 1));
mSoundMap.put(SOUND_2, mSoundPool.load(this, R.raw.sound2, 1));
}
}
Then play a sound by calling a custom function.
Playing Sound
/*
*Call this function from code with the sound you want e.g. playSound(SOUND_1);
*/
public void playSound(int sound) {
AudioManager mgr = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = streamVolumeCurrent / streamVolumeMax;
if(mSoundPool != null){
mSoundPool.play(mSoundMap.get(sound), volume, volume, 1, 0, 1.0f);
}
}