2011-08-19 7 views
14

Sto provando a registrare il suono prodotto da un'uscita dell'unità mixer.Come registrare il suono prodotto dall'uscita dell'unità mixer (audio core iOS e grafico audio)

Per il momento, il mio codice è basato sulla apple MixerHost iOS app demo: Un nodo miscelatore è collegato ad un nodo remoto IO sul graphe audio.

E provo a impostare un callback ingresso sul nodo IO remoto ingresso l'uscita del mixer.

Faccio qualcosa di sbagliato ma non riesco a trovare l'errore.

Ecco il codice qui sotto. Questo viene fatto solo dopo che l'unità di installazione multicanale Mixer:

UInt32 flag = 1; 

// Enable IO for playback 
result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 
           0, // Output bus 
           &flag, 
           sizeof(flag)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;} 

/* can't do that because *** AudioUnitSetProperty EnableIO error: -1073752493 00000000 
result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 
           0, // Output bus 
           &flag, 
           sizeof(flag)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;} 
*/ 

quindi creare un formato di flusso:

// I/O stream format 
iOStreamFormat.mSampleRate   = 44100.0; 
iOStreamFormat.mFormatID   = kAudioFormatLinearPCM; 
iOStreamFormat.mFormatFlags   = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
iOStreamFormat.mFramesPerPacket  = 1; 
iOStreamFormat.mChannelsPerFrame = 1; 
iOStreamFormat.mBitsPerChannel  = 16; 
iOStreamFormat.mBytesPerPacket  = 2; 
iOStreamFormat.mBytesPerFrame  = 2; 

[self printASBD: iOStreamFormat]; 

Poi influisce sul formato e specificare la frequenza di campionamento:

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
           1, // Input bus 
           &iOStreamFormat, 
           sizeof(iOStreamFormat)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;} 

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
           0, // Output bus 
           &iOStreamFormat, 
           sizeof(iOStreamFormat)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;} 

// SampleRate I/O 
result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 
           0, // Output 
           &graphSampleRate, 
           sizeof (graphSampleRate)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty (set I/O unit input stream format)" withStatus: result]; return;} 

Poi, i prova ad impostare il callback del rendering.

Soluzione 1 >>> mia richiamata la registrazione non viene mai chiamato

effectState.rioUnit = iOUnit; 

AURenderCallbackStruct renderCallbackStruct; 
renderCallbackStruct.inputProc  = &recordingCallback; 
renderCallbackStruct.inputProcRefCon = &effectState; 
result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 
           0, // Output bus 
           &renderCallbackStruct, 
           sizeof (renderCallbackStruct)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty SetRenderCallback" withStatus: result]; return;} 

Soluzione 2 >>> miei applicazione si blocca al momento del lancio di questa

AURenderCallbackStruct renderCallbackStruct; 
renderCallbackStruct.inputProc  = &recordingCallback; 
renderCallbackStruct.inputProcRefCon = &effectState; 

result = AUGraphSetNodeInputCallback (processingGraph, iONode, 
             0, // Output bus 
             &renderCallbackStruct); 
if (noErr != result) {[self printErrorMessage: @"AUGraphSetNodeInputCallback (I/O unit input callback bus 0)" withStatus: result]; return;} 

se qualcuno ha un'idea ...

MODIFICA Soluzione 3 (grazie a arlo anser) >> V'è ora un problema di formato

AudioStreamBasicDescription dstFormat = {0}; 
dstFormat.mSampleRate=44100.0; 
dstFormat.mFormatID=kAudioFormatLinearPCM; 
dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked; 
dstFormat.mBytesPerPacket=4; 
dstFormat.mBytesPerFrame=4; 
dstFormat.mFramesPerPacket=1; 
dstFormat.mChannelsPerFrame=2; 
dstFormat.mBitsPerChannel=16; 
dstFormat.mReserved=0; 

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
        1, 
        &stereoStreamFormat, 
        sizeof(stereoStreamFormat)); 

if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;} 


result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
        0, 
        &stereoStreamFormat, 
        sizeof(stereoStreamFormat)); 

if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;} 


AudioUnitAddRenderNotify(
         iOUnit, 
         &recordingCallback, 
         &effectState 
         ); 

e la configurazione del file:

if (noErr != result) {[self printErrorMessage: @"AUGraphInitialize" withStatus: result]; return;} 

// On initialise le fichier audio 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
NSString *documentsDirectory = [paths objectAtIndex:0]; 
NSString *destinationFilePath = [[[NSString alloc] initWithFormat: @"%@/output.caf", documentsDirectory] autorelease]; 
NSLog(@">>> %@", destinationFilePath); 
CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)destinationFilePath, kCFURLPOSIXPathStyle, false); 

OSStatus setupErr = ExtAudioFileCreateWithURL(destinationURL, kAudioFileWAVEType, &dstFormat, NULL, kAudioFileFlags_EraseFile, &effectState.audioFileRef); 
CFRelease(destinationURL); 
NSAssert(setupErr == noErr, @"Couldn't create file for writing"); 

setupErr = ExtAudioFileSetProperty(effectState.audioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &stereoStreamFormat); 
NSAssert(setupErr == noErr, @"Couldn't create file for format"); 

setupErr = ExtAudioFileWriteAsync(effectState.audioFileRef, 0, NULL); 
NSAssert(setupErr == noErr, @"Couldn't initialize write buffers for audio file"); 

E il callback di registrazione:

static OSStatus recordingCallback  (void *       inRefCon, 
           AudioUnitRenderActionFlags *  ioActionFlags, 
           const AudioTimeStamp *   inTimeStamp, 
           UInt32       inBusNumber, 
           UInt32       inNumberFrames, 
           AudioBufferList *     ioData) { 
if (*ioActionFlags == kAudioUnitRenderAction_PostRender && inBusNumber == 0) 
{ 
    EffectState *effectState = (EffectState *)inRefCon; 

    ExtAudioFileWriteAsync(effectState->audioFileRef, inNumberFrames, ioData); 
} 
return noErr;  
} 

C'è qualcosa che manca nel file di output output.caf :). Sono totalmente perso nei formati da applicare.

+0

che sto cercando di fare lo stesso bt in grado di attuare codice ur oltre MixerHost esempio può u plz help me .. – Aadil

+0

Hi lefakir e arlomedia, sarebbe possibile che uno di voi possa pubblicare la classe EffectState? Sto cercando di riprodurre il codice funzionante usando MixerHost e quanto sopra. Best, Gregor –

+0

Ciao Gregor, dovresti guardare qui http://stackoverflow.com/questions/7032468/record-sounds-played-by-my-iphone-app-with-audio-units. La struttura EffectState è dichiarata in questa domanda. – lefakir

risposta

15

Non penso sia necessario abilitare l'input sull'unità I/O. Vorrei anche commentare la configurazione del formato e della frequenza di campionamento che si sta facendo sull'unità I/O fino a quando non si esegue la richiamata, perché un formato non corrispondente o non supportato può impedire il collegamento delle unità audio.

Per aggiungere la richiamata, provare questo metodo:

AudioUnitAddRenderNotify(
    iOUnit, 
    &recordingCallback, 
    self 
); 

A quanto pare gli altri metodi sostituirà il collegamento del nodo, ma questo metodo non - in modo che le unità audio possono rimanere in contatto anche se hai aggiunto una richiamata.

Una volta che il callback è in esecuzione, se si scopre che non ci sono dati nel buffer (iodata), avvolgere questo codice intorno al vostro codice di callback:

if (*ioActionFlags == kAudioUnitRenderAction_PostRender) { 
    // your code 
} 

Ciò è necessario perché un callback aggiunto in questo modo viene eseguito sia prima che dopo l'unità audio esegue il rendering dell'audio, ma si desidera eseguire il codice solo dopo il rendering.

Una volta eseguito il callback, il passaggio successivo consiste nel determinare il formato audio che riceve e gestirlo in modo appropriato. Prova ad aggiungere questo alla tua callback:

SInt16 *dataLeftChannel = (SInt16 *)ioData->mBuffers[0].mData; 
for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) { 
    NSLog(@"sample %lu: %d", frameNumber, dataLeftChannel[frameNumber]); 
} 

Questo rallenterà la vostra applicazione, tanto che sarà probabilmente evitare che l'audio da realtà a giocare, ma si dovrebbe essere in grado di farlo funzionare abbastanza a lungo per vedere che cosa i campioni assomigliano. Se il callback riceve audio a 16 bit, i campioni devono essere interi positivi o negativi tra -32000 e 32000. Se i campioni si alternano tra un numero dall'aspetto normale e un numero molto più piccolo, provare invece questo codice nella callback:

SInt32 *dataLeftChannel = (SInt32 *)ioData->mBuffers[0].mData; 
for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) { 
    NSLog(@"sample %lu: %ld", frameNumber, dataLeftChannel[frameNumber]); 
} 

Questo dovrebbe mostrare i campioni 8.24 completi.

Se è possibile salvare i dati nel formato in cui viene ricevuta la richiamata, è necessario disporre di ciò che è necessario. Se è necessario salvarlo in un formato diverso, dovresti essere in grado di convertire il formato nell'unità audio I/O remoto ... ma io haven't been able to figure out how to do that quando è collegato a un'unità Mixer multicanale. In alternativa, è possibile convertire i dati utilizzando Audio Converter Services. In primo luogo, definire i formati di input e di output:

AudioStreamBasicDescription monoCanonicalFormat; 
size_t bytesPerSample = sizeof (AudioUnitSampleType); 
monoCanonicalFormat.mFormatID   = kAudioFormatLinearPCM; 
monoCanonicalFormat.mFormatFlags  = kAudioFormatFlagsAudioUnitCanonical; 
monoCanonicalFormat.mBytesPerPacket = bytesPerSample; 
monoCanonicalFormat.mFramesPerPacket = 1; 
monoCanonicalFormat.mBytesPerFrame  = bytesPerSample; 
monoCanonicalFormat.mChannelsPerFrame = 1; 
monoCanonicalFormat.mBitsPerChannel = 8 * bytesPerSample; 
monoCanonicalFormat.mSampleRate  = graphSampleRate; 

AudioStreamBasicDescription mono16Format; 
bytesPerSample = sizeof (SInt16); 
mono16Format.mFormatID   = kAudioFormatLinearPCM; 
mono16Format.mFormatFlags  = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
mono16Format.mChannelsPerFrame = 1; 
mono16Format.mSampleRate  = graphSampleRate; 
mono16Format.mBitsPerChannel = 16; 
mono16Format.mFramesPerPacket = 1; 
mono16Format.mBytesPerPacket = 2; 
mono16Format.mBytesPerFrame = 2; 

Poi definire un convertitore da qualche parte al di fuori di richiamata, e creare un buffer temporaneo per la gestione dei dati durante la conversione:

AudioConverterRef formatConverterCanonicalTo16; 
@property AudioConverterRef formatConverterCanonicalTo16; 
@synthesize AudioConverterRef; 
AudioConverterNew(
    &monoCanonicalFormat, 
    &mono16Format, 
    &formatConverterCanonicalTo16 
); 

SInt16 *data16; 
@property (readwrite) SInt16 *data16; 
@synthesize data16; 
data16 = malloc(sizeof(SInt16) * 4096); 

Quindi aggiungere questo alla vostra callback , prima di salvare i vostri dati:

UInt32 dataSizeCanonical = ioData->mBuffers[0].mDataByteSize; 
SInt32 *dataCanonical = (SInt32 *)ioData->mBuffers[0].mData; 
UInt32 dataSize16 = dataSizeCanonical; 

AudioConverterConvertBuffer(
    effectState->formatConverterCanonicalTo16, 
    dataSizeCanonical, 
    dataCanonical, 
    &dataSize16, 
    effectState->data16 
); 

Quindi è possibile salvare DATO16, che è in formato a 16 bit e potrebbe essere quello che vuoi salvato nel file. Sarà più compatibile e grande la metà dei dati canonici.

Quando hai finito, è possibile ripulire un paio di cose:

AudioConverterDispose(formatConverterCanonicalTo16); 
free(data16); 
+0

grazie! il mio callback è gestito ma sto registrando il rumore! – lefakir

+0

Ho appena aggiornato la mia risposta per includere i passaggi successivi ora che è in esecuzione la richiamata. – arlomedia

+0

Ho appena modificato la mia domanda per fornire più codice di quello che sto cercando di fare – lefakir

Problemi correlati