Forzando el micrófono del iPhone como input de audio

Estoy desarrollando una aplicación de iOS (específica para iPhone, actualmente) que requiere que la aplicación grabe audio solo desde el micrófono interno del iPhone (incluso cuando los auriculares / auriculares están enchufados) y la reproducción en los auriculares (supongamos que los auriculares están conectados por ahora).

Me pregunto si esto es posible actualmente con las API disponibles. Si es así, ¿alguien puede arrojar algo de luz sobre cómo hago para hacer esto?

¡Gracias!

Creo que la respuesta a esta pregunta es 'no'. Utilicé un iPhone 4 y la nueva AVFoundation 4 para experimentar, centrándome en la class AVCaptureDevice .

Agregué lo siguiente a una aplicación:

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

Entonces, se pide que se enumeren todos los dispositivos que pueden ser utilizados para capturar audio y / o video. Sin los auriculares enchufados, obtengo:

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

Con los auriculares enchufados obtengo:

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

Entonces, el micrófono del iPhone cae de la list de AVCaptureDevices disponibles tan pronto como los auriculares estén disponibles. Para seguir investigando esto, agregué un código rápido para tomar la instancia de AVCaptureDevice para el dispositivo de audio disponible e imprimir su identificación única. Tanto para el dispositivo que se identificó como "iPhone Microphone" y el dispositivo se identificó como "Headphones", obtuve:

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

Me parece obvio que dos dispositivos no pueden tener la misma ID única, por lo que claramente es el mismo dispositivo que cambia su estado. Aunque AVCaptureDevices tiene muchas cosas para establecer el estado, está limitado a elementos visuales como enfoque, exposition, flash y balance de blancos.

Parece que realmente no es posible.

Mi objective es enviar salida a los auriculares bluetooth y registrar la input de él también. Hasta donde puedo ver, mis mejores opciones son: propiedad "PlayAndRecord + AllowBluetoothInput" (iphone 4, auriculares Nokia BH-214)

¡IMPORTANTE es que de acuerdo con la documentation de Apple, siempre tendrá que re-anular su categoría de audio cuando cambie la ruta de audio!

ESTO ES MI MÉTODO DE LISTADO DE CAMBIO DE RUTA, que imprime: 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(@"]--------------------------[ ]-----------------------------["); } 

}

Desde que Apple cambió de nuevo el sistema de audio a partir del 7.0, voy a publicar el código de actualización aquí:

 #pragma mark Route change listener // ********************************************************************************************************* // *********** Route change listener *********************************************************************** // ********************************************************************************************************* -(void)routeChanged:(NSNotification*)notification { NSLog(@"]-----------------[ Audio Route Change ]--------------------["); AVAudioSession *session = [AVAudioSession shanetworkingInstance]; //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 prefernetworking 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(@"]--------------------------[ ]-----------------------------["); } 

Recuerde agregar un observador ya que el delegado de la session de audio también está en desuso:

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

PS: no necesita restablecer la categoría aquí (como en 6.0)

Estoy bastante seguro de que esto es posible a través de la session de Audio de su aplicación:

Una session de audio es un intermediario entre su aplicación y iOS. Cada aplicación de iPhone tiene exactamente una session de audio. Lo configura para express las intenciones de audio de su aplicación. Para comenzar, respondes algunas preguntas sobre cómo quieres que se comporte tu aplicación:

  • ¿Cómo desea que su aplicación responda a las interrupciones, como una llamada telefónica?
  • ¿Tiene la intención de mezclar los sonidos de su aplicación con los de otras aplicaciones en ejecución, o tiene la intención de silenciarlos?
  • ¿Cómo debería su aplicación responder a un cambio de ruta de audio, por ejemplo, cuando un usuario conecta o desconecta un auricular?

Con las respuestas disponibles, utiliza la interfaz de session de audio (declarada en AudioToolbox / AudioServices.h) para configurar su session de audio y su aplicación.

Inserta estos documentos:

  • Descripción general del audio básico: sesiones de audio: queueborar con Core Audio
  • Progtwigción de la session de audio
  • Progtwigción de la session de audio: event handling cambios de ruta de hardware de audio

¡Y dime cómo va!

No es posible, trato de averiguar con el oyente cambiado por la ruta (con AudioSession). Mi resultado es: no se puede configurar la input o salida por separado debido a las categorías proporcionadas por Apple. Intento * PlayAndRecord, cuando emparejo un dispositivo bluetooth, cambio de ruta como este:

 old route : HeadsetBT new route : SpeakerAndMicrophone 

De hecho, mi bluetooth no es un auricular, solo altavoces … Entonces, para mí no hay solución.