SoundPool sounds stuttering on KitKat - android

In a Xamarin Android (not Xamarin Forms) application on KitKat (API 19), using Visual Studio 2015 Update 3, I'm having trouble with playback of sounds from a SoundPool. Sometimes my sound plays fine. Other times it stutters.
Weirdest of all, on rare occasion, playback fails completely with a warning in the debug log like:
W/SoundPool(30751): sample 2 not READY
This is despite the fact that I have absolutely positively waited for the sample to be loaded before trying to play it, and despite the same sample ID playing just fine immediately before and after the "sample X not READY" failure.
The TL;dr version of the code:
var Pool = new SoundPool(6, Stream.Music, 0);
var ToneID = await Pool.LoadAsync(Android.App.Application.Context, Resource.Raw.tone, 1);
beepButton.Click += (sender, e) => Pool.Play(ToneID, 1.0F, 1.0F, 1, 0, 1.0F);
The Resource.Raw.tone is little-endian 16-bit PCM mono audio with a sample rate of 44100Hz and a Microsoft "WAV" header. It is a 0.25s 440Hz sine wave created using Audacity 2.1.3.
I know all about SoundPool.Builder. It was introduced in API 21. I'm targeting API 19.
The actual code disables the button for 500ms after each press, so there's no chance I'm trying to play the sample more than once concurrently. Indeed, I can reproduce the problem with individual presses some tens of seconds apart.
Things I have tried without success:
using smaller or larger maxStreams (first arg) values when calling the SoundPool ctor
using different Android.Media.Stream enumeration values for streamType (second arg) when calling the SoundPool ctor
using different srcQuality (third arg) values when calling the SoundPool ctor (despite the Android docco saying it does nothing and to always use zero)
using different priority (third arg) values when calling the SoundPool.Load() instance method
using sub-unity volume arguments (like 0.99F) when calling the SoundPool.Play() instance method
using different priority (fourth arg) values when calling the SoundPool.Play() instance mehtod
using different file formats (MP3, OggVorbis) for my sound resource
Here is a .zip file containing my entire ready-to-build application. This is essentially just the template app except for the MainActivity.cs.
What the app should do: beep each time you press the (one and only) button on the UI. It will probably do just that the first dozen presses or so. But then you'll hear "BaBeep" or "BeeeeBip" or "Bee<pause>beep" or silence.
FWIW, the same resource plays back just fine every time using MediaPlayer.
(Edited to link a much-simplified and cleaned-up .zip file example.)

Related

Android: Attaching effect to SoundPool sound?

I have an Android application which uses android.media.SoundPool to play audio cues for the user. It works as expected, but I'd like to be able to add effects to the playback (via android.media.audiofx.AudioEffect subclasses).
I understand how audio effects work on (e.g.) MediaPlayer, but I don't see how to use them with SoundPool. In particular, I'm not sure what to use for the session ID. (I happen to be using Xamarin Android because... reasons. So my example looks weird -- but I'm happy to adapt native Java or Kotlin answers!)
int session = ???; // What goes here?
var fx = new LoudnessEnhancer(session);
fx.SetTargetGain(2400);
fx.SetEnabled(true);
Pool.Play(soundID, 1.0F, 1.0F, 1, 0, 1.00F);
I tried using session ID 0, but (on my WT6000 running Android 7.1.1) it crashes in the LoudnessEnhancer ctor:
Java.Lang.RuntimeException: Cannot initialize effect engine for type: fe3199be-aed0-413f-87bb-11260eb63cf1 Error: -3
(and anyway, my understanding is that applying effects to session ID zero is deprecated).

Why might PlayEarcon error and how can I identify the actual reason?

I am attempting to intersperse sound effects with text in a Xamarin Android project, but I get an opaque error callback.
I first cache the earcon in a path given by
var filename = Path.Combine(Application.Context.CacheDir.ToString(), id + ".wav");
(where id is unique to the earcon; the result on one emulator is /data/data/My.Package/cache/fx1.wav) and then call
var result = _TTS.AddEarcon(id.ToString(), filename);
The result is SUCCESS.
Later I attempt to play it using
var parms = new Android.OS.Bundle();
// https://code.google.com/p/android/issues/detail?id=64925
parms.PutInt(TextToSpeech.Engine.KeyParamStream, 3); // AudioManager.STREAM_MUSIC
parms.PutString(TextToSpeech.Engine.KeyParamUtteranceId, "earcon");
var result = _TTS.PlayEarcon(id.ToString(), QueueMode.Add, parms, "earcon");
The result is a callback to OnError(string) on my registered UtteranceProgressListener.
I'm using API level 21, so I think it should call OnError(string, TextToSpeechError); without the error code, I'm unsure what the problem is.
My best guess was that the audio format was unsupported, but this happens even with a mono 16000 Hz 16-bit stream, which should be supported unconditionally. The file plays back from the cached location via MediaPlayer without problems, and in fact my best workaround is to play a silent utterance of the right length and use the UtteranceProgressListener to trigger a MediaPlayer playing the file.
I observe the same problem on an emulated machine and on a real tablet.
What other causes for the error could there be? Is there any way to tell what the cause is other than guessing and trying a workaround?

AudioFlinger could not create track. status: -12

I am programming for android 2.2 and am trying to using the
SoundPool class to play several sounds simultaneously but at what feel like random times sound will stop coming out of the speakers.
for each sound that would have been played this is printed in the logcat:
AudioFlinger could not create track. status: -12
Error creating AudioTrack
Audio track delete
No exception is thrown and the program continues to execute without any changes except for the lack of volume. I've had a really hard time tracking down what conditions cause the error or recreating it after it happens. I can't find the error in the documentation anywhere and am pretty much at a loss.
Any help would be greatly appreciated!
Edit: I forgot to mention that I am loading mp3 files, not ogg.
i had almost this exact same problem with some sounds i was attempting to load and play recently.
i even broke it down to loading a single mp3 that was causing this error.
one thing i noted: when i loaded with a loop of -1, it would fail with the "status 12" error, but when i loaded it to loop 0 times, it would succeed. even attempting to load 1 time failed.
the final solution was to open the mp3 in an audio editor and re-edit it with slightly lesser quality so that the file is now smaller, and doesn't seem to take up quite as many resources in the system.
finally, there is this discussion that encourages performing a release on the objects you are using, because there is indeed a hard limit on the resources that can be used, and it is system-wide, so if you use several of the resources, other apps will not be able to use them.
https://groups.google.com/forum/#!topic/android-platform/tyITQ09vV3s/discussion%5B1-25%5D
For audio, there's a hard limit of 32 active AudioTrack objects per
device (not per app: you need to share those 32 with rest of the system), and AudioTrack is used internally beneath SoundPool,
ToneGenerator, MediaPlayer, native audio based on OpenSL ES, etc. But
the actual AudioTrack limit is < 32; it depends more on soft factors
such as memory, CPU load, etc. Also note that the limiter in the
Android audio mixer does not currently have dynamic range compression,
so it is possible to clip if you have a large number of active sounds
and they're all loud.
For video players the limit is much much lower due to the intense load
that video puts on the device.
I'll use this as an opportunity to remind media developers: please
remember to call release() for media objects when your app is paused.
This frees up the underlying resources that other apps will need.
Don't rely on the media objects being cleaned up in finalize by the
garbage collector, as that has unpredictable timing.
I had a similar issue where the music tracker within my Android game would drop notes and I got the Audioflinger error (although my status was -22). I got it working however so this might help some people.
The problem occurred when a single sample was being output multiple times simultaneously. So in my case it was a single sample being played on two or more tracks. This seemed to occasionally deadlock or something and one of the two notes would be dropped. The solution was to have two copies of the sample (two actual ogg files - identical but both in the assets). Then on each track even although I was playing the same sample, it was coming from a different file. This totally fixed the issue for me.
Not sure why it works as I cache the samples into memory, but even loading the same file into two different sounds didn't fix it. Only when the samples came out of two different files did the errors go away.
I'm sure this won't help everyone and it's not the prettiest fix but it might help someone.
john.k.doe is right. You must reduce the size of your mp3 file. You should keep the size under 100kb per file. I had to reduce my 200kb file to 72kb using a constante bit rate(CBR) of 32kbps instead of the usual 128kbps. That worked for me!
Try
final ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 50);
tg.startTone(ToneGenerator.TONE_PROP_BEEP, 200);
tg.release();
Releasing should keep your resources.
I was with this problem. In order to solve it i run the method .release() of SoundPool object after finish playing the sound.
Here's my code:
SoundPool pool = new SoundPool(10, AudioManager.STREAM_MUSIC, 50);
final int teste = pool.load(this.ctx,this.soundS,1);
pool.setOnLoadCompleteListener(new OnLoadCompleteListener(){
#Override
public void onLoadComplete(SoundPool sound,int sampleId,int status){
pool.play(teste, 20,20, 1, 0, 1);
new Thread(new Runnable(){
#Override
public void run(){
try {
Thread.sleep(2000);
pool.release();
} catch (InterruptedException e) { e.printStackTrace(); }
}
}).start();
}
});
Note that in my case my sounds had length 1-2 seconds max, so i put the value of 2000 miliseconds in Thread.sleep(), in order to only release the resources after the player have had finished.
Like said above, there is a problem with looping: when I set repeat to -1 I get this error, but with 0 everything is working properly.
I've noticed that some sounds give this error when I'm trying to play them one by one. For example:
mSoundPool.stop(mStreamID);
mStreamID = mSoundPool.play(mRandID, mVolume, mVolume, 1, -1, 1f);
In such case, first track is played ok, but when I switch sounds, next track gives this error. It seems that using looping, a buffer is somehow overloaded, and mSoundPool.stop cannot release resources immediately.
Solution:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mStreamID = mSoundPool.play(mRandID, mVolume, mVolume, 1, -1, 1f);
}, 350);
And it's working, but delay is different for different devices.
In my case, reducing the quality and thereby the file sizes of the MP3's to under 100kb wasn't sufficient, as some 51kb files worked while some longer duration 41kb files still did not.
What helped us was reducing the sample rate from 44100 to 22050 or shortening the duration to less than 5 seconds.
I see too many overcomplicated answer. Error -12 means that you did not release the variables.
I had the same problem after I played an OGG audio file 8 times.
This worked for me:
SoundPoolPlayer onBeep; //Global variable
if(onBeep!=null){
onBeep.release();
}
onBeep = SoundPoolPlayer.create(getContext(), R.raw.micon);
onBeep.setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) { //mp will be null here
loge("ON Beep! END");
startGoogleASR_API_inner();
}
}
);
onBeep.play();
Releasing the variable right after .play() would mess things up, and it is not possible to release the variable inside onCompletion, so notice how I release the variable before using it(and checking for null to avoid nullpointer exceptions).
It works like charm!
A single soundPool has an internal memory limitation of 1 (one) Mb. You might be hitting this if your sound is very high quality. If you have many sounds and are hitting this limit, just create more soundpools, and distribute your sounds across them.
You may not even be able to reach the hard track limit if you are running out of memory before you get there.
That error not only appears when the stream or track limit has been reached, but also the memory limit. Soundpool will stop playing old and/or de-prioritized sounds in order to play a new sound.

Getting audio volume during playback

I have some audio data (raw AAC) inside a byte array for playback. During playback, I need to get its volume/amplitude to draw (something like an audio wave when playing).
What I'm thinking now is to get the volume/amplitude of the current audio every 200 milliseconds and use that for drawing (using a canvas), but I'm not sure how to do that.
.
.
.
.
** 2011/07/13 add following **
Sorry just been delayed on other project until now.
What I tried is run the following codes in a thread, and playing my AAC audio.
a loop
{
// int v=audio.getStreamVolume(AudioManager.MODE_NORMAL);
// int v=audio.getStreamVolume(AudioManager.STREAM_MUSIC);
int v=audio.getStreamVolume(AudioManager.STREAM_DTMF);
// Tried 3 settings above
Log.i(HiCardConstants.TAG, "Volume - "+v);
try{Thread.sleep(200);}
catch(InterruptedException ie){}
}
But only get a fixed value, not dynamic volume...
And I also found a class named Visualizer, but unfortunately, my target platform is Android 2.2 ... :-(
Any suggestions are welcome :-)
After days and nights, I found that an Android app project called ringdroid
can solve my problem.
It helps me to get an audio gain value array, so that I can use to to draw my sound wave.
BTW, as my experience, some .AMR or .MP3 can't be parsed correctly, due to too low bitrate...

Android SoundPool sometimes plays sound twice when loop param is set to 0

Here is what I'm doing:
private SoundPool pool;
private int soundId;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// new SoundPool with three channels, STREAM_MUSIC, default quality
pool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
// load sound with current context, click resource, priority 1
soundId = pool.load(this, R.raw.click, 1);
// originally I wasn't using this but it seemed to help a bit
// set no loop for soundId
pool.setLoop(soundId, 0);
}
private void play()
{
Log.v(TAG, "Play the sound once!");
// half volume L & R, priority 1, loop 0, rate 1
pool.play(soundId, 0.5f, 0.5f, 1, 0, 1);
}
#Override
public void onDestroy()
{
super.onDestroy();
// release the pool
pool.release();
}
Originally I was using a .wav file for the click sound and the problem would occur 90% of the time. I added setLoop and it appears to reduce it a tiny bit.
I thought it might be a problem with loading .wav files so I converted the file to .mp3. Now the problem happens 5% of the time, but it still happens. I built with and without the setLoop call and it appears that including it helps a tiny bit.
As you can see I have added a debug log message to ensure that I am not accidentally calling the play function twice. According to the log output, the sound is only played once but I hear the sound twice.
I have also used several different sound files. Some of the files seem to repeat more frequently than others. I don't see any correlations except it happens more frequently with .wav files.
I see the problem happening on Samsung Continuum running 2.1 (min & target API: level 7). I haven't experienced any extra looping with any market apps I've downloaded on the same device. Unfortunately, I don't have other devices to test with.
I have only found one other person experiencing this issue and he or she was also using a Samsung device.
Here is a link to the other issue reported:
https://stackoverflow.com/q/4873995/695336
At this point I guess I'll try making a release build to see if it still happens and then maybe convert to .ogg format. After that I'll probably try switching to MediaPlayer to see if I get the same results.
Thanks for your help.

Categories

Resources