2012-04-11 14 views
7

He intentado grabar desde una unidad RemoteIO directamente a AAC en un renderCallback en iOS 5 en un iPad 2. He visto información conflictiva diciendo que no es posible & que es posible (en los comentarios here). Mi razón para querer hacerlo es porque la grabación en PCM requiere tanto espacio en disco para una grabación de cualquier longitud, incluso si luego se convierte en AAC.Grabación en AAC desde RemoteIO: se están escribiendo datos pero no se pueden reproducir archivos

Estoy a punto de darme por vencido. Recorrí Google, SO, el libro Core Audio y la lista de correo de Apple Core-Audio & foros y llegué al punto en el que no recibo ningún error, y estoy grabando algo en el disco, pero el archivo resultante no se puede reproducir . Este es el caso tanto con el simulador como con el dispositivo.

Entonces ... si alguien tiene experiencia con esto, realmente agradecería un empujón en la dirección correcta. La configuración es que RemoteIO reproduce la salida de AUSamplers & que está funcionando bien.

Aquí es lo que estoy haciendo en el código de abajo

  • Especificar las AudioStreamBasicDescription formatos para la unidad remoteIO a kAudioFormatLinearPCM

  • Crear y especificar el formato de destino para el ExtAudioFileRef Especificar el cliente formato obteniéndolo de la unidad RemoteIO

  • Especifique la renderCallback f o la unidad RemoteID

  • En el renderCallback, escribir datos en el kAudioUnitRenderAction_PostRender fase

Como ya he dicho, no estoy recibiendo ningún error, y los tamaños de los archivos de audio resultantes muestran algo que se está escribiendo, pero el archivo no se puede reproducir. Quizás tengo mis formatos estropeados?

De todos modos, este es mi mensaje en una botella y/o bandera de "Be Here Dragons" para cualquier persona que desafíe las aguas oscuras de Core-Audio.


// El msg infeliz consigo al intentar reproducir el archivo:

enter image description here

// parte de la instalación remoteIO

// Enable IO for recording 

UInt32 flag = 1; 
result = AudioUnitSetProperty(ioUnit, 
           kAudioOutputUnitProperty_EnableIO, 
           kAudioUnitScope_Input, 
           kInputBus, // == 1 
           &flag, 
           sizeof(flag)); 
if (noErr != result) {[self printErrorMessage: @"Enable IO for recording" withStatus: result]; return;} 

// Describe format - - - - - - - - - - 
size_t bytesPerSample = sizeof (AudioUnitSampleType); 
AudioStreamBasicDescription audioFormat; 
memset(&audioFormat, 0, sizeof(audioFormat)); 
audioFormat.mSampleRate = 44100.00; 
audioFormat.mFormatID  = kAudioFormatLinearPCM; 
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
audioFormat.mFramesPerPacket = 1; 
audioFormat.mChannelsPerFrame = 1; 
audioFormat.mBitsPerChannel = 16; 
audioFormat.mBytesPerPacket = 2; 
audioFormat.mBytesPerFrame = 2; 

result = AudioUnitSetProperty(ioUnit, 
           kAudioUnitProperty_StreamFormat, 
           kAudioUnitScope_Output, 
           kInputBus, // == 1 
           &audioFormat, 
           sizeof(audioFormat)); 


result = AudioUnitSetProperty(ioUnit, 
           kAudioUnitProperty_StreamFormat, 
           kAudioUnitScope_Input, 
           kOutputBus, // == 0 
           &audioFormat, 
           sizeof(audioFormat)); 

// Función que configura el archivo & rendercallback

- (void)startRecordingAAC 
{ 

OSStatus result; 

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
NSString *documentsDirectory = [paths objectAtIndex:0]; 
NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"]; 

CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 
                 (__bridge CFStringRef)recordFile, 
                 kCFURLPOSIXPathStyle, 
                 false); 

AudioStreamBasicDescription destinationFormat; 
memset(&destinationFormat, 0, sizeof(destinationFormat)); 
destinationFormat.mChannelsPerFrame = 2; 
destinationFormat.mFormatID = kAudioFormatMPEG4AAC; 
UInt32 size = sizeof(destinationFormat); 
result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);   
if(result) printf("AudioFormatGetProperty %ld \n", result); 


result = ExtAudioFileCreateWithURL(destinationURL, 
            kAudioFileM4AType, 
            &destinationFormat, 
            NULL, 
            kAudioFileFlags_EraseFile, 
            &extAudioFileRef); 
if(result) printf("ExtAudioFileCreateWithURL %ld \n", result); 

AudioStreamBasicDescription clientFormat; 
memset(&clientFormat, 0, sizeof(clientFormat)); 


result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size); 
if(result) printf("AudioUnitGetProperty %ld \n", result); 

result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat),&clientFormat); 
if(result) printf("ExtAudioFileSetProperty %ld \n", result); 

result = ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL); 
if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];} 

result = AudioUnitAddRenderNotify(ioUnit, renderCallback, (__bridge void*)self); 
if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];} 
} 

// Y finalmente, el rendercallback

static OSStatus renderCallback (void *      inRefCon, 
          AudioUnitRenderActionFlags * ioActionFlags, 
          const AudioTimeStamp *  inTimeStamp, 
          UInt32      inBusNumber, 
          UInt32      inNumberFrames, 
          AudioBufferList *   ioData) 
{ 

OSStatus result; 
if (*ioActionFlags == kAudioUnitRenderAction_PostRender){ 
    MusicPlayerController* THIS = (__bridge MusicPlayerController *)inRefCon; 

     result = ExtAudioFileWriteAsync(THIS->extAudioFileRef, inNumberFrames, ioData); 
     if(result) printf("ExtAudioFileWriteAsync %ld \n", result); 

} 
return noErr; 
} 

Respuesta

14

¡Así que finalmente resolví esto! Uf, qué búsqueda de información del tesoro.

De todos modos, aquí está el bit en los documentos para ExtAudioFile que me perdí (ver texto en negrita). No estaba estableciendo esta propiedad. Se estaban escribiendo datos en mi archivo .m4a pero no se podían leer en la reproducción. Para resumir: tengo un montón de AUSamplers -> AUMixer -> RemoteIO. Una devolución de llamada de representación en la instancia de RemoteIO escribe los datos en el disco en un formato m4a comprimido. Por lo tanto, es posible generar audio comprimido sobre la marcha (iOS 5/iPad 2)

Parece bastante robusto: tuve algunas declaraciones de printf en el rendercallback y la escritura funcionó bien.

¡Yay!

ExtAudioFileProperty_CodecManufacturer El fabricante del códec para ser utilizado por el objeto de archivo de audio extendida. El valor es un UInt32 de lectura/escritura. Debe especificar esta propiedad antes de configurar la propiedad kExtAudioFileProperty_ClientDataFormat (página 20), que a su vez activará la creación del códec. Utilice esta propiedad en iOS para elegir entre un codificador de hardware o software, especificando kAppleHardwareAudioCodecManufacturer o kAppleSoftwareAudioCodecManufacturer. Disponible en Mac OS X v10.7 y posterior. declarado en ExtendedAudioFile.h.

// specify codec 
UInt32 codec = kAppleHardwareAudioCodecManufacturer; 
size = sizeof(codec); 
result = ExtAudioFileSetProperty(extAudioFileRef, 
           kExtAudioFileProperty_CodecManufacturer, 
           size, 
           &codec); 

if(result) printf("ExtAudioFileSetProperty %ld \n", result); 
+0

Muchas gracias hombre! ¡Acabo de grabar directamente en AAC (m4a) trabajando! –

+0

La única parte en la que me quedé atascado fue establecer kExtAudioFileProperty_ClientDataFormat. Tenía que ver con el entrelazado (El sonido era audible pero con muchas grietas). Mi audioFormat (formato de los búferes de entrada) está intercalado y necesitaba establecer el kExtAudioFileProperty_ClientDataFormat exactamente en AudioFormat. –

+0

@skinnyTOD Esto funcionó.Muchas gracias, pero, cada dos o tres veces que llamo a la función startRecordingAAC, obtengo un error EXC_BAD_ACCESS (código = 1, dirección = 0x2f8) y la aplicación se cierra. ¿Alguna idea de cómo puedo resolver este problema? Gracias por adelantado. –

1

qué escribió la cookie mágica requerido al comienzo de un archivo de audio mpg 4?

También necesita hacer al menos el primer archivo de escritura fuera de la devolución de llamada de la unidad de audio.

Agregado:

que se podía tirar y cierre el archivo de audio adecuadamente al final? (fuera de la devolución de llamada AU)

+0

gracias por la respuesta. No, no escribí la cookie, no sabía que tenía que hacerlo. He usado TPAACAudioConverter (https://github.com/michaeltyson/TPAACAudioConverter) y funciona bien (aunque no en tiempo real) y no utiliza una cookie. Confuso. –

+0

Los documentos para ExtAudioFileWriteAsync dicen que tiene que hacer una última cosa después del final de la grabación. – hotpaw2

+0

Resuelto - No estaba especificando el fabricante del códec. Gracias por el empujón para mirar los documentos de ExtAudio de nuevo. Publicará el código como una respuesta –

0

Parece que hay un poco más a esto que acaba de especificar el códec para el ID de archivo de audio de salida.

De acuerdo con este hilo: ExtAudioFileWrite to m4a/aac failing on dual-core devices (ipad 2, iphone 4s) ciertos modelos de iPhone (4S), por ejemplo, no juegan bien con el decodificador de hardware.

Desde mi experiencia con un 4S, intentar codificar un archivo AAC con el códec de hardware ya sea a) funciona a veces, b) falla con un error -66567 (kExtAudioFileError_MaxPacketSizeUnknown) oc) escribe algunas muestras y luego simplemente cuelga sin ningún error detectable

El códec de software funciona bien en un iPhone 4S a expensas de un rendimiento más lento.

Editar: Algunos afirman que al códec de hardware no le gusta la frecuencia de muestreo de 44.1 kHz. Todavía tengo que confirmar esto.

Cuestiones relacionadas