Handling interruptions with Audio queue - iphone

I am developing an audio streamer and have declared an interruption listener to save state of a song when an interruption occurs - like an incoming call or an sms.
Here is the relevant code
In my AppDelegate, I have this
AudioSessionInitialize (NULL, NULL, interruptionListenerCallback, self);
AudioSessionSetActive(YES);
This is what the interruption listener looks like
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState) {
// This callback, being outside the implementation block, needs a reference
//to the AudioPlayer object
MyPlayer *player = (MyPlayer *)inUserData;
if (interruptionState == kAudioSessionBeginInterruption) {
if ([player audioStreamer]) {
// if currently playing, pause
[player pausePlayback];
player.interruptedOnPlayback = YES;
}
} else if ((interruptionState == kAudioSessionEndInterruption) && player.interruptedOnPlayback) {
// if the interruption was removed, and the app had been playing, resume playback
[player resumePlayback];
player.interruptedOnPlayback = NO;
}
}
When I get a phone call the interruption listener is called and if the user declines the call the playback resume method is also called. But before the resumePlayback method is called, I get this error in the console for AudioQueueEnqueueBuffer
error: tca! error int: 560030580
Does anyone have an idea of how to correctly handle audio interruptions when streaming audio files.
Thanks.

This link shows what the problem is. Might help someone.
https://devforums.apple.com/message/31758#31758

!act is kAudioSessionNotActiveError, declared in AudioServices.h, with the comment
"The operation failed because the AudioSession is not active. Calling AudioSessionSetActive(true) first will fix this error in most cases."
You also get this error when you call AudioQueueStart() after an interruption (as I found out today).

It looks to me like you are setting inUserData as your appDelegate instead of your player.

Related

How to resume spotify player playback after interrupt when app is in background?

I have an app which plays Spotify track. It works perfectly when app moves to background but when it get interrupted by 'call' or 'iOS pop-ups' it stops playing.
I have added Required background modes to App plays audio in .plist.
How to resume playback when interruption ended while app is in background.
i have used audio interruption callbacks.
AudioSessionInitialize (NULL, NULL, interruptionListenerCallback,self);
AudioSessionSetActive(YES);
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
And after receiving interruption call play/pause method of playback manager.
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState)
{
// This callback, being outside the implementation block, needs a reference
//to the AudioPlayer object
CustomPlayerView *controller = (__bridge CustomPlayerView *) inUserData;
if (interruptionState == kAudioSessionBeginInterruption)
{
[controller.playbackManager setIsPlaying:NO];
}
else if (interruptionState == kAudioSessionEndInterruption)
{
[controller.playbackManager setIsPlaying:YES];
}
}

Cannot initialize a variable of type'CFDictionaryRef' in XCode 4.6

i'm writing an iPhoneApp that will record sound from a device through the audio Jack, I tried compiling my code but it keep saying
'Cannot initialize a variable of type'CFDictionaryRef'(aka 'const_CFDictionary*') with an lvalue of type 'const void*'
Here is part of my Code:
#pragma mark Audio session callbacks_______________________
// Audio session callback function for responding to audio route changes. If playing
// back application audio when the headset is unplugged, this callback pauses
// playback and displays an alert that notifies the user that power has been terminated.
//
// The system takes care of iPod audio pausing during route changes--this callback
// is not involved with pausing playback of iPod audio.
void audioRouteChangeListenerCallback (
void *inUserData,
AudioSessionPropertyID inPropertyID,
UInt32 inPropertyValueSize,
const void *inPropertyValue
) {
// ensure that this callback was invoked for a route change
if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return;
// This callback, being outside the implementation block, needs a reference to the
// MainViewController object, which it receives in the inUserData parameter.
// You provide this reference when registering this callback (see the call to
// AudioSessionAddPropertyListener).
MaxViewController *controller = (__bridge MaxViewController *) inUserData;
// if application sound is not playing, there's nothing to do, so return.
if (controller.sound.playing == 0 ) {
NSLog (#"Audio route change while application audio is stopped.");
return;
} else {
// Determines the reason for the route change, to ensure that it is not // because of a category change.
CFDictionaryRef routeChangeDictionary = inPropertyValue;
CFNumberRef routeChangeReasonRef =
CFDictionaryGetValue (
routeChangeDictionary,
CFSTR (kAudioSession_AudioRouteChangeKey_Reason)
);
SInt32 routeChangeReason;
I've searched every possible result here in stackoverflow and elsewhere but it has proven abortive. Any help would be greatly appreciated.
Thanks in advance.

kAudioSessionOverrideAudioRoute_Speaker issue

i need to trigger the iphone audio route back and forth from speaker to normal mode very quickly. I've created an audio Session similar to this site:
http://atastypixel.com/blog/using-remoteio-audio-unit/
Im creating a app that can do VOIP.
So let me explain the simple issue i have. I have a button that toggles from speaker to normal mode like this:
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = true;
}
... you get the idea just toggling the speaker on and off.
Here is the issue: When the end user RAPIDLY hits the button to toggle the speaker after about 10 presses the application freezes for a while as if its releasing something and after 30 seconds comes back. I need the end user to be able to constantly tap this button on/off as much as they want.
update: I tried calling the speakers in the background thread but still if i press the speakers too many times it freezes or i loose audio here is the code:
-(void) manageSpeakerState{
OSStatus error;
UInt32 value;
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = false;
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = true;
speakerState = true;
}
}
// this gets called whenever the speaker button is pressed ..it toggles the speakers on/off
-(IBAction) speaker
{
[self performSelectorInBackground:#selector(manageSpeakerState) withObject:nil ];
}
UPDATE: My audio session is running on a seperate thread using pthread and its in a .c file. It could be a concurrency issue as it seems when the main thread gets tied up this issue occurs. How can i update the audio route on the thread that the audio session is running on ?
UPDATE: i have the same issue as this: http://lists.apple.com/archives/coreaudio-api/2012/Jul/msg00129.html
Whenever i change routes (ie. speaker or headphone) my audioUnits are paused so im reading empty stuff into the buffers. Its almost like a hardware manufacturer issue. How can i get around this ? the call backs are still being called but theres no audio unit while switching routes.
On button click just change button's background image and tag.Change audio session property in background thread.
In your application delegate just add this piece of code..I hope it work fine.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];

Getting wrong playback state in MP Music Player Controller in ios 5

i am getting wrong playback state in MP music player.
while playing a song i am getting pause state.
My app is working fine in ios 4.but i am having this issue in ios 5.
can anybody Help me ??
My code is here.
[musicPlayer stop];
if (userMediaItemCollection)
{
userMediaItemCollection=nil;
}
musicPlayer.nowPlayingItem=nil;
userMediaItemCollection=[MPMediaItemCollection collectionWithItems:[mediaItemCollection items]];
[musicPlayer setQueueWithItemCollection:userMediaItemCollection];
[musicPlayer setNowPlayingItem:
[[userMediaItemCollectionitems]objectAtIndex:indexOfCurrentObject]];
[self enablePrevAndNextButtons];
[musicPlayer play];
}
-(void)playbackStateDidChanged:(NSNotification *)notification
{
if (musicPlayer.playbackState!=MPMusicPlaybackStatePlaying)
{
[playPauseButton setBackgroundImage:[UIImage imageNamed:#"play_iPad.png"] forState:UIControlStateNormal];
}
else if(musicPlayer.playbackState==MPMusicPlaybackStatePlaying)
{
[playPauseButton setBackgroundImage:[UIImage imageNamed:#"pause_iPad.png"] forState:UIControlStateNormal];
}
I have also reported this bug to Apple. I was able to reproduce it 100% of the time by doing the following:
Launch application that uses MPMusicPlayerController.
Launch the "Music" App.
Hit Play, Skip, Skip, Pause, Play, Pause
Open the original application and the MPMusicPlaybackState of MPMusicPlayerController will be incorrect.
None of the proposed solutions here worked for me. The solution that did work was to keep track of when the bug was occurring and updating the UI specially in these cases.
When the UIApplicationDidBecomeActiveNotification notification is received (see matbur post for more details on this), see if audio is actually not playing when the MPMusicPlaybackState said it was:
-(BOOL) isPlaybackStateBugActive {
MPMusicPlaybackState playbackState = self.musicPlayer.playbackState;
if (playbackState == MPMusicPlaybackStatePlaying) {
AudioSessionInitialize (NULL, NULL, NULL, NULL);
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
AudioSessionSetActive (true);
UInt32 audioIsPlaying;
UInt32 size = sizeof(audioIsPlaying);
AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &size, &audioIsPlaying);
if (!audioIsPlaying){
NSLog(#"PlaybackState bug is active");
return YES;
}
}
return NO;
}
Don't forget to import the AudioToolbox framework.
None of these workarounds fix the issue for my app. It is a bug in iOS, and my app will never function properly until Apple fixes it.
I have a music player with a play/pause button. When music is playing, the button shows the "pause" icon. When music is paused, the button shows the "play" icon - just like all music apps. I can replicate the bug at any time by doing the following:
1. Play music in my app (the play/pause button shows the "pause" icon correctly)
2. Background my app and lock my phone for ~10 minutes
3. Double tap home and hit the pause button from the iPod controls
4. Unlock my phone and open my app again
5. Music will be stopped, but my app still shows the "pause" icon when it should so "play"
I've done extensive debugging and logging to ensure that the method that updates my play/pause button is always called when my application becomes active. The issue is that when I re-enter my app, the playback state of MPMusicPlayer is still set to MPMusicPlaybackStatePlaying even when music is stopped/paused.
I filed a bug report for this about a year ago and haven't heard anything from Apple. If someone else would file one it would be greatly appreciated.
I have the same problem but when I play/pause many times when my app is in background, I reported the bug to Apple and hope to get an amswer soon, I want to know if my coding is wrong or there is an API problem. If this was the error you were having, this may be helpful. I came to a workaround which even though isnt the best solution, for my app is acceptable:
In the viewDidLoad, add this:
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver: self
selector: #selector (handle_ApplicationDidBecomeActive:)
name: UIApplicationDidBecomeActiveNotification
object: nil];
Then, create a handle_ApplicationDidBecomeActive method, and add this:
- (void) handle_ApplicationDidBecomeActive: (id) notification
{
if (musicPlayer.playbackState!=MPMusicPlaybackStatePlaying)
{
[playPauseButton setBackgroundImage:[UIImage imageNamed:#"play_iPad.png"] forState:UIControlStateNormal];
[musicPlayer pause];
}
else if(musicPlayer.playbackState==MPMusicPlaybackStatePlaying)
{
[playPauseButton setBackgroundImage:[UIImage imageNamed:#"pause_iPad.png"] forState:UIControlStateNormal];
[musicPlayer pause];
}
}
(dont put this code inside your playbackStateDidChanged method as this may generate an endless loop)
This will sync the state of your buttons and music player to the one reported by the API. in the cases in which there is a coincidence, there will be no impact of any type, in the other cases, the player will pause/play accordingly.
I experienced the same problem with the release of iOS 5. I found that the playbackState property does get updated, but after a delay, so it's not yet set when your playbackStateDidChanged method runs.
My workaround was to set my own instance variable called musicPlayerRecentlyStartedPlaying whenever I start playback. Then I use a method called from a timer to check both that variable and the playbackState property to find out if the player is really playing:
- (void)playMusic {
[self.musicPlayer play];
self.musicPlayerRecentlyStartedPlaying = TRUE;
self.musicControlsUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateMusicControls:) userInfo:nil repeats:TRUE];
}
- (void)stopMusic {
[self.musicPlayer stop];
self.musicPlayerRecentlyStartedPlaying = FALSE;
[self.musicControlsUpdateTimer invalidate];
}
- (void)updateMusicControls:(NSTimer *)timer {
BOOL playing = (([self.musicPlayer playbackState] == MPMusicPlaybackStatePlaying)&&(self.musicPlayer.nowPlayingItem));
if (!playing) {
// check to see if we recently started playing
if (self.musicPlayerRecentlyStartedPlaying) {
playing = TRUE;
}
} else {
// once the property is updated, we no longer need this
self.musicPlayerRecentlyStartedPlaying = FALSE;
}
}
You might not need to call updateMusicControls from a timer, but I do because I'm also updating the position of a progress bar as the music plays.
This code using when previous Button click Previous song will be play
- (IBAction)playPreviousSongInList:(id)sender {
static NSTimeInterval skipToBeginningOfSongIfElapsedTimeLongerThan = 3.5;
NSTimeInterval playbackTime = self.musicPlayer.currentPlaybackTime;
if (playbackTime <= skipToBeginningOfSongIfElapsedTimeLongerThan) {
[self.musicPlayer skipToPreviousItem];
} else {
[self.musicPlayer skipToBeginning];
}
}

how to resume recording after interruption occured in iphone?

I am working on an audio recorder application, it is working perfectly fine.
But I'm stuck with the problem of interruption. When a call comes,
- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder
then this method is called and the recording is paused.
And if the user rejects the call:
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder
Then here I want to resume the recording from the point where it was interrupted.
But when I call the record method again, the recording starts with a new file.
The problem is solved!
I have re-written the recording code to avoid this problem. I used AudioQueues, basically the backend code is the same of SpeakHere application with some minor changes. Provided two more apis in it:
-(void)resume
{
AudioQueueStart(queueObject, NULL);
}
-(void)pause
{
AudioQueuePause(queueObject);
}
In AudioRecorder class. The basic purpose being to avoid the recording setup which is done in record method.
Setup the interrupt callback and then use this pause and resume methods appropriately in the callback. Also take care to set active the audio session based on whether your application needs it or not.
Hope this helps someone out there.
EDIT:
Audio interrupt listener callback:
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState)
{
if (interruptionState == kAudioSessionBeginInterruption)
{
[self.audioRecorder pause];
}
else if (interruptionState == kAudioSessionEndInterruption)
{
// if the interruption was removed, and the app had been recording, resume recording
[self.audioRecorder resume];
}
}
Listening for audio interruption:
AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, self);
The OS is probably stopping the AVAudioRecorder during the interruption. You can either present the two or more files as a single recording to the user, or you can use AudioQueue to write your code for handling interruptions (as well as saving audio data to a file).
I tried to do this a while ago and came to the conclusion that it couldn't be done properly; the operating system itself won't let you do it. Maybe that's changed in 3.1.2, but when I tried in 3.0 it just wouldn't work.
To handle the interruption, you have to use AVAudioSessionDelegate methodes instead of AVAudioRecorderDelegate methods.This is the code sample for handling interruption:
/*=================================================================
Interruption Handling Method during Recording
==================================================================*/
- (void) beginInterruption
{
if(self.recorder)
{
//Method to handle UI
}
}
- (void) endInterruption
{
NSError *err = noErr;
[[AVAudioSession sharedInstance] setActive: YES error: &err];
if(err != noErr)
{
NSLog([err description]);
}
//method to handle UI
}
First method automatically deactivate the audio session.So,in second method,you have to reactivate the audio session.First method will pause the recording and when interruption ends you can resume with second method.I have tried this on version 3.0 and upper version.