Android and iOS audio playback is to different speakers

asked 2021-11-15T07:50:31.253+00:00
Grime 776 Reputation points

I have successfully implemented Plugin.AudioRecorder in my app and the playback on my Android device is excellent through the loudspeaker, but the playback on my iOS device is through the earpiece, not the loudspeaker.
I'm at a loss to work out how to play the recorded audio out loud on iOS.

This is not really to do with the app. It works! It's to do with the different devices.

Any ideas? Can anyone suggest something at the ??? below?

private void PlaySelectedAudioButton_Clicked(object sender, EventArgs e)
        {
            try
            {
                Vibration.Vibrate();
                if (Device.RuntimePlatform == Device.Android)
                {
                    audioPlayer.Play(audioRecorderService.FilePath);
                }

                else if (Device.RuntimePlatform == Device.iOS)
                {
                    ???
                    audioPlayer.Play(audioRecorderService.FilePath);
                }

            }
            catch (Exception ex)
            {
                // an error has occurred.
            }
        }
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
4,805 questions
No comments
{count} votes

Accepted answer
  1. answered 2021-11-16T07:12:00.327+00:00
    Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 15,581 Reputation points Microsoft Employee

    Hello,

    Welcome to our Microsoft Q&A platform!

    You could select the Speaker in iPhone without any code modification. When you click the button to play , tap AirPlay and select the speaker , refer to https://support.apple.com/en-us/HT202809#ios

    In other way, you could set AVAudioSessionCategory . I check the iOS sample of Plugin.AudioRecorder, there is a RequestAVAudioSessionCategory method, and it can change the play mode, we just need to use AVAudioSessionCategory.Playback.

    // this controls whether the library will attempt to set the shared AVAudioSession category, and then reset it after recording completes  
    AudioRecorderService.RequestAVAudioSessionCategory (AVAudioSessionCategory.PlayAndRecord);  
    

    But I add it to Xamarin.iOS project and it doesn't work. So, I check the source code and find this plugin use native AVFoundation framework. I try to use DependencyService and AVFoundation , it works, you could refer to the following code:

    Interface in Forms

    public interface IAVService  
        {  
            void setSessionCategory(bool isSpeaker);  
        }  
    

    iOS implementation with the DependencyService

    using AutoReorderiOSDemo;  
    using AutoReorderiOSDemo.iOS;  
    using AVFoundation;  
    using Foundation;  
    using Plugin.AudioRecorder;  
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Text;  
    using UIKit;  
    using Xamarin.Forms;  
      
    [assembly: Dependency(typeof(AVService_iOS))]  
    namespace AutoReorderiOSDemo.iOS  
    {  
        public class AVService_iOS : IAVService  
        {  
            NSError error;  
            public void setSessionCategory(bool isSpeaker)  
            {  
                AVAudioSession.SharedInstance().SetCategory(isSpeaker == true? AVAudioSession.CategoryPlayback: AVAudioSession.CategoryPlayAndRecord, out error);  
            }  
        }  
    }  
    

    Invoke the method in Forms

    async Task RecordAudio()  
    {  
        try  
        {  
            if (!recorder.IsRecording) //Record button clicked  
            {  
                if (Device.RuntimePlatform == Device.iOS)  
                {  
                    DependencyService.Get<IAVService>().setSessionCategory(false);//set CategoryPlayAndRecord  
                }  
                ......  
            }  
            else //Stop button clicked  
            {  
                ......  
            }  
        }  
        catch (Exception ex)  
        {  
            //blow up the app!  
            throw ex;  
        }  
    }  
    

    When record the audio, set CategoryPlayAndRecord, when play the audio set CategoryPlayback

     void PlayAudio()  
    {  
        try  
        {  
            var filePath = recorder.GetAudioFilePath();  
            if (filePath != null)  
            {  
                if (Device.RuntimePlatform == Device.iOS)  
                {  
                    DependencyService.Get<IAVService>().setSessionCategory(true);  
                }  
                player.Play(filePath);  
            }  
        }  
        catch (Exception ex)  
        {  
            throw ex;  
        }  
    }  
    

    ----------update--------

    In similator, it will get a audioQueue.Start() returned non-OK status: GeneralParamError, it means there is no microphone. You could try to change the similator settings( I/O=>Audio Input=>System/Internal ), if your Mac( such as Mac mini) doesn't have microphone, you have to plug a real physical microphone into your Mac. This is a limitation of the iOS simulator.

    Best Regards,
    Wenyan Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


1 additional answer

Sort by: Most helpful
  1. answered 2021-11-17T02:16:52.97+00:00
    Grime 776 Reputation points

    See my comment from the previous answer...

    AudioPage.xaml.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    using Xamarin.Essentials;
    using Plugin.AudioRecorder;
    
    namespace HandsFreeNotes.View
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class AudioPage : ContentPage
        {
            private readonly AudioRecorderService audioRecorderService = new AudioRecorderService()
            { 
                StopRecordingOnSilence = false,
                StopRecordingAfterTimeout = false
            };
            private readonly AudioPlayer audioPlayer = new AudioPlayer();
    
            public AudioPage()
            {
                // add a bit of padding to cater to the "notch" on the iPhone.
                if (Device.RuntimePlatform == Device.iOS) { Padding = new Thickness(0, 40, 0, 0); }
    
                InitializeComponent();
    
                string selAudio = Preferences.Get("selAudio_key", "none");
                if (selAudio == "none")
                {
    
                }
                else
                {
                    LastAudio.Text = selAudio;
                }
            }
    
            private async void BackButton_Clicked(object sender, EventArgs e)
            {
                await Navigation.PopModalAsync();
            }
    
            public interface IAVService
            {
                void setSessionCategory(bool isSpeaker);
            }
    
            async void RecordNewAudio_Clicked(object sender, EventArgs e)
            {
                var status = await Permissions.RequestAsync<Permissions.Microphone>();
    
                if (status != PermissionStatus.Granted)
                    return;
    
                if (audioRecorderService.IsRecording)
                {
                    if (Device.RuntimePlatform == Device.iOS)
                    {
                        DependencyService.Get<IAVService>().setSessionCategory(false);//set CategoryPlayAndRecord
                    }
    
                    await audioRecorderService.StopRecording();
    
                    AudioImage.Source = "flatlineblue.png";
                    RecordNewAudio.Text = "Record New Audio";
                    RecordNewAudio.TextColor = Color.Black;
    
                    try
                    {
                        // Use default vibration length
                        Vibration.Vibrate();
                    }
                    catch (FeatureNotSupportedException ex)
                    {
                        // Feature not supported on device
                    }
                    catch (Exception ex)
                    {
                        // Other error has occurred.
                     }
    
                    string selAudio = audioRecorderService.FilePath;
                    Preferences.Set("selAudio_key", selAudio);
                    LastAudio.Text = selAudio;
                    LastAudio.TextColor = Color.Green;
    
                }
                else
                {
                    try
                    {
                        Vibration.Vibrate();
                    }
                    catch (FeatureNotSupportedException ex)
                    {
                        // Feature not supported on device
                    }
                    catch (Exception ex)
                    {
                        // Other error has occurred.
                    }
    
                    AudioImage.Source = "audiocrop.gif";
                    RecordNewAudio.Text = "Recording... Click to Finish";
                    RecordNewAudio.TextColor = Color.Red;
    
                    await audioRecorderService.StartRecording();
    
                }
    
            }
    
            private void PlaySelectedAudioButton_Clicked(object sender, EventArgs e)
            {
                try
                {
                    Vibration.Vibrate();
    
                    if (Device.RuntimePlatform == Device.iOS)
                    {
                        DependencyService.Get<IAVService>().setSessionCategory(true);
                    }
    
                    audioPlayer.Play(audioRecorderService.FilePath);
                }
                catch (Exception ex)
                {
                    // an error has occurred.
                }
            }
        }
    }
    

    AVService_iOS.cs:

    using AVFoundation;
    using Foundation;
    using HandsFreeNotes.iOS;
    using Plugin.AudioRecorder;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using UIKit;
    using Xamarin.Forms;
    using static HandsFreeNotes.View.AudioPage;
    
    [assembly: Dependency(typeof(AVService_iOS))]
    
    namespace HandsFreeNotes.iOS
    {
        public class AVService_iOS : IAVService
        {
            NSError error;
            public void setSessionCategory(bool isSpeaker)
            {
                AVAudioSession.SharedInstance().SetCategory(isSpeaker == true ? AVAudioSession.CategoryPlayback : AVAudioSession.CategoryPlayAndRecord, out error);
            }
        }
    }