2012-01-21 4 views
5

Я пытаюсь написать (что должно быть) простое приложение, которое имеет последовательность аудиоустройств последовательно в AUGraph, а затем записывает вывод в файл. Я добавил обратный вызов с помощью AUGraphAddRenderNotify. Вот моя функция обратного вызова:Как написать вывод AUGraph в файл?

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

Этот вид работ. Файл воспроизводится, и я могу слышать, что я записал, но есть ужасное количество статичности, которое делает его едва слышимым.

Кто-нибудь знает, что не так с этим? Или кто-нибудь знает, как лучше записать вывод AUGraph в файл?

Спасибо за помощь.

+0

Соответствуют ли форматы вашего AUGraph и ExtAudioFile (формат данных клиента)? Кроме того, вам нужно записывать в режиме реального времени? – sbooth

+0

Я думаю, они совпадают. Я использовал тот же AudioStreamBasicDescription, чтобы настроить все аудиоустройства и создать файл. Я не уверен, как на самом деле проверить. Вы знаете, как это сделать? Думаю, я посмотрю. Думаю, мне не нужно записывать в реальном времени, но есть ли другой способ сделать это? Если я не сохраню данные, где-то в режиме реального времени, то он будет потерян. – HowsItStack

+0

Я верю, что вместо вызова 'AUGraphStart' вы хотите называть' AudioUnitRender' несколько раз на голове графика. Я думаю, что проблема с этим в реальном времени заключается в том, что внутренний кольцевой буфер ExtAudioFile заполняет и теряет данные. – sbooth

ответ

6

У меня было прозрение в отношении Audio Units только сейчас, которые помогли мне решить мою собственную проблему. У меня было неправильное представление о том, как работают соединения аудиоустройств и обратные вызовы рендеринга. Я думал, что они совершенно разные вещи, но оказывается, что соединение - это просто короткая рука для обратного вызова рендеринга.

Ведение kAudioUnitProperty_MakeConnection из выходных звукового устройства А на вход звукового устройства В таком же, как делает kAudioUnitProperty_SetRenderCallback на входе блок B и имеющая функцию обратного вызова вызова AudioUnitRender на выходе звукового блока А.

Я проверил это, выполнив соединение make после установки обратного вызова рендеринга, и обратный вызов визуализации больше не вызывался.

Таким образом, я был в состоянии решить мою проблему, выполнив следующие действия:

И их моя функция обратного вызова сделал что-то вроде этого:

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

Это, вероятно, должно было быть очевидным для меня но так как это было не так, я буду держать пари, есть и другие, которые были смущены таким же образом, поэтому, надеюсь, это тоже полезно для них.

Я все еще не уверен, почему у меня были проблемы с обратным вызовом AUGraphAddRenderNotify. Я углубится в это позже, но на данный момент я нашел решение, которое, похоже, работает.

+0

Действительно ли работает этот код? Я бы подумал, что это вызовет ошибку, так как вы установите refCon на «я», а затем в обратном вызове добавьте его как AUMixer. –

+0

Вы правы. Я немного упростил код из того, что есть в моем проекте. Я прошел сам, а затем получил доступ к mixerUnit через собственность. Я обновил свой пост, чтобы передать в mixerUnit, который является модулем перед выводом, выход которого я хотел записать в файл, прежде чем передавать его в выходной блок. – HowsItStack

+0

Хорошо, интересно. Вам может быть интересно мое решение для сохранения вывода графика в файл - помещение renderCallback на экземпляр remoteIO, а затем получение отображаемых данных на этапе post_Render. Не знаю, является ли это «хорошим» способом сделать это, но он работает. Также было сохранено сохранение в режиме сжатия сжатого формата ACC (yay!): Http://stackoverflow.com/questions/10113977/recording-to-aac-from-remoteio-data-is-getting-written-but-file-unplayable –

0

Возможно, попробуйте это. Скопируйте данные из обратного вызова аудиоустройства в длинный буфер. Проиграйте буфер, чтобы проверить его, затем напишите весь буфер в файл после того, как вы подтвердите, что весь буфер в порядке.

1

Вот некоторые примеры кода от компании Apple (проект PlaySequence, но это не MIDI конкретных), которые могут помочь:

{ 
    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); 
    } 
} 
Смежные вопросы