Audio Unit - Messing up recording - ios

Sorry for a long introduction of the problem.
In my app I am using recording audio and set category to record
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:&err];
When an ad network shows interstitial ad, after that recording is all mute (there is no sound in recording). As this happens with users on live network only, I am not able to reproduce it at my end.
In the log I get ">>>> frameSizeChanged = 4096" from users which points to webkit code
https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp
This looks like some process is holding on to Audio Unit setup and not letting it go even after it is completed(freed / released).
Question: Is there a way to clean up audio unit setup by some other module? Just need to reset so that recording can work again in app after the ad is shown.
I have tried to capture "AVAudioSessionInterruptionNotification" but there is none. Any help is really appreciated

You need to handle your audio system getting interrupted. Many things can cause your audio to get interrupted:
- Receive a phone call while your app runs
- Other app running uses audio
- iOS needs to play a sound
- etc
Your app needs to detect this interruption as your app's audio session will be essentially muted. Once the interruption is done, you need to restart your session.
Set up your listener as the first step in your session setup
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourInterruptionListener:) name:AVAudioSessionInterruptionNotification object:nil];
In your interruption listener, handle the cases of getting interrupted and handle restarting your session
-(void) yourInterruptionListener:(NSNotification*) aNotification
{
NSLog(#"Interruption happened");
NSDictionary *interruptionDict = aNotification.userInfo;
NSNumber* interruptionTypeValue = [interruptionDict valueForKey:AVAudioSessionInterruptionTypeKey];
NSUInteger interruptionType = [interruptionTypeValue intValue];
if ( interruptionType == AVAudioSessionInterruptionTypeBegan)
{
// stop your audio session here
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *errorInAudio = nil;
[session setActive:NO error:&errorInAudio];
}
else if ( interruptionType == AVAudioSessionInterruptionTypeEnded )
{
// Your interruption ended, restart the session
// call or paste your session startup code here
}
else if ( interruptionType == AVAudioSessionInterruptionOptionShouldResume )
{
}
else
{
NSLog(#"Some other interruption");
}
}
If you need to simulate the behavior of your users, start your app then call your self from a different phone. It will definitely interrupt your audio.

Related

my app media play in background mode didn't play if once I open apple music app

I have problem with media play in background mode.
In my app I have to play media play in background mode when server send any notification through socket connection.
In my case media player working fine when app in background mode.
Problem is when app in background mode if I play music app and stop the music player and send notification to my app .Than my app didn't play media player.
I added in plist this one "App plays audio or streams audio/video using AirPlay"
I am using "AVPlayer"
Please help me.
Thanks.
If you look into the AVPlayer documentation it says that it can only play one asset at a time. I think Apple Music uses if not the same API something that is related to this.
AVPlayer is intended for playing a single media asset at a time. The
player instance can be reused to play additional media assets using
its replaceCurrentItem(with:) method, but it manages the playback of
only a single media asset at a time
1). Put this code in you AppDelegate.m (didFinishLaunchingWithOptions) methods :
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
2). After that set Background Modes active for Audio, Airplay and Picture in Picture
Might be, its due to Interruption with current AVplayer running item.
Also, take care you implemented it(Below code)before loading any item to AVPlayer. AND enabled Capabilities to Play audio in background.
NSError *myErr;
[self becomeFirstResponder];
[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
AVAudioSession *aSession = [AVAudioSession sharedInstance];
[aSession setCategory:AVAudioSessionCategoryPlayback error:&myErr];
[aSession setMode:AVAudioSessionModeDefault error:&myErr];
[aSession setActive: YES error: &myErr];
Implement and Add Interruption handler, so you can re-play your stopped audio after another audio/interruption stopped. and below listener of it will call.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:aSession];
And manage AVplayer play/pause again in it:
- (void)handleAudioSessionInterruption:(NSNotification*)notification {
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
// • Audio has stopped, already inactive
// • Change state of UI, etc., to reflect non-playing state
IsInteruptionOccured=YES;
} break;
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
IsInteruptionOccured=NO;
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should continue playback.
// Resume after exteranl interruption.
[_audioPlayer play];
BOOL isPlayingWithOthers = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
// test it with...
NSLog(#"other audio is playing %d",isPlayingWithOthers);
}
} break;
default:
break;
}
}

AVAudioSession can't play sound after app re-becomes active

So I have a music app that uses an AVAudioSession to allow it to play when it is in the background. I use this call:
[audioSession setActive:YES
withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
error:nil];
My problem now is if I go to another app and it steals the audio session (thus now stopping music playback from my app and playing something else), and I come back to my app, no matter what I do to reset my audio session or my audio units, my app's sound is gone.
Does anyone know what to do?
So after registering for the AVAudioSession notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:aSession];
You need to resume/restart you need to restart your player in the handler interruption type is AVAudioSessionInterruptionTypeEnded:
- (void)handleAudioSessionInterruption:(NSNotification*)notification {
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
// • Audio has stopped, already inactive
// • Change state of UI, etc., to reflect non-playing state
} break;
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should continue playback.
[player play];
}
} break;
default:
break;
}
}
You can see a complete explanation here: AVplayer resuming after incoming call

iOS background audio recording after call

I'm developing an iOS application that records audio in background. That's work fine.
The problem is when a call comes in: when i'm calling the app stops to record (that's fine) but when the call ends, I need that the app start to record again.
Is it possible ? How can I do this ?
Thanks to all.
UPDATE
I use AudioQueueNewInput to record audio. Now I'm trying to use this code to get incoming call:
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *categoryError = nil;
[session setActive: NO error: nil];
if (![session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&categoryError]){
NSLog(#"Error"); //no error
}
[session setActive: YES error: nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:session];
I can get the ends of call on handleAudioSessionInterruption by AVAudioSessionInterruptionOptionShouldResume, but how can I restart my audio recorder?
I tried to use to call AudioQueueStart again o simply same function that I use to start to record, but I can't see the red bar (mic in use) to know that app is recording. What's wrong ?
Please see this question:
Can not restart an interrupted audio input queue in background mode on iOS
Yes it is possible, but to reactivate the session in the background,
the audio session has to either set AudioSessionProperty
kAudioSessionProperty_OverrideCategoryMixWithOthers
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (allowMixing),
&allowMixing);
or the app has to receive remote control command events:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

Starting AVQueuePlayer from background

Can't make AVQueuePlayer start playing sound queue when it's starting after the app went into the background.
The basic question is: how to start sound from newly created AVQueuePlayer instance from background?
It's for navi-like app that need to play couple of combined sounds with appropriate directions when the time comes. And most of the time the app works in background...
The details are below...
It plays just fine when I start it from active application, and finishes playing sound even after app went to background.
What I did so far:
In AppDelegate inside didFinishLaunchingWithOptions I added:
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
After the app started I clicked the home button so the app went into the background.
When the time came this code executes (the app is still in background, but note, that I have enabled Audio and AirPlay background mode):
-(void)playTrainingFinishedSound
{
NSMutableArray *queue = [NSMutableArray array];
[queue addObject:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"alertkoniectreningu" withExtension:#"m4a"]]];
[self initializeAudioPlayerWithQueue:queue];
[self.appDelegate.audioPlayer play];
}
-(void)initializeAudioPlayerWithQueue:(NSArray *)queue
{
self.appDelegate.audioPlayer = [[AVQueuePlayer alloc] initWithItems:queue];
self.appDelegate.audioPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
}
Unfortunately this code doesn't make any sound, opposite to the situation when the app was in foreground.
Oh God, there was just one line missing in the AppDelegate didFinishLaunchingWithOptions [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
so it looks now like:
audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
if (audioSession) [audioSession setActive:YES error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
The reason the AVPlayer is failing is because you can't create/initialize an Audio Unit while an application is in the background, which is what the AVPlayer is attempting to do under the hood. The exception to this rule is of course audio-playing applications which can be started/resumed via the "Play" buttons built into the OS, while they are in the background. Thus, applications which subscribe to remote control events have the capability to start Audio Units in the background, which is why subscribing to these events appears to solve the problem.
It's unclear whether Apple is OK with using the API feature in this way.

Playing sequence of sound reminders in background pausing music and not stopped by other sounds

I want to write application for iPhone which play the sequence of audio reminders and communicate in a good way with sounds produced by other applications.
To be more specific I want to achieve following points:
1. Play sound reminder every 10 seconds. (this is to simplify question, in my app there is a sequence of time moment, which specifies when to play reminders)
2. Sound should work in background.
3. If music plays it should be paused, my application should produce audio reminder, then music resumed.
4. If other applications start to use sound, my sequence of reminders should continue to work (e.g. music started or incoming call after my application ran).
With the code below I was able to achieve points 1. and 2. But I can not realize how to achieve 3. and 4. at the same time.
- (void)viewDidLoad
{
[super viewDidLoad];
audioSession = [AVAudioSession sharedInstance];
}
// Following function is used to start reproducing sounds and also called
// for the next sounds
- (IBAction)soundPlayShort:(id)sender
{
NSURL * url = #"reminder sound url here";
// soundPlayer is defined in .h file as AVAudioPlayer
if (soundPlayer == nil) {
// initialize soundPlayer
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
soundPlayer.delegate = self;
}
[soundPlayer playAtTime:soundPlayer.deviceCurrentTime + 10];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[self soundPlayShort:self];
}
If I update audioPlayerDidFinishPlaying as in code below, other sounds are resumed (point 3.), but next reminder in my sequence does not called any more (related to point 2.)
- (void)restoreOtherSounds
{
[audioSession setActive:NO
withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:nil];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[self restoreOtherSounds];
[self soundPlayShort:self];
}
And I have not realized how to resume reminder sequence e.g. after incoming call. (point 4)
Even partial answers could be very helpful for me. Thanks in advance.
Related to point 2,Try setting the audio session as active before initialising AVAudioPlayer in soundPlayShort:
[[AVAudioSession sharedInstance] setActive:YES error:nil];

Resources