iOS 7+ AVAudioPlayer from phone's Internal Ear Piece - ios

I'm trying to figure how i can play audio using AVAudioPlayer to the phone's internal ear piece.
When headphones are connected the audio should play on those else it must play from internal ear piece (AVAudioPlayer should never play from Phone's Bottom Speaker).
Is there any way to achieve this? iOS 7+ answers are welcome!

It's interesting to see how many more answers there are regarding Android questions, iOS questions are much less answered.
This is the solution that i managed to find:
NSError *error;
// Initialize Audio Player
_player = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:_audioPath] error:&error];
// Set Audio Session to "Play and Record", UNFORTUNATELY this is the only way to play
// from the internal speaker. It asks the user to grant Recording permission to the app.
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error: &setCategoryError];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (setCategoryError) {
CLS_LOG(#"Error routing audio: %#",setCategoryError);
[self.navigationController popViewControllerAnimated:YES];
}
[_player prepareToPlay];
// And now you can play from the internal speaker.
You can see that i had to use AVAudioSessionCategoryPlayAndRecord because it's the only category that routes the audio to the receiver (the small speaker you hold to your ear when on a phone call) see the audio session programming guide for reference.
Actually, there is a method named overrideOutputAudioPort:error: that looks promising but it can only be used AVAudioSessionCategoryPlayAndRecord to force the device to use the external speaker, and not vice versa.

Related

iPhone: Play sound when in Silent mode iOS 10

I've read many posts and articles and am having no luck playing audio when iPhone is in silent. I'm using react-native-sound which uses AVAudioPlayer under the hood. The following calls succeed without any luck with sound coming through when in silent.
NSError *setCategoryError = nil;
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (!success) { /* handle the error condition */ }
NSError *activationError = nil;
success = [audioSession setActive:YES error:&activationError];
if (!success) { /* handle the error condition */ }
Any suggestions?
Resources:
https://developer.apple.com/library/content/qa/qa1668/_index.html
I am struggling with this as well. I don't think it is totally possible. I can have sound play in silent mode when the app is in foreground then continue playing while it is in the background, but I don't think iOS allows you to trigger a sound to play while the app is in the background and in silent mode. I am using local notifications to trigger the sound to play.
Some people have had luck setting 'Application does not run in background' to YES in the info.plist which keeps the app in the foreground when someone locks there phone with the app still in the foreground but I need the app to work if the phone is locked when the app is in the background.
Currently the only solutions I know of are to play a silent audio track then tell it to keep it active after playing (to not waste cpu/battery). See link here. Apple normally doesn't approve of this in app review. Or to use location updates.

Setting AVAudioSession Category has no effect on sound from WKWebView

I can not seem to override the AVAudioSession category or port when audio is being played from a WKWebView. The same code works perfectly when using a normal UIWebView.
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
I've also tried activating the sharedInstance like this, but it did not help:
[session setActive: YES error: nil];
The above code has no effect on audio coming from a WKWebView. I did find some reports on Twitter that iOS 8.1 is mixing WKWebView audio with background app audio, but I couldn't find the source for that. See this twitter thread for reference: https://twitter.com/marcoarment/status/530881842900373504
So apparently WKWebView runs in a separate process from your app. Which probably means, it has its own AudioSession separate from your app's AudioSession as well. That's why changes to your app's AudioSession won't affect the webView. And also why it works with UIWebView (no separate process). At least that's what I gathered so far...
The solution for me was to allow my app's AudioSession to mix with others:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&error];
Of course this has other implications as well. Your app's audio will be mixed with all other apps' audio, not just your webview's.

How to force play audio on iPhone speaker?

I'm developing a mobile application to record audio. Functionality as follows. There is one static audio file on my application (which plays drum sound). When user start recording he can play this drum sound and start speaking/singing. Here I'm facing a problem with the static sound, when the recording is done and the user play back the same recorded audio file the static audio file sound (Drum sound in my case) is not audible properly like the way how user voice audible. If I can route the static audio file sound to the iPhone speaker even ear phone plugged in that will solve the problem. Can any one please help me how can I force play audio through speaker even though ear phone plugged in ? Thank you so much,
You can try this, it solves my problem.
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
Try this,
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)
} catch _ {
}

iOS 7 Bluetooth Output not working for AVAudioSessionCategoryPlayAndRecord

In my app, if iPhone is connected to Bluetooth headset, play sound with Bluetooth. Else use default speaker.
Here's my code
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeVoiceChat error:nil];
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
However, when I tried to use category setCategory:AVAudioSessionCategoryPlayAndRecord. Route to bluetooth device stopped working.. I don't know why. Because by right, the bluetooth option should only work for Record or PlayAndRecord category...
/* only valid with AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayAndRecord */
Addition: Can I have bluetooth for output ONLY (input still use default Mic from phone)?
UPDATE
I changed the order by calling setMode first, followed by setCategory. It works for the first time running the application (a call). When I tried to do the same action again, currentRoute still gave me output = Bluetooth but the actual output returns to default phone front speaker.
It has been a while since I post this question and I kind forgot what exactly the problem is... If I remembered correctly it's due to another lib that kept rewriting the output route. somehow I fixed it and made it work the way I want.
I contributed the code to Jawbone's AudioSessionManager lib. Hope that helps.

ios AVAudioPlayer rings in background until something else turns it off?

So I successfully created an app, in this case it's a VOIP app using linphone sip library. I was doing some tests where I want a WAV file to always play, regardless of whether my app is in the foreground or background. I was able to successfully implement this test with the following code:
NSString * resourcePath = [NSString stringWithFormat:#"%#/myres/sounds/oldphone-mono.wav",[[NSBundle mainBundle] resourcePath]];
ringer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:resourcePath] error:nil];
[ringer setNumberOfLoops:100];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[ringer setVolume:1.0];
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
[ringer play];
I think this is pretty straightforward code, because I copied it from another stack overflow answer. I also had to do something to my plist file to allow for audio background playing.
So the problem is that a function from some 3rd party software (in this case LinPhone SIP Library's Receive Incoming Call Function) that when fired will prevent my app from playing my sound file in background.
As an example, here's a test case:
start up my app and i hear the ringing wav file, that's good
i put the app in background mode and i still hear the ringing wav file, that's good
i bring the app back into focus, i still hear the ringing, that's good
i trigger the linphone receive call function, the ringing still continues because i'm in foreground, that's good
i put the app in background mode, the ringing stops, that's not good
So I suspect that linphone has done something to the avaudioplayer/avaudiosession, such that i can no longer play in background. So my question is, can anyone hazard a guess as to what linphone may have done to prevent my app from playing sound in the background, and how i might get around this issue?
ADDITIONAL NOTES
I even tried to instantiate a new avaudioplayer every second while the app was in the background. But it couldn't override whatever linphone has done to silence the playing of audio.
It appears that Linphone messed with the AVAudioSession Category Options. I had to reset the category option to AVAudioSessionCategoryOptionMixWithOthers in order to get around the problem like so:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];

Resources