问题
I'm building a turn-by-turn navigation app that plays periodic, short clips of sound. Sound should play regardless of whether the screen is locked, should mix with other music playing, and should make other music duck when this audio plays.
Apple discusses the turn-by-turn use case in detail in the "WWDC 2010 session 412 Audio Development for iPhone OS part 1" video at minute 29:20. The implementation works great, but there is one problem - when the app is running, pressing the hardware volume controls adjust the ringer volume, not the app volume. If you want to change the app volume, you must press the volume buttons while a prompt is playing.
Apple is very specific in the video that you shouldn't leave the AVAudioSession active, but if the AVAudioSession is inactive, the volume buttons won't control the volume of my app.
Here is the code I'm using to set things up:
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
propertySetError = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(allowMixing), &allowMixing);
UInt32 shouldDuck = true;
propertySetError = AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(shouldDuck), &shouldDuck);
OSStatus activationResult = AudioSessionSetActive(true);
NSError* err = nil;
_player = [[AVAudioPlayer alloc] initWithData:audioData error:&err];
_player.delegate = self;
[_player play];
And I set the session active to NO at the end, as Apple recommends:
OSStatus activationResult = AudioSessionSetActive(false);
NSAssert(activationResult == kAudioSessionNoError, @"Error deactivating audio session");
Is there something I'm missing, or do I have to go against what they recommended in the video?
回答1:
In your case, you don't want to set the audio session inactive. What you need to do is use two methods, one to set the session up for playing a sound, and the other to set it up for being idle. The first method sets up a mix+duck audio mode, and the second uses a background-audio-friendly mode like ambient.
Something like this:
- (void)setActive {
UInt32 mix = 1;
UInt32 duck = 1;
NSError* errRet;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setActive:NO error:&errRet];
[session setCategory:AVAudioSessionCategoryPlayback error:&errRet];
NSAssert(errRet == nil, @"setCategory!");
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(mix), &mix);
AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(duck), &duck);
[session setActive:YES error:&errRet];
}
- (void)setIdle {
NSError* errRet;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setActive:NO error:&errRet];
[session setCategory:AVAudioSessionCategoryAmbient error:&errRet];
NSAssert(errRet == nil, @"setCategory!");
[session setActive:YES error:&errRet];
}
Then to call it:
[self setActive];
[self _playAudio:nil];
To clean up after playing:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player
successfully:(BOOL)flag {
[self setIdle];
}
To be a good citizen, your app should set the audio session inactive when the it isn't navigating (i.e., performing its main function), but when it is, there is absolutely nothing wrong with keeping the audio session active and using modes to peacefully coexist with other apps. You can duplicate Apple's navigation app functionality using the code above.
来源:https://stackoverflow.com/questions/16474771/cannot-control-volume-of-avaudioplayer-via-hardware-buttons-when-audiosessionact