2012-01-21 28 views
5

Próbuję napisać (co powinno być) prostą aplikację, która ma kilka jednostek audio w sekwencji w AUGraph, a następnie zapisuje dane wyjściowe do pliku. Dodałem wywołanie zwrotne za pomocą AUGraphAddRenderNotify. Oto moja funkcja zwrotna:Jak zapisać wyjście pliku AUGraph do pliku?

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); 
    } 
} 

Tego rodzaju prace. Plik można odtwarzać i słyszę to, co nagrałem, ale jest tam strasznie dużo szumu, który sprawia, że ​​jest ledwo słyszalny.

Czy ktoś wie, co jest nie tak z tym? A może ktoś wie o lepszym sposobie zapisywania wyjścia AUGraph do pliku?

Dzięki za pomoc.

+0

Czy formaty plików AUGraph i ExtAudioFile (dane klienta) są zgodne? Czy musisz nagrywać w czasie rzeczywistym? – sbooth

+0

Myślę, że pasują. Użyłem tego samego AudioStreamBasicDescription, aby skonfigurować wszystkie jednostki audio i utworzyć plik. Nie jestem pewien, jak to sprawdzić. Czy wiesz, jak to zrobić? Chyba się nad tym zastanowię. Zgaduję, że nie muszę nagrywać w czasie rzeczywistym, ale czy jest jakiś inny sposób na zrobienie tego? Jeśli nie zapiszę danych w niektórych miejscach w czasie rzeczywistym, to zostaną utracone. – HowsItStack

+0

Wierzę, zamiast wywoływać 'AUGraphStart', który chcesz nazwać' AudioUnitRender' wielokrotnie na początku wykresu. Myślę, że problem z robieniem tego w czasie rzeczywistym polega na tym, że wewnętrzny bufor pierścieniowy ExtAudioFile napełnia się i traci dane. – sbooth

Odpowiedz

6

miałem objawienie w odniesieniu do jednostek audio właśnie co pomogło mi rozwiązać mój własny problem. Miałem błędne przekonanie o tym, jak działają połączenia jednostek audio i wywołań renderowania. Myślałem, że są to całkowicie oddzielne rzeczy, ale okazuje się, że połączenie jest tylko krótką ręką dla wywołania renderowania.

Robi kAudioUnitProperty_MakeConnection od wyjścia z sytemu audio A do wejścia jednostki dźwięku B jest taka sama jak robi kAudioUnitProperty_SetRenderCallback na wejściu urządzenia B i posiadające funkcję callback rozmowę AudioUnitRender na wyjściu z sytemu audio A.

Przetestowałem to, wykonując połączenie make po ustawieniu mojego wywołania renderowania, a wywołanie renderowania nie było już wywoływane.

Dlatego udało mi się rozwiązać mój problem, wykonując następujące czynności:

je i moja funkcja zwrotna zrobił coś takiego:

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; 
} 

To chyba powinno być oczywiste dla mnie ale ponieważ tak nie było, założę się, że są inni, którzy byli zdezorientowani w ten sam sposób, więc mam nadzieję, że jest to również pomocne dla nich.

Nadal nie jestem pewien, dlaczego miałem problemy z wywołania zwrotnego AUGraphAddRenderNotify. Pogłębię to później, ale na razie znalazłem rozwiązanie, które wydaje się działać.

+0

Czy powyższy kod rzeczywiście działa? Myślę, że spowodowałoby to błąd, od kiedy ustawisz refcon na "self", a następnie w callbacku użyjesz go jako AUMixer. –

+0

Masz rację. Uproszczono nieco kod z tego, co jest w moim projekcie. Zdałem osobiście, a następnie przejrzałem mikserUnit przez właściwość. Zaktualizowałem swój post do przekazania w mixerUnit, który jest jednostką przed jednostką wyjściową, której wyjście chciałem zapisać do pliku przed przekazaniem go do jednostki wyjściowej. – HowsItStack

+0

Ok- zastanawiałem się. Być może zainteresuje mnie moje rozwiązanie do zapisywania danych wyjściowych wykresu do pliku - umieszczenie obiektu renderCallback na instancji remoteIO, a następnie uzyskanie renderowanych danych na etapie "post_Render". Nie wiem, czy jest to "dobry" sposób, ale działa. Również zapisywanie na żywo do formatu skompresowanego ACC działa (yay!): Http://stackoverflow.com/questions/10113977/recording-to-aac-from-remoteio-data-is-getting-written-but-file-unplayable –

0

Może spróbuj tego. Skopiuj dane z wywołania zwrotnego urządzenia audio do długiego bufora. Odtwórz bufor, aby go przetestować, a następnie zapisz cały bufor do pliku po sprawdzeniu, czy cały bufor jest w porządku.

1

Oto przykładowy kod z Apple (projekt jest PlaySequence, ale to nie jest specyficzne MIDI), które mogą pomóc:

{ 
    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); 
    } 
}