ios send audio to speakers during phone call - ios

Does anyone know how to stop the iphone muting the speaker output whilst a phone call is taking place?
I'm developing an app where I need to be able to play audio through the phone speakers whilst there's a phonecall..
Hope someone can help!

I know there's an accepted answer but I don't think that it's completely correct. Some navigation apps (Waze for instance) are able to give audio directions during phone calls.
If you use AVAudioPlayer you can handle interruptions (i.e. phone calls) by responding to the audioPlayerBeginInterruption: delegate (you can read more about it here).
While I didn't have too much time to look into it I did manage to play a short sound file while initiating a phone call or playing a song by using the following code:
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"mySound" ofType:#"mp3"];
SystemSoundID soundFileObject;
NSURL *pathURL = [NSURL fileURLWithPath : path];
CFURLRef soundFileURLRef = (CFURLRef)CFBridgingRetain(pathURL);
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundFileObject);
AudioServicesPlaySystemSound(soundFileObject);
}
Keep in mind that when an interruption comes in your app might be backgrounded or suspended which will also affect your ability to play sounds (or run any other code for that matter).

I don't believe Apple allows other audio to mix with the audio during a phone call. Apple wants to ensure that poorly written apps won't interfere with a user's ability to perform important functions, such as making phone calls, on their phone. So your app is basically forced to comply.

I do not think this is possible and I hope it's not, because personally, I do not want any app recoding my phone calls or interrupting important calls with prank sounds.
I also think this would be a very risky thing to allow if you think about it from a security standpoint.

Related

Can not play sound in background objective c

I have a problem in playing audio file from URL in the background while the iPhone is locked.
my app plays podcasts, but while it is playing the podcast at specific second the podcast will stop and an ad will play, the ad consist of three audio tracks, first and last are the messages "please wait, ad will play" and "thanks, your track will complete now", and the middle track is the ad it self from URL.
I tried to make two AVPlayers one for track and the other for the ad, and I tried to make four players for each track, the process works well if the app is running, but when the iPhone is locked, just the first message starts, and nothing happen after.
any suggestions !!
Finally I found the solution of my problem,
When I save the ad file I use [file writeToFile: atomically:], but the app can't reach the document directory when the device locked for a long time unless the file is in the noneProtection mode, so I use [file writeToFile:filePath options:NSDataWritingFileProtectionNone error:nil];
It Works :)

All instances of AVPlayer mute and refuse to play

iOS 8.1
I have a number of instances of AVPlayer playing sound effects, I also have an instance of AVAudioPlayer playing music.
Randomly when using the app the interface will hang for a split second and the music will stop, after this point none of the AVPlayer plays will trigger.
I'm getting nothing in the log and can't seem to find any reason for the players to stop. I have been unable to reproduce the issue reliably, it seems to happen without any direct cause.
Interestingly even when using MPMusicPlayerController in place of AVAudioPlayer I am experiencing the same problem: Music from my iTunes library will stop just as the AVAudioPlayer tracks did.
Has anybody experienced anything like this? I am completely stumped.
NSURL *urlRadio;
if (is2P){
urlRadio = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"retroremix" ofType:#"mp3"]];
} else {
urlRadio = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"pixel" ofType:#"mp3"]];
}
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlRadio error:nil];
if (!ownMusic){
[audioPlayer play];
} else {
[mDRMAudioPlayer prepareToPlay];
}
I'm getting nothing in the log
I suggest registering for the AVAudioSessionMediaServicesWereResetNotification. Then you'll get something in the log! The problem, I'm guessing, is that the media services daemon is being hosed. You might be crashing it, or some other app might be doing it. I'm guess that you're doing it, but that's neither here nor there; if you discover that you are the cause, you'll need to figure out why. Either way, you need to be prepared for this possibility. If the notification fires, you must throw away all audio and video players and recreate them from scratch.
(And see Apple's Q&A document on this topic: https://developer.apple.com/library/ios/qa/qa1749/.)

AVAudioSessionDelegate called at endInterruption, but beginInterruption not called

I'm setting up an AVAudioSession when the app launches and setting the delegate to the appDelegate. Everything seems to be working (playback, etc) except that beginInterruption on the delegate is not being called when the phone receives a call. When the call ends endInterruption is being called though.
The only thought I have is that the audio player code I'm using used to be based on AVAudioPlayer, but is now using AVPlayer. The callbacks for the AVAudioPlayer delegate for handling interrupts are still in there, but it seems odd that they would conflict in any way.
Looking at the header, in iOS6, it looks like AVAudioSessionDelegate is now deprecated.
Use AVAudioSessionInterruptionNotification instead in iOS6.
Update: That didn't work. I think there's a bug in the framework.
Yes, in my experience, beginInterruption, nor the newly documented AVAudioSessionInterruptionNotification work properly. What I had to do was track the status of the player using a local flag, then handle the endInterruption:withFlags: method in order to track recovery from interruptions.
With iOS 6, the resuming from an interruption will at least keep your AudioPlayer in the right place, so there was no need for me to store the last known play time of my AVAudioPlayer, I simply had to hit play.
Here's the solution that I came up with. It seems like iOS 6 kills your audio with a Media Reset if an AVPlayer stays resident too long. What ends up happening, is the AVPlayer plays, but no sound comes out. The rate on the AVPlayer is 1, but there's absolutely no sound. To add pain to the situation, there's no error on either the AVAudioSession setActive, nor the AVPlayer itself that indicates that there's a problem.
Add to the fact that you can't depend on appWillResignActive, because your app may already be in the background if you're depending on remote control gestures at all.
The final solution I implemented was to add a periodic observer on the AVPlayer, and record the last known time. When I receive the event that I've been given back control, I create a new AVPlayer, load it with the AVPlayerItem, and seekToTime to the proper time.
It's quite an annoying workaround, but at least it works, and avoids the periodic crashes that were happening.
I can confirm that using the C api, the interruption method is also not called when the interruption begins; only when it ends
(AudioSessionInitialize (nil, nil, interruptionListenerCallback, (__bridge void *)(self));
I've also filed a bug report with apple for the issue.
Edit: This is fixed in iOS 6.1 (but not iOS 6.0.1)
Just call:
[[AVAudioSession sharedInstance] setDelegate: self];
I just checked on my iPhone 5 (running iOS 6.0) by setting a breakpoint in the AudioSessionInterruptionListener callback function that was declared in AudioSessionInitialize(), and this interrupt callback does, in fact, get called when the app has an active audio session and audio unit and is interrupted with an incoming phone call (Xcode shows the app stopped at the breakpoint at the beginning of the interruption, which I then continue from).
I have the app then stop its audio unit and de-activate its audio session. Then, on the end interruption callback, the app re-activates the audio session and restarts the audio unit without problems (the app is recording audio properly afterwards).
I built a brand new audio streaming (AVPlayer) application atop iOS 6.0.x and found the same problem.
Delegates are now deprecated and we have to use notifications, that's great, however here's my findings:
During an incoming phone call I get only AVAudioSessionInterruptionTypeEnded in my handler, along with AVAudioSessionInterruptionOptionShouldResume. Audio session gets suspended automatically (audio fades) and I just need to resume playback of AVPlayer.
However when attempting to launch a game, such as CSR Racing, I oddly get the dreaded AVAudioSessionInterruptionTypeBegan but no sign when my application can resume playback, not even killing the game.
Now, this may depend on other factors, such as my audio category (in my case AVAudioSessionCategoryPlayback) and the mixing settings of both applications (kAudioSessionProperty_OverrideCategoryMixWithOthers), I'm not sure, but definitely I see something out of place.
Hopefully others reported that on 6.1beta this is fixed and I yet have to upgrade, so we'll see.

Volume Control in a mostly Silent iOS app

I've asked this question before but I feel I should start a new thread since my other thread is dated and probably poorly worded. I'm wondering what the best approach would be for adding volume control to an iOS app the is mostly silent. A good example would be a navigation app that only plays audio when you approach or miss a turn. In such an app, hearing a turn prompt which is not loud enough, the user would want the volume for the prompts to be audible and would naturally used the side volume controls to adjust prompts to their liking.
There are several problems here. One is that audio is not currently playing so the user has no reference as to how much it has been increased. This is more or less expected however there are technical issues that I am more interested in. To link the side volume control to your app you have to start and manage an audio session. I have not found an authoritative reference for such a situation as most documentation assumes you are currently playing or in the process of starting audio. Managing an audio session for a mostly silent app seems to be an edge case, though I find it rather common in that two of the major apps I've worked on require such functionality.
Of the various problems associated with audio session management, you have to address killing and restoring the audio session as you move in and out of the background. You have to consider other apps playing audio as you begin and stop the session. Depending on your type of app, you may have other more advanced needs such as custom override routing to the speakers, custom mute controls, etc. If you have any experience with such an app could you elaborate on how you addressed such challenges and expound on other issues?
One very common merhod is to set the audio session category appropriate for the type of app at launch, no matter whether sound is immanent or won't be played till tomorrow (as long as the purpose and settings of the app is to play such).
Added:
One way to allow to user to adjust the volume when the app is silent is to provide some means for the user to have your app to immediately start (and/or maybe stop) playing some sound with an amplitude typical for your app: some calibration tone/talk, your copyright notice, trademark jingle, or a safety message, for instance.
The main issue I see when developing apps that are mostly silent regard moving in/out of the foreground and playing nicely with other audio. To give a better idea of what I usually do I'll give some snippets from a recent project. (These are intentionally incomplete and only meant to illustrate a point.) For the sake of argument let's assume we have an AudioManager class that is responsible for maintaining the audio sessions. This class is what we use to instantiate our custom audio player. In such a class we put:
#interface MyAudioManager ()
#property (nonatomic, retain) BOOL alwaysMaintainAudioSession;
#property (nonatomic, retain) MyCustomAudioPlayer *player;
#end
#implementation MyAudioManager
#synthesize alwaysMaintainAudioSession;
#synthesize player;
-(void) applicationWillEnterForeground
{
isInBackground = NO;
if (NO==[self anyAudioIsPlaying] && self.alwaysMaintainAudioSession) {
[self activateAudioSession];
}
}
-(void) activateAudioSession
{
AudioSessionSetActive(TRUE);
AudioSessionAddPropertyListener ( kAudioSessionProperty_AudioRouteChange, AudioPropertyListener, self);
}
-(BOOL) anyAudioIsPlaying
{
return [self otherAudioIsPlaying] || [player isPlaying];
}
-(BOOL) otherAudioIsPlaying
{
UInt32 yesNo;
UInt32 propertySize = sizeof(yesNo);
OSStatus status = AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &propertySize, &yesNo);
if (kAudioSessionUnsupportedPropertyError == status) {
return MPMusicPlaybackStatePlaying == [theiPodMusicPlayer playbackState];
} else {
return MPMusicPlaybackStatePlaying == [theiPodMusicPlayer playbackState] || yesNo;
}
}
The manager allows you to set a property that always keeps the volume control linked to the app sounds which means we always make sure either a session is active or some other app is playing audio. In any other case the volume control reverts to controlling the ringer. So when entering the foreground we have to check for any other audio playing and conditionally activate the audio session. We also need to close the session when moving to the background to restore the ringer volume control.
-(void) applicationDidEnterBackground
{
if (NO==[self anyAudioIsPlaying]) {
AudioSessionSetActive(NO);
}
}
In my solution I include a bunch of other code to handle things like responding intelligently when bluetooth audio devices are connected, factory methods for creating the custom player, custom audio compression and more. The main idea, however, is handling other apps playing audio while attempting to keep the volume control linked to app volume while in the foreground.

Is there a way to access the currently played track while the iPhone is connected to an accessory?

I am trying to receive information about the currently played track in a iOS app. This works pretty fine while the iPhone is not connected to an accessory. If I connect it to my car (Opel Astra, iPhone jack), the following code stops to work as described in the documentation:
If you create an iPod music player and the user plays an item from another library using Home Sharing, the value of this property is nil.
Code:
// nil while connected to an accessory
MPMediaItem *nowPlayingMediaItem =
[[MPMusicPlayerController iPodMusicPlayer] nowPlayingItem];
// Works while not connected to an accessory
NSString *title = [nowPlayingMediaItem valueForProperty:MPMediaItemPropertyTitle];
I even tried "hacky" stuff like to access "private" properties (original code):
MPMediaQuery *query=nil;
MPMediaItemCollection *collection=nil;
id internalPlayer=nil;
Ivar internalPlayeriVar = object_getInstanceVariable(iPod, "_internal", NULL);
internalPlayer = object_getIvar(iPod, internalPlayeriVar);
NSLog(#"internalPlayer: %#", internalPlayer);
Ivar queryIvar = object_getInstanceVariable(internalPlayer, "_query", NULL);
query = object_getIvar(internalPlayer, queryIvar); // nil everytime
Ivar collectionIvar = object_getInstanceVariable(internalPlayer,
"_itemCollection", NULL);
collection = object_getIvar(internalPlayer, collectionIvar); // nil everytime
or to call private methods:
// Same behaviour like [iPod nowPlayingItem], works
// only while no accessory is connected
MPMediaItem *nowPlayingMediaItem =
[iPod nowPlayingItemAtIndex:[iPod indexOfNowPlayingItem]];
// Works while not connected to an accessory
NSString *title = [nowPlayingMediaItem valueForProperty:MPMediaItemPropertyTitle];
Its also no solution to access the new MPNowPlayingInfoCenter, its nil all the time.
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo
My car plays my music directly without using a iPhone app and it seems my iPhone knows what the car is currently playing because it displays the title, artist and cover icon on the lock screen (and only there). Also the internal play count gets increased.
If I check the playback state, it returns also YES if the car plays music:
[[MPMusicPlayerController iPodMusicPlayer] playbackState] == MPMusicPlaybackStatePlaying
So, is there any way (may be through calling private methods) to access the song, the car is currently playing?
Are you using threads? If so run the code on the main thread. If not then register for the MPMusicPlayerController notifications for item change. That way when the song changes your app will know what the new song is. Also be sure this runs on the main thread as well.
If your playback state is updating while connected but your nowPlayingItem isn't, this would confirm it is a bug. I would submit a bug report for this issue.
EDIT:
Visit https://developer.apple.com/support/resources/bug-reporting.html and scroll to the bottom. The last question says you can contact TSI for bug work arounds. You get 2 TSI requests for being a developer for free so you can use one of those to ask them if they have a work around using a private library until the bug is fixed.
Apple just "fixed that" in iOS 6.1 after I reported it as a bug. The following code now works while my iPhone is connected to my car:
MPMediaItem *nowPlayingMediaItem = [iPod nowPlayingItem];
NSString *title = [nowPlayingMediaItem valueForProperty:MPMediaItemPropertyTitle];
NSLog(#"Playing title: %#", title);
And, what I really like: It is also possible to change the playing track using the iPod app - the app appears as you expect it, instead of the big white "connected to an accessory" screen. So this may also work programatically.
I am pretty sure the answer is no, you can't with any public api's at least but you should file a bug with apple for two reasons:
The reason MPNowPlayingInfoCenter does not give you the info is because it has to be specifically implemented by the app playing the music, if apple's app playing then it should have been implemented so file a bug.
Now if you say [[MPMusicPlayerController iPodMusicPlayer] playbackState] reflects playback changes then that would mean iPodMusicPlayer is still the app in charge of playback so giving you nil for MPMediaItemPropertyTitle should also be reported to apple as a bug.
Additionally non public information on the topic is likely covered by the MFi NDA and nobody is going to risk his ass.
Actually you won't get any MPMediaItem because your iPhone isn't playing the songs but your car's accessory connected to the iPhone is accessing the media library. In doing so, it is responsible to update all the metadata of the accessed objects (songs), especially incrementing the playcount and updating the last accessed date of the song. It also stores some information of where in the song it is (position) in the iTunes library.
This information is read by the lock screen to update the cover. This is also what helps the iPod app to continue where your car's accessory left.
So tap into the library and get the latest information from there. Have a look at the TopSongs example project to get started.

Resources