AVAudioSession / Audio Session Services switching

2019-03-25 13:06发布

问题:

Okay, I have my AVAudioSession defined with the following (yes, mix of the c and obj-c calls) Also note that the app has background mode audio, because if recording it must continue to do so while the app is in the background:

[(AVAudioSession *)[AVAudioSession sharedInstance] setDelegate: self];
// Allow the app sound to continue to play when the screen is locked.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
//Turn off automatic gain on the microphone
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeMeasurement error:nil];
//Turn on the ability to mix with others
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);

//Activate the audio session
[[AVAudioSession sharedInstance] setActive:YES error:nil];

The app has several different options for audio (only the first two have been programmed):

  • Normal: no audio from app, so don't mess with outside audio
  • Only recording of headset mic: must have no gain adjustments, and don't mess with outside audio (which will be playing through headset)
  • Play music from in app, without recording: Stop outside audio, and play through current output (speaker or headset)
  • Play music from in app and record headset mic: Stop outside audio, record and play only through headset

The recording is working well in foreground and background, and I'll add the playing later. However, I just noticed tonight that if audio is already playing (Pandora) on the loud speaker and I go into my app and activate the recording mode then it switches Pandora to playing through the phone speaker, and even after the audiosession is deactivated and the app is backgrounded (but not forced closed) the audio continues to play through the phone speaker until I force close the app.

//Deactivate the audio session
[[AVAudioSession sharedInstance] setActive:NO error:nil];

On the flip side, if the headset is in and the music is playing through the headset when the app is started into the recording mode, then there is only a brief pause and the music continues playing at the same volume (correctly, no ducking).

I don't see any reason why the route should change when I activate the audio session without a headset in, and why it doesn't change back when the session is deactivated. Especially changing to the phone speaker! Is there something that I'm doing wrong, or do I just need to define the AVAudioSession differently depending on what the user wants to do (instead of the blanket play+record and mode measurement)? And even if I need to define it separately for different use cases. For instance the audio will -always- be going through the headset if the app is recording, and if the app is not recording, then the audio output either the headset or loud speaker (depending on if the user has a headset plugged in or not - i.e., normal behavior).

Additional Details

Okay, I tried switching all the code c and noticed a note buried within the docs about kAudioSessionProperty_OverrideAudioRoute

kAudioSessionOverrideAudioRoute_None
Specifies, for the kAudioSessionCategory_PlayAndRecord category, that output audio should go to the receiver. This is the default output audio route for this category.

So then I tried setting this property (these properties) in 3 different ways:

  • kAudioSessionProperty_OverrideAudioRoute with kAudioSessionOverrideAudioRoute_Speaker continues playing the audio, but switches it out through the receiver, and shows the route as ReceiverAndMicrophone
  • kAudioSessionProperty_OverrideCategoryDefaultToSpeaker with kAudioSessionOverrideAudioRoute_Speaker stops the currently playing audio, and shows the route as SpeakerAndMicrophone
  • kAudioSessionProperty_OverrideCategoryDefaultToSpeaker with a value of 1 does the same thing as kAudioSessionOverrideAudioRoute_Speaker

So basically the docs say that the default is to switch to the Receiver. However, no matter what I do, I can't seem to keep the Speaker output and keep the outside audio playing.

回答1:

Apparently the order of setting properties matters, although there is no mention of this in the docs. For those that find this eventually, here's what works:

//Init and set the interrupt listener.  last parameter is passed to interruptlistener
AudioSessionInitialize(NULL, NULL, interruptlistener, NULL);

//Allow the app sound to continue to play when the screen is locked.
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);

//Force current audio out through speaker
UInt32 routeSpeaker = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(routeSpeaker), &routeSpeaker);

//Turn on the ability to mix with others
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);