Routing iPhone Audio Sound

拥有回忆 提交于 2019-12-03 10:08:39

问题


I have an app which does listen and play sound at the same time. By default, the sound output goes through the earphone. So I use the following code to route it through the speaker:

UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);

This works fine. But now, I'd like to route the sound through the headphones when headphones or external speakers are attached. How would I achieve that?

Also ideally all other sound (i.e. music etc.) should mute when the app launches.

Thanks!


回答1:


To do this you have to add property listener when you setup audio session:

AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, audioSessionPropertyListener, nil);

Where

void audioSessionPropertyListener(void* inClientData, AudioSessionPropertyID inID,
                                          UInt32 inDataSize, const void* inData) {
          UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;

          if (!isHeadsetPluggedIn()) 
            AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
        }

BOOL isHeadsetPluggedIn() {
  UInt32 routeSize = sizeof (CFStringRef);
  CFStringRef route;

  OSStatus error = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
                                            &routeSize,
                                            &route
                                            );



     if (!error && (route != NULL) && ([(NSString*)route rangeOfString:@"Head"].location != NSNotFound)) {
        NSLog(@"HeadsetPluggedIn");
        return YES;
      }
      NSLog(@"Headset_NOT_PluggedIn");
      return NO;
    }

So when headphones are plugged in or out you get a notification and change audio output direction.




回答2:


This is a quick and dirty way and seems to work for me:

void sessionPropertyListener(void *                  inClientData,
                             AudioSessionPropertyID  inID,
                             UInt32                  inDataSize,
                             const void *            inData){

  if (inID == kAudioSessionProperty_AudioRouteChange)
  {
    CFStringRef newRoute;
    UInt32 size = sizeof(CFStringRef);
    AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &newRoute);
    if (newRoute)
    {
      CFShow(newRoute);
      if (CFStringCompare(newRoute, CFSTR("ReceiverAndMicrophone"),
                          (UInt32)NULL) == kCFCompareEqualTo)//if receiver, play through speakers
      {
        UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
        AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
                                 sizeof(audioRouteOverride),
                                 &audioRouteOverride);
      }
      else if (CFStringCompare(newRoute, CFSTR("HeadsetInOut"),
                               (UInt32)NULL) == kCFCompareEqualTo)//headset
      {
        UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None;
        AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
                                 sizeof(audioRouteOverride),
                                 &audioRouteOverride);
      }
    }
  }
}



回答3:


With AudioSessionSetProperty deprecated since iOS 7 we should be using AVFoundation AVAudioSession. Since the desired action is to allow a user action to override the route through the speaker you might consider the difference between AVAudioSessionPortOverrideSpeaker and AVAudioSessionCategoryOptionDefaultToSpeaker.

According to Technical bulletin Q&A QA1754: "When using AVAudioSessionCategoryOptionDefaultToSpeaker, user gestures will be honored. For example, plugging in a headset will cause the route to change to headset mic/headphones and unplugging the headset will cause the route to change to built-in mic/speaker".

Note that the technical bulletin explains that AVAudioSessionPortOverrideSpeaker would be more appropriate for use with a speakerphone button for example, which is not what the original post was asking for.

https://developer.apple.com/library/ios/qa/qa1754/_index.html

My own implementation is called before I invoke the player as follows:

NSError *error;
AVAudioSession* audioSession   = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
// handle any error
// initiate the player or recorder
[_player play];

Also this question is similar to one addressed in a different but related post.

According to the same technical bulletin referred to here "Think of using overrideOutputAudioPort: in terms of what you might use to implement a Speakerphone button where you want to be able to toggle between the speaker (AVAudioSessionPortOverrideSpeaker) and the normal output route (AVAudioSessionPortOverrideNone)."

Refer to that post if you are looking for implementing the speaker override overrideOutputPort category: How Do I Route Audio to Speaker without using AudioSessionSetProperty?



来源:https://stackoverflow.com/questions/3678249/routing-iphone-audio-sound

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!