As described in documentation, the automatic ducking feature has been introduced in Android 8.0. Ducking means that if your music application has been interrupted by some short sound (like a notification, for ex.), your application will continue playing music, but music volume will be temporary lowered while short sound is playing.
I use a text-to-speech engine to read long articles, and want it to behave similar to music player, i.e. I want it to be automatically ducked by the system in android 8.0
I've got ducking to work fine without any additional code on Android 8.0 for music playing, but not for text-to-speech playing.
Here is a sample code
public class PlayerService extends Service {
//private MediaPlayer mp;
TextToSpeech tts;
#Override
public void onCreate() {
super.onCreate();
//...
//create foreground notification stuff omitted...
//...
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
am.requestAudioFocus(listener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
/*
mp = MediaPlayer.create(this, R.raw.music);
mp.setLooping(true);
mp.start();
*/
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tts.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
);
}
tts.speak("IF you happen to have read another book about Christopher Robin, you may remember that he once had a swan (or the swan had Christopher Robin, I don't know which) and that he used to call this swan Pooh. That was a long time ago, and when we said good-bye, we took the name with us, as we didn't think the swan would want it any more. Well, when Edward Bear said that he would like an exciting name all to himself, Christopher Robin said at once, without stopping to think, that he was Winnie-the-Pooh. And he was. So, as I have explained the Pooh part, I will now explain the rest of it. ",
TextToSpeech.QUEUE_ADD, null);
}
});
}
#Override
public void onDestroy() {
tts.stop();
/*
mp.stop();
mp.release();
*/
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
am.abandonAudioFocus(listener);
super.onDestroy();
}
private AudioManager.OnAudioFocusChangeListener listener = new AudioManager.OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
//nothing to do here
}
};
}
If I'd remove text-to-speech stuff and uncomment MediaPlayer lines, ducking will work fine for music playing. But if using text-to-speech as presented, no ducking is happening - during the notification sound text-to-speech continues playing as usually, and no volume change is performed.
I want ducking (volume change) to happen also if I use text-to-speech. What am I doing wrong?
Related
I'm trying to build a game which plays some sounds effects on click & at the same time music in the background.
I tried implementing this with two MediaPlayer objects.
The first one, which served for the effects on click works great.
The second one however sometimes logs error 100, sometimes error 38. No sound at all.
Variables
private MediaPlayer mEffects;
private MediaPlayer mpSoundBackground;
Implementation of the sound media player:
mpSoundBackground = MediaPlayer.create(MainActivity.this, R.raw.soundbackground1small);
mpSoundBackground.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Logger.d("prepared");
musicPrepared = true;
}
});
mpSoundBackground.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Logger.d("error "+what);
return false;
}
});
if (musicPrepared) {
mpSoundBackground.start();
Logger.d("music is prepared");
} else {
Logger.d("music is not prepared");
}
Implementation of the effects Media Player:
stopPlaying();
mEffects= MediaPlayer.create(MainActivity.this, R.raw.soundhit);
mEffects.start();
private void stopPlaying() {
if (mEffects!= null) {
mEffects.stop();
mEffects.release();
mEffects= null;
}
}
Update
To add to the confusion: It does seem to work in emulator
(Genymotion), but does not work on my OnePlus One, running Lollipop
You need to use the setOnPreparedListener method for both players. also if you want to play a sound on clicks consider using SoundPool.
Also in the public void onPrepared(MediaPlayer mp) method, you can use mp.start there is no need for that flag, since you can not know for sure that it is prepared once you reach that prepared flag
I couldn't make the errors go away, until I reconverted my soundfile to MP3.
Now it plays both on device & simulator without any problems.
Moral of this story: if you are running into errors, try a few encodings of the same file (possibly a few file sizes too!), it might be the solution.
I have tried all methods mentioned in the following links
How to shut off the sound MediaRecorder plays when the state changes
Need to shut off the sound MediaRecorder plays when the state changes
but none of them work.
Anyone knows how to achieve that ?
Though I am too late to answer it. It may still help peoples who all are googling the same problem.
Before starting media recorder add following two lines of code ..
Its gonna mute phones sound..
//mute phone
AudioManager audioManager = (AudioManager) context.getSystemService(AUDIO_SERVICE);
audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
mediaRecorder.start();
After starting media recorder wait one or two seconds and un-mute the phone, u may use following runnable...
new Thread(new UnmuterThread()).start();
//timer thread to un-mute phone after 1 sec
//This is runnable inner class inside your activity/service
class UnmuterThread implements Runnable{
#Override
public void run() {
synchronized (this){
try {
wait(1000);
} catch (InterruptedException e) {
} finally {
//unmute the phone
AudioManager audioManager1 = (AudioManager) context.getSystemService(AUDIO_SERVICE);
audioManager1.setRingerMode(AudioManager.RINGER_MODE_NORMAL); }
}
}
}
I'm trying to write a function to play a short sound (in /res/raw) in my program, called at effectively random times throughout the program. So far I have this function:
public void playSound() {
MediaPlayer mp = new MediaPlayer();
mp = MediaPlayer.create(this, R.raw.ShortBeep);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setLooping(false);
mp.start();
}
It works fine for awhile, but after exactly 30 plays of the sound, it stops making sound.
According to the Docs
... failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether.
When you are done with it call mp.release() so that it can release the resources. I don't know what the limit is and I'm sure it depends on many factors. Either way you should be calling this function on your MediaPlayer object, especially if it will be used more than once.
I've just solved the exact same problem, but I'm using Xamarin. I ended up changing from holding on to a MediaPlayer instance for the lifetime of the activity to creating an instance each time I want to play a sound. I also implemented the IOnPreparedListener and IOnCompletionListener.
Hopefully you can get the idea despite it being C# code
public class ScanBarcodeView :
MvxActivity,
MediaPlayer.IOnPreparedListener,
MediaPlayer.IOnCompletionListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ScanBarcodeView);
((ScanBarcodeViewModel) ViewModel).BarcodeScanFailed += (sender, args) => PlaySound(Resource.Raw.fail);
((ScanBarcodeViewModel) ViewModel).DuplicateScan += (sender, args) => PlaySound(Resource.Raw.tryagain);
}
private void PlaySound(int resource)
{
var mp = new MediaPlayer();
mp.SetDataSource(ApplicationContext, Android.Net.Uri.Parse($"android.resource://com.company.appname/{resource}"));
mp.SetOnPreparedListener(this);
mp.SetOnCompletionListener(this);
mp.PrepareAsync();
}
public void OnPrepared(MediaPlayer mp)
{
mp.Start();
}
public void OnCompletion(MediaPlayer mp)
{
mp.Release();
}
}
So, each time I want a sound to be played I create a MediaPlayer instance, so the data source, tell it that my Activity is the listener to Prepared and Completion events and prepare it. Since I'm using PrepareAsync I don't block the UI thread. When the media player is prepared the Start method on the MediaPlayer is called, and when the sound has finished playing the MediaPlayer object is released.
Before I made these changes I would get to 30 sounds played and it would all stop working. Now I've gone way past 30, also multiple sounds can be played simultaneously.
Hope that helps.
Im using this code to get AudioFocus and it works ok with
Android Music app ( the one preinstalled )
int result = audioManager.requestAudioFocus(meService, AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
However when i release it with this code
audioManager.abandonAudioFocus(meService);
The Android Music app ( the one preinstalled ) does not continue playing.
if i use the AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK it works
but Android Music app is not lowering the volume enough.
Any ides why Android Music app is not resuming playback?
im using api8 and using the onAudioFocusChange
You should release it in MediaPlay.onCompleteListener(){}
After trying 4 players non of them are ducking. I might be doing something wrong and would like to see that. I answer my own question that we have to live with this.
This way works fine for me.
public class HomeActivity extends Activity implements AudioManager.OnAudioFocusChangeListener {
// Other stuff
#Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.i("HomeActivity", "Audio focus granted.");
}else if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.i("HomeActivity", "Audio focus failed.");
}
}
}
Request AudioFocus:
private void requestAudioFocus(){
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
// Request audio focus for playback
am.requestAudioFocus(this,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
Abandon AudioFocus:
private void abandonAudioFocus(){
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
am.abandonAudioFocus(this);
}
Hope this would help you.
I've created an app that uses MediaPlayer to play a random (short) sound when a button is clicked. The sounds are played correctly on android devices < 2.2. This is the code responsible for playing sounds.
r = new Random();
sounds = new ArrayList<MediaPlayer>();
sounds.add(MediaPlayer.create(this, R.raw.sound1));
sounds.add(MediaPlayer.create(this, R.raw.sound2));
sounds.add(MediaPlayer.create(this, R.raw.sound3));
sounds.add(MediaPlayer.create(this, R.raw.sound4));
sounds.add(MediaPlayer.create(this, R.raw.sound5));
theButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
playSound();
}
});
private void playSound() {
Thread thread = new Thread() {
public void run() {
MediaPlayer soundPlayer = sounds.get(r.nextInt(sounds.size()));
while (soundPlayer.isPlaying())
{
soundPlayer = sounds.get(r.nextInt(sounds.size()));
}
soundPlayer.seekTo(0);
soundPlayer.start();
}
};
thread.start();
}
The sounds are all .wav files. I tried converting them to .mp3, but then they wouldn't play at all. Am I doing something extremely wrong, or is the MediaPlayer in 2.2 buggy? Anyone else had this problem and know of a fix? Keep in mind that the sounds are played normally on all other devices with an android version below 2.2.
I think you shouldn't create a ArrayList for MediaPlayer. Instead that, you use only a MediaPlayer object and a ArrayList to contain all music resources.
When you next other song, you update only the info of MediaPlayer. For example,
Release the previous MediaPlayer object.
Create other MediaPlayer object
Finally, start this song
Seems there was a problem with the sampling rate of the mp3's that the 2.2 Framework frowned upon. I fixed it by opening up the sounds in a sound editor, resampling them and adding silence to the first and last second of the sounds.