NateRickard / Plugin.AudioRecorder

Audio Recorder plugin for Xamarin and Windows
MIT License
164 stars 68 forks source link

No audio on AudioPlayer when iOS mute switch is on #27

Closed Rayday closed 6 years ago

Rayday commented 6 years ago

I have run into a problem where users expect the audio to still play back on iOS even when they have the mute hardware switch on. Most apps will play audio despite this, but the AudioPlayer does not. Can you implement this functionality? Or even have ignoring the mute switch as an option?

Rayday commented 6 years ago

Fixed this by adding some iOS specific code to App.xaml.cs: At the top:

#if __IOS__
using Foundation;
using AVFoundation;
#endif

into OnStart():

#if __IOS__
    SetupAVAudioSession();
#endif

into OnSleep():

#if __IOS__
    SetAVAudioSessionActive(false);
#endif

into OnResume():

#if __IOS__
    SetAVAudioSessionActive(true);
#endif

Right below OnResume():

#if __IOS__
protected void SetupAVAudioSession()
{
    var audioSession = AVAudioSession.SharedInstance();
    Foundation.NSError error;
    var success = audioSession.SetCategory(AVAudioSession.CategoryPlayAndRecord, out error);
    if (success)
    {
        success = audioSession.OverrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, out error);
        if (success)
        {
            audioSession.SetActive(true, out error);
        }
    }
}

protected void SetAVAudioSessionActive( bool active)
{
    var audioSession = AVAudioSession.SharedInstance();
    Foundation.NSError error;
    var success = audioSession.SetActive(active, out error);
}
#endif

And that made it so you could always hear the playback

NateRickard commented 6 years ago

Added the ability to handle this for iOS. From your iOS AppDelegate or somewhere in the iOS project:

// forces the shared AVAudioSession into recording mode, and then reset it after recording completes
AudioPlayer.RequestAVAudioSessionCategory (AVAudioSessionCategory.PlayAndRecord); //or another category that supports playing

Then you can do additional session config using callback(s):

// allows you to add additional code to configure/change the shared AVAudioSession before each playback instance
//  this can be used to alter the cateogry, audio port, check if the system will allow your app to access the session, etc.
//  See https://github.com/NateRickard/Plugin.AudioRecorder/issues/27 for additional info
AudioPlayer.OnPrepareAudioSession = audioSession =>
{
    // maybe force audio to route to the speaker?
    var success = audioSession.OverrideOutputAudioPort (AVAudioSessionPortOverride.Speaker, out NSError error);

    // do something else like test if the audio session can go active?

    //if (success)
    //{
    //  audioSession.SetActive (true, out error);
    //}
};

// called after playback if you want to change it there
AudioPlayer.OnResetAudioSession = audioSession => {}

Note that when possible, it still probably makes sense to do the session config in your app so you can handle lifecycle events (like @Rayday did above). But these can help if you have other plugins, etc., competing for the AVAudioSession.

cortensinger commented 5 years ago

Hi Nate Rickard. I am new to Xamarin and really love your AudioRecorder package! Now, I am trying to force the audio to route to the speaker every time playback occurs on iOS. It looks like you are doing this above, but I dont know how to implement this in my code.

I have a cross platform (Android/iOS) app that uses your AudioRecorder. I only need to route the audio to the speaker on the iOS side of things.

Can you provide me with instructions on how to implement this code in my project? For instance, is the above code all I need to add to my AppDelegate.cs file in the iOS project? Should I place this code in the FinishedLaunching() method, or should it go outside of that? How does my application know to execute this code when the myAudioPlayer.play(filepath) function (which is called from a ContentPage) is called?

Your help with this is greatly appreciated :) thank you

NateRickard commented 5 years ago

Hi @cortensinger, sorry I missed your msg since the issue is closed.

Yes, you can add the AudioPlayer.OnPrepareAudioSession handler as shown above somewhere in your AppDelegate - FinishedLaunching() is a good place. Each time the audio player is going to play the audio, it will execute that handler, so if you're changing the output port as I showed above, that would ensure that the audio is always routed thru the speaker.

cortensinger commented 5 years ago

Thank you Nate. I was able to successfully route the audio through the speaker. Works like a charm.

One last question: Say I wanted to access the input volume level coming in through the microphone to display a volume meter for user feedback while recording. Do you think this volume meter can work with your plugin? Thank you :)

EDIT: I simply cloned the project to create a local assembly and then made a Public Static Float volumeLevel variable in the AudioRecordingService.cs file and then updated the value of volumeLevel with the result of Calculate_Levels() every time its called in the AudioStream_OnBroadcast() event handler. Then from my personal app, I referenced the local DLL and was able to simply access the volumeLevel variable whenever I wanted to update the GUI to show the changing volume level. Works great!