【问题标题】:Forcing iPhone Microphone as Audio Input强制 iPhone 麦克风作为音频输入
【发布时间】:2011-04-29 11:08:06
【问题描述】:

我正在开发一个 iOS 应用程序(目前专门针对 iPhone),该应用程序要求该应用程序仅从 iPhone 内置麦克风录制音频(即使插入了耳机/耳机),并且在耳机上播放(假设现在耳机已插入)。

我想知道目前可用的 API 是否可以做到这一点?如果是这样,任何人都可以阐明我该怎么做吗?

谢谢!

【问题讨论】:

  • 最有趣和最创新的问题自然会无人回答。你自己有很多工作要做。
  • 在 iOS6 中通过内置麦克风录制并通过耳机的耳机同时播放(并且耳机的麦克风未使用)是不可能的。在 iOS7 中,这可以通过 AVAudioSession 类中的 API 扩展来实现。
  • @Daniel S. 我们如何从内部麦克风录制并通过耳机的耳机播放?

标签: ios core-audio microphone


【解决方案1】:

我相信这个问题的答案是否定的。我使用 iPhone 4 和新的 iOS 4 AVFoundation 进行实验,重点关注 AVCaptureDevice 类。

我在应用程序中添加了以下内容:

NSLog(@"%@", [AVCaptureDevice devices]);

因此要求列出所有可用于捕获音频和/或视频的设备。在没有插入耳机的情况下,我得到:

(
    "Back Camera",
    "Front Camera",
    "iPhone Microphone" 
)

插入耳机后,我得到:

(
    "Back Camera",
    "Front Camera",
    Headphones
)

因此,一旦耳机可用,iPhone 麦克风就会从可用的 AVCaptureDevices 列表中删除。为了进一步探讨这一点,我添加了一些代码来获取可用音频设备的 AVCaptureDevice 实例并打印其唯一 ID。对于将自己标识为“iPhone 麦克风”的设备和将自己标识为“耳机”的设备,我得到了:

com.apple.avfoundation.avcapturedevice.built-in_audio:0

在我看来,两台设备不能具有相同的唯一 ID,这显然是显而易见的,因此很明显这是同一台设备在改变其状态。虽然 AVCaptureDevices 有很多设置状态的东西,但它仅限于视觉方面的东西,比如焦点、曝光、闪光灯和白平衡。

【讨论】:

    【解决方案2】:

    看起来真的不可能。

    我的目标是将输出发送到蓝牙耳机并记录来自它的输入。 据我所知,我最好的选择是:“PlayAndRecord + AllowBluetoothInput”属性 (iphone 4,诺基亚 BH-214 耳机)

    重要的是,根据苹果文档,当音频路由发生变化时,您总是必须重新覆盖您的音频类别!

    这是我的 ROUTE CHANGE LISTENER 方法,打印:RouteChangeReasons, outputRoute, audioRout:

    void RouteChangeListener(void                    *inClientData,
                         AudioSessionPropertyID  inID,
                         UInt32                  inDataSize,
                         const void              *inData) {
    
    
    if (inID == kAudioSessionProperty_AudioRouteChange) {
    
        NSLog(@"]-----------------[ Audio Route Change ]--------------------[");
    
        // ************************************************************************************************
        // Check route change reason **********************************************************************
        // ************************************************************************************************
        CFDictionaryRef routeDict = (CFDictionaryRef)inData;
        NSNumber* reasonValue = (NSNumber*)CFDictionaryGetValue(routeDict, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
        int reason = [reasonValue intValue];
    
        if (reason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
            NSLog(@"] Logic: audio route change reason: OldDeviceUnavailable");
        }else if (reason == kAudioSessionRouteChangeReason_NewDeviceAvailable ) {
            NSLog(@"] Logic: audio route change reason: NewDeviceAvailable");
        }else if (reason == kAudioSessionRouteChangeReason_Unknown ) {
            NSLog(@"] Logic: audio route change reason: Unknown");
        }else if (reason == kAudioSessionRouteChangeReason_CategoryChange ) {
            NSLog(@"] Logic: audio route change reason: CategoryChange");
        }else if (reason == kAudioSessionRouteChangeReason_Override ) {
            NSLog(@"] Logic: audio route change reason: Override");
        }else if (reason == kAudioSessionRouteChangeReason_WakeFromSleep ) {
            NSLog(@"] Logic: audio route change reason: WakeFromSleep");
        }else if (reason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory ) {
            NSLog(@"] Logic: audio route chang reasone: NoSuitableRouteForCategory");
        }
    
        // ************************************************************************************************
        // Check output type ******************************************************************************
        // ************************************************************************************************
        CFDictionaryRef currentRouteDescriptionDictionary = nil;
        UInt32 dataSize = sizeof(currentRouteDescriptionDictionary);
        AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, &currentRouteDescriptionDictionary);
        if (currentRouteDescriptionDictionary) {
            CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs);
            if(CFArrayGetCount(outputs) > 0) {
                CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0);
                CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type);
    
                if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo) ) {                // if Airplay
                    NSLog(@"] Logic: output changed to Airplay");
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothA2DP, 0) == kCFCompareEqualTo) ) {     // if Bluetooth A2DP
                    NSLog(@"] Logic: output changed to A2DP");
    
                    // Mix with others category
                    UInt32 doSetProperty = 1;
                    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
    
                    // Bluetooth support enable
                    UInt32 allowBluetoothInput = 1;
                    AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
    
    
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothHFP, 0) == kCFCompareEqualTo) ) {     // if Bluetooth HFP
                    NSLog(@"] Logic: output changed to HFP");
                    // Mix with others category
                    UInt32 doSetProperty = 1;
                    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
    
                    // Bluetooth support enable
                    UInt32 allowBluetoothInput = 1;
                    AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_LineOut, 0) == kCFCompareEqualTo) ) {           // if Line Out
                    NSLog(@"] Logic: output changed to Line Out");
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_Headphones, 0) == kCFCompareEqualTo) ) {        // if Headphones
                    NSLog(@"] Logic: output changed to Headphone");
    
                    // Mix with others category
                    UInt32 doSetProperty = 1;
                    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
    
                    // Bluetooth support disable
                    UInt32 allowBluetoothInput = 0;
                    AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
    
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInSpeaker, 0) == kCFCompareEqualTo) ) {    // if Built In Speaker
                    NSLog(@"] Logic: output changed to Built In Speaker");
    
                    // Mix with others category
                    UInt32 doSetProperty = 1;
                    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
    
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_USBAudio, 0) == kCFCompareEqualTo) ) {          // if USB audio
                    NSLog(@"] Logic: output changed to USB Audio");
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_HDMI, 0) == kCFCompareEqualTo) ) {              // if HDMI
                    NSLog(@"] Logic: output changed to HDMI");
                }
                else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInReceiver, 0) == kCFCompareEqualTo) ) {   // if Built in Reciever
                    NSLog(@"] Logic: output changed to Built in Reciever");
    
                    // Mix with others category
                    UInt32 doSetProperty = 1;
                    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
                }
                else {                                                                                                          // Unknown audio type
                    NSLog(@"] Logic: WARNING: Unknown audio type: %@",(NSString*)outputType);
                }
            }
    
    
    
        }
    
        // ************************************************************************************************
        // Check audio route ******************************************************************************
        // ************************************************************************************************
        UInt32 routeSize = sizeof(CFStringRef);
        CFStringRef route;
        AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route);
        NSLog(@"] Logic: the audio route is: %@",(NSString*)route);
    
    
        // ************************************************************************************************
        NSLog(@"]--------------------------[  ]-----------------------------[");        
    
    }
    

    }

    【讨论】:

    • 在iOS 8模拟器中,需要将:if(CFArrayGetCount(outputs) > 0)改为:if(outputs && CFArrayGetCount(outputs) > 0)
    【解决方案3】:

    由于苹果从 7.0 开始再次更改音频系统,我将在此处发布更新后的代码:

    #pragma mark Route change listener
    // *********************************************************************************************************
    // *********** Route change listener ***********************************************************************
    // *********************************************************************************************************
    -(void)routeChanged:(NSNotification*)notification {
    
        NSLog(@"]-----------------[ Audio Route Change ]--------------------[");
    
        AVAudioSession *session = [AVAudioSession sharedInstance];
    
        //AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
    
        // Reason
        NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
        switch (reason) {
            case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
                NSLog(@"] Audio Route: The route changed because no suitable route is now available for the specified category.");
                break;
            case AVAudioSessionRouteChangeReasonWakeFromSleep:
                NSLog(@"] Audio Route: The route changed when the device woke up from sleep.");
                break;
            case AVAudioSessionRouteChangeReasonOverride:
                NSLog(@"] Audio Route: The output route was overridden by the app.");
                break;
            case AVAudioSessionRouteChangeReasonCategoryChange:
                NSLog(@"] Audio Route: The category of the session object changed.");
                break;
            case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
                NSLog(@"] Audio Route: The previous audio output path is no longer available.");
                break;
            case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
                NSLog(@"] Audio Route: A preferred new audio output path is now available.");
                break;
            case AVAudioSessionRouteChangeReasonUnknown:
                NSLog(@"] Audio Route: The reason for the change is unknown.");
                break;
            default:
                NSLog(@"] Audio Route: The reason for the change is very unknown.");
                break;
        }
    
        // Output
        AVAudioSessionPortDescription *output = [[session.currentRoute.outputs count]?session.currentRoute.outputs:nil objectAtIndex:0];
        if ([output.portType isEqualToString:AVAudioSessionPortLineOut]) {
            NSLog(@"] Audio Route: Output Port: LineOut");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortHeadphones]) {
            NSLog(@"] Audio Route: Output Port: Headphones");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothA2DP]) {
            NSLog(@"] Audio Route: Output Port: BluetoothA2DP");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
            NSLog(@"] Audio Route: Output Port: BuiltInReceiver");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
            NSLog(@"] Audio Route: Output Port: BuiltInSpeaker");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortHDMI]) {
            NSLog(@"] Audio Route: Output Port: HDMI");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortAirPlay]) {
            NSLog(@"] Audio Route: Output Port: AirPlay");
        }
        else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothLE]) {
            NSLog(@"] Audio Route: Output Port: BluetoothLE");
        }
        else {
            NSLog(@"] Audio Route: Output Port: Unknown: %@",output.portType);
        }
    
        // Input
        AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count] ? session.currentRoute.inputs:nil objectAtIndex:0];
    
        if ([input.portType isEqualToString:AVAudioSessionPortLineIn]) {
            NSLog(@"] Audio Route: Input Port: LineIn");
        }
        else if ([input.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
            NSLog(@"] Audio Route: Input Port: BuiltInMic");
        }
        else if ([input.portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
            NSLog(@"] Audio Route: Input Port: HeadsetMic");
        }
        else if ([input.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
            NSLog(@"] Audio Route: Input Port: BluetoothHFP");
        }
        else if ([input.portType isEqualToString:AVAudioSessionPortUSBAudio]) {
            NSLog(@"] Audio Route: Input Port: USBAudio");
        }
        else if ([input.portType isEqualToString:AVAudioSessionPortCarAudio]) {
            NSLog(@"] Audio Route: Input Port: CarAudio");
        }
        else {
            NSLog(@"] Audio Input Port: Unknown: %@",input.portType);
        }
    
        NSLog(@"]--------------------------[  ]-----------------------------[");
    
    }
    

    记得添加观察者,因为音频会话的委托也已被弃用:

    [[NSNotificationCenter defaultCenter] addObserver: self
                                                     selector: @selector(audioInterruption:)
                                                         name: AVAudioSessionInterruptionNotification
                                                       object: nil];
    

    PS:这里不需要重置类别(和6.0一样)

    【讨论】:

      【解决方案4】:

      这是不可能的,我试着用路由改变的监听器(用 AudioSession)来弄清楚。我的结果是:由于Apple提供的categories,您无法单独配置输入或输出。我尝试 *PlayAndRecord,当我配对蓝牙设备时,路由更改如下:

      old route : HeadsetBT
      new route : SpeakerAndMicrophone
      

      事实上,我的蓝牙不是耳机,只是扬声器......所以对我来说没有解决方案。

      【讨论】:

      • 同样的问题。我的 BT 扬声器和 BT 耳机都显示 BluetoothHFP 而不是 BluetoothA2DP 用于扬声器
      【解决方案5】:

      我非常有信心通过您的应用程序的音频会话可以做到这一点:

      音频会话是中介 在您的应用程序和 iOS 之间。每个 iPhone应用程序正好有一个 音频会话。您将其配置为 表达你的应用程序的音频 意图。首先,你回答一些 关于你想要你的 应用程序行为:

      • 您希望您的应用程序如何 回应中断,例如 打电话?
      • 你打算混合你的 应用程序的声音与来自 其他正在运行的应用程序,还是你 打算让他们沉默吗?
      • 应该怎么做 您的应用程序响应音频 路线改变,例如,当用户 插入或拔出耳机?

      有 手头有答案,您使用音频 会话接口(在 AudioToolbox/AudioServices.h) 到 配置您的音频会话和您的 应用。

      挖掘这些文档:

      告诉我进展如何!

      【讨论】:

      • 重点回答。我将深入研究以上几点,让你们知道什么对我有用。谢谢@defbyte
      【解决方案6】:

      我发现在使用 AirPod pro 耳机并尝试录制语音邮件问候语时,即使耳机已完全连接并正在使用,手机仍使用内置麦克风。

      【讨论】:

        猜你喜欢
        • 2014-01-01
        • 2014-10-21
        • 1970-01-01
        • 1970-01-01
        • 2014-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-31
        相关资源
        最近更新 更多