ios - Why does this audio session fail to recognise an interruption? -
my app synthesises audio lookup table. plays audio crashes moment try stop playing. audio playback needs exit without restarting requirements handling interruption basic. reread apple’s audio session programming guide including section responding interruptions. method handleaudiosessioninterruption
not seem register interrupt i’m missing something.
edit see answer. when began work on knew next nothing nsnotificationcenter
welcome suggestion improvement.
two methods set audio session play in foreground.
- (void)setupaudio { if (_playqueue == null) { if ([self setupaudiosession] == true) { [self setupplayqueue]; [self setupplayqueuebuffers]; } } } - (bool)setupaudiosession { bool success = no; nserror *audiosessionerror = nil; avaudiosession *session = [avaudiosession sharedinstance]; // set notifications [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(handleaudiosessioninterruption:) name:avaudiosessioninterruptionnotification object:session]; // set category success = [session setcategory:avaudiosessioncategoryplayback error:&audiosessionerror]; if (!success) { nslog(@"%@ error setting category: %@", nsstringfromselector(_cmd), [audiosessionerror localizeddescription]); // exit return success; } // set mode success = [session setmode:avaudiosessionmodedefault error:&audiosessionerror]; if (!success) { nslog(@"%@ error setting mode: %@", nsstringfromselector(_cmd), [audiosessionerror localizeddescription]); // exit return success; } // set preferred values nstimeinterval bufferduration = .005; // prefer 5ms buffer duration success = [session setpreferrediobufferduration:bufferduration error:&audiosessionerror]; if (audiosessionerror) { nslog(@"error %ld, %@ %i", (long)audiosessionerror.code, audiosessionerror.localizeddescription, success); } double samplerate = _audioformat.msamplerate; // prefer sample rate of 44.1khz success = [session setpreferredsamplerate:samplerate error:&audiosessionerror]; if (audiosessionerror) { nslog(@"error %ld, %@ %i", (long)audiosessionerror.code, audiosessionerror.localizeddescription, success); } success = [session setactive:yes error:&audiosessionerror]; if (!success) { nslog(@"%@ error activating %@", nsstringfromselector(_cmd), [audiosessionerror localizeddescription]); } // current values samplerate = session.samplerate; bufferduration = session.iobufferduration; nslog(@"sample rate:%0.0fhz i/o buffer duration:%f", samplerate, bufferduration); return success; }
and here method handles interruption when press stop button. not respond.
edit correct method needs block,
not selector.
see answer.
- (void)handleaudiosessioninterruption:(nsnotification*)notification { if (_playqueue) { nsnumber *interruptiontype = [[notification userinfo] objectforkey:avaudiosessioninterruptiontypekey]; nsnumber *interruptionoption = [[notification userinfo] objectforkey:avaudiosessioninterruptionoptionkey]; nslog(@"in-app audio playback stopped %@ %lu", notification.name, (unsigned long)interruptiontype.unsignedintegervalue); switch (interruptiontype.unsignedintegervalue) { case avaudiosessioninterruptiontypebegan: { if (interruptionoption.unsignedintegervalue == avaudiosessionsetactiveoptionnotifyothersondeactivation) { nslog(@"notify other apps audio available"); } } break; default: break; } } }
answer method handle audiosessioninterruption
did not subscribe observer
correctly nsnotificationcentre
. has been fixed adding observer
using block,
not selector.
the solution replaces deprecated avaudiosession delegate
methods in audiobufferplayer,
extremely fit purpose audio player developed direct audio synthesis matthias hollejmans. several deprecated functions including interruptionlistenercallback
later upgraded mario diana. solution (below) uses nsnotification
allowing users exit avaudiosession
gracefully pressing button.
here relevant code.
playviewcontroller.m
uibutton
action performs orderly shutdown of synth
, invalidates timer
, posts notification exit avaudiosession
- (void)fromescbutton:(uibutton*)button { [self stopconcertclock]; ... // code exit playviewcontroller not shown } - (void)stopconcertclock { [_synthlock lock]; [_synth stopallnotes]; [_synthlock unlock]; [timer invalidate]; timer = nil; [self postavaudiosessioninterruptionnotification]; nslog(@"esc button pressed or sequence ended. exit playviewcontroller "); } - (void) postavaudiosessioninterruptionnotification { [[nsnotificationcenter defaultcenter] postnotificationname:@"avaudiosessioninterruptionnotification" object:self]; }
initialising avaudiosession
includes subscribing single interruption notification before starting startaudioplayer
in audiobufferplayer
- (id)init { if (self = [super init]) { nslog(@"playviewcontroller starts motionlistener , audiosession"); [self startaudiosession]; } return self; } - (void)startaudiosession { // synth , audiobufferplayer must use same sample rate. _synthlock = [[nslock alloc] init]; float samplerate = 44100.0f; // initialise synth fill audio buffer audio samples. _synth = [[synth alloc] initwithsamplerate:samplerate]; // initialise audio buffer. _player = [[audiobufferplayer alloc] initwithsamplerate:samplerate channels:1 bitsperchannel:16 packetsperbuffer:1024]; _player.gain = 0.9f; __block __weak playviewcontroller *weakself = self; _player.block = ^(audioqueuebufferref buffer, audiostreambasicdescription audioformat) { playviewcontroller *blockself = weakself; if (blockself != nil) { // lock access synth. callback runs on internal audio queue thread , don't // want thread change synth's state while we're still filling audio buffer. [blockself -> _synthlock lock]; // calculate how many packets fit buffer. remember packet equals 1 frame // because dealing uncompressed audio; frame set of left+right samples // stereo sound, or single sample mono sound. each sample consists of 1 or more // bytes. 16-bit mono sound, each packet 2 bytes. stereo 4 bytes. int packetsperbuffer = buffer -> maudiodatabytescapacity / audioformat.mbytesperpacket; // let synth write buffer. synth knows how fill buffers // in particular format , not care come from. int packetswritten = [blockself -> _synth fillbuffer:buffer->maudiodata frames:packetsperbuffer]; // have tell buffer how many bytes wrote it. buffer -> maudiodatabytesize = packetswritten * audioformat.mbytesperpacket; [blockself -> _synthlock unlock]; } }; // set notifications [self subscribeforblocknotification]; [_player startaudioplayer]; } - (void)subscribeforblocknotification { nsnotificationcenter * __weak center = [nsnotificationcenter defaultcenter]; id __block token = [center addobserverforname:@"avaudiosessioninterruptionnotification" object:nil queue:[nsoperationqueue mainqueue] usingblock:^(nsnotification *note) { nslog(@"received notification!"); [_player stopaudioplayer]; [center removeobserver:token]; }]; }
playviewcontroller.h
these relevant interface settings
@interface playviewcontroller : uiviewcontroller <escbuttondelegate> { ... // initialisation of audio player , synth audiobufferplayer* player; synth* synth; nslock* synthlock; } ... - (audiobufferplayer*)player; - (synth*)synth; @end
audiobufferplayer.m
- (void)stopaudioplayer { [self stopplayqueue]; [self teardownplayqueue]; [self teardownaudiosession]; } - (void)stopplayqueue { if (_audioplaybackqueue != null) { audioqueuepause(_audioplaybackqueue); audioqueuereset(_audioplaybackqueue); _playing = no; } } - (void)teardownplayqueue { audioqueuedispose(_audioplaybackqueue, no); _audioplaybackqueue = null; } - (bool)teardownaudiosession { nserror *deactivationerror = nil; bool success = [[avaudiosession sharedinstance] setactive:no withoptions:avaudiosessionsetactiveoptionnotifyothersondeactivation error:nil]; if (!success) { nslog(@"%s avaudiosession error: %@", __function__, deactivationerror); } return success; }
Comments
Post a Comment