2012-01-21 2 views
5

Sto provando a scrivere (cosa dovrebbe essere) una semplice app che ha un gruppo di unità audio in sequenza in un AUGraph e quindi scrive l'output in un file. Ho aggiunto un callback usando AUGraphAddRenderNotify. Ecco la mia funzione di callback:Come scrivere l'output di AUGraph in un file?

OSStatus MyAURenderCallback(void *inRefCon, 
         AudioUnitRenderActionFlags *actionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList *ioData) { 
    if (*actionFlags & kAudioUnitRenderAction_PostRender) { 
     ExtAudioFileRef outputFile = (ExtAudioFileRef)inRefCon; 
     ExtAudioFileWriteAsync(outputFile, inNumberFrames, ioData); 
    } 
} 

Questo tipo di opere. Il file è riproducibile e riesco a sentire ciò che ho registrato, ma c'è una quantità orribile di elettricità statica che la rende appena udibile.

Qualcuno sa cosa c'è di sbagliato in questo? O qualcuno sa di un modo migliore per registrare l'output di AUGraph in un file?

Grazie per l'aiuto.

+0

I formati di AUGraph e ExtAudioFile (formato dati client) corrispondono? Inoltre, hai bisogno di registrare in tempo reale? – sbooth

+0

Penso che corrispondano. Ho usato lo stesso AudioStreamBasicDescription per configurare tutte le unità audio e creare il file. Non sono sicuro di come effettivamente controllare però. Sai come farlo? Immagino che lo esaminerò. Immagino di non dover registrare in tempo reale, ma esiste un altro modo per farlo? Se non salvi i dati alcuni in tempo reale, allora è perso. – HowsItStack

+0

Credo invece di chiamare 'AUGraphStart' per chiamare' AudioUnitRender' ripetutamente sulla testa del grafico. Penso che il problema con farlo in tempo reale sia che il buffer circolare interno di 'ExtAudioFile' si riempie e perda i dati. – sbooth

risposta

6

ho avuto un'epifania per quanto riguarda Audio Units solo ora che mi ha aiutato risolvi il mio problema Avevo un'idea sbagliata su come funzionano le connessioni delle unità audio e il rendering dei callback. Ho pensato che fossero cose completamente separate ma si scopre che una connessione è solo una scorciatoia per una richiamata di rendering.

Facendo un kAudioUnitProperty_MakeConnection dall'uscita dell'unità audio A all'ingresso dell'unità audio B è lo stesso facendo kAudioUnitProperty_SetRenderCallback sull'ingresso dell'unità B ed avente la chiamata AudioUnitRender funzione di richiamata sull'uscita dell'unità audio A.

Ho provato questo facendo una connessione make dopo aver impostato il mio callback di rendering e il callback del rendering non era più invocato.

Pertanto, sono riuscito a risolvere il mio problema nel modo seguente:

E loro la mia funzione di callback fatto qualcosa di simile:

OSStatus MyAURenderCallback(void *inRefCon, 
         AudioUnitRenderActionFlags *actionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList *ioData) { 

    AudioUnit mixerUnit = (AudioUnit)inRefCon; 

    AudioUnitRender(mixerUnit, 
        actionFlags, 
        inTimeStamp, 
        0, 
        inNumberFrames, 
        ioData); 

    ExtAudioFileWriteAsync(outputFile, 
          inNumberFrames, 
          ioData); 

    return noErr; 
} 

Questo probabilmente avrebbe dovuto essere ovvio per me ma dato che non lo era, scommetto che ci sono altri che sono stati confusi allo stesso modo, quindi spero che questo sia utile anche a loro.

Non sono ancora sicuro del motivo per cui ho avuto problemi con il callback AUGraphAddRenderNotify. Approfondirò questo più tardi ma per ora ho trovato una soluzione che sembra funzionare.

+0

Il codice sopra riportato funziona davvero? Penserei che genererebbe un errore dal momento che hai impostato il refCon su "self" e poi nel callback lo ha lanciato come AUMixer. –

+0

Sei corretto. Ho semplificato leggermente il codice da ciò che è nel mio progetto. Sono passato in self e quindi ho avuto accesso a mixerUnit attraverso una proprietà. Ho aggiornato il mio post per passare in mixerUnit che è l'unità prima dell'unità di output di cui volevo scrivere l'output prima di passarlo all'unità di output. – HowsItStack

+0

Ok, mi stavo chiedendo. Potresti essere interessato alla mia soluzione per il salvataggio dell'output del grafo su file - mettendo un renderCallback sull'istanza remoteIO e poi ottenendo i dati renderizzati nella fase 'post_Render'. Non so se questo è un modo "buono" per farlo, ma funziona. Ho anche ottenuto il salvataggio dal vivo in formato compresso ACC (yay!): Http://stackoverflow.com/questions/10113977/recording-to-aac-from-remoteio-data-is-getting-written-but-file-unplayable –

0

Forse prova questo. Copia i dati dal callback dell'unità audio a un buffer lungo. Riprodurre il buffer per testarlo, quindi scrivere l'intero buffer su un file dopo aver verificato che l'intero buffer sia OK.

1

Ecco alcuni esempi di codice da parte di Apple (il progetto è PlaySequence, ma non è specifica MIDI) che potrebbe aiutare:

{ 
    CAStreamBasicDescription clientFormat = CAStreamBasicDescription(); 
    ca_require_noerr (result = AudioUnitGetProperty(outputUnit, 
                kAudioUnitProperty_StreamFormat, 
                kAudioUnitScope_Output, 0, 
                &clientFormat, &size), fail); 
    size = sizeof(clientFormat); 
    ca_require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail); 

    { 
     MusicTimeStamp currentTime; 
     AUOutputBL outputBuffer (clientFormat, numFrames); 
     AudioTimeStamp tStamp; 
     memset (&tStamp, 0, sizeof(AudioTimeStamp)); 
     tStamp.mFlags = kAudioTimeStampSampleTimeValid; 
     int i = 0; 
     int numTimesFor10Secs = (int)(10./(numFrames/srate)); 
     do { 
      outputBuffer.Prepare(); 
      AudioUnitRenderActionFlags actionFlags = 0; 
      ca_require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail); 

      tStamp.mSampleTime += numFrames; 

      ca_require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail);  

      ca_require_noerr (result = MusicPlayerGetTime (player, &currentTime), fail); 
      if (shouldPrint && (++i % numTimesFor10Secs == 0)) 
       printf ("current time: %6.2f beats\n", currentTime); 
     } while (currentTime < sequenceLength); 
    } 
}