2016-06-01 6 views
1

Я пытаюсь захватить входной поток данных на выходной блок в звуковом графике, чтобы я мог записать его в файл. Я зарегистрировал функцию обратного вызова для ввода выходного блока (выход по умолчанию) после создания графа как так:Функция обратного вызова блока вывода аудиографа - не может читать данные

AudioComponent comp = AudioComponentFindNext (NULL, &cd); 
if (comp == NULL) { 
    printf ("can't get output unit"); 
    exit (-1); 
} 
CheckError (AudioComponentInstanceNew(comp, &player->outputUnit), 
      "Couldn't open component for outputUnit"); // outputUnit is of type AudioUnit 

// register render callback 
AURenderCallbackStruct input; 
input.inputProc = MyRenderProc; 
input.inputProcRefCon = player; 
CheckError(AudioUnitSetProperty(player->outputUnit, 
           kAudioUnitProperty_SetRenderCallback, 
           kAudioUnitScope_Input, 
           0, 
           &input, 
           sizeof(input)), 
      "AudioUnitSetProperty failed"); 

// initialize unit 
CheckError (AudioUnitInitialize(player->outputUnit),"Couldn't initialize output unit"); 

Функция обратного вызова вызывается, но когда я пытаюсь прочитать поток входных данных от ioData-> mBuffers [ 0] .mData, все, что я получаю, это нули. Вот функция обратного вызова:

OSStatus MyRenderProc(void *inRefCon, // PLAYER CODE 
         AudioUnitRenderActionFlags *ioActionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList * ioData) { 

int frame = 0; 
Float32 leftFloat = 0; 
Float32 rightFloat = 0; 
NSNumber *leftNumber; 
NSNumber *rightNumber; 
for (frame = 0; frame < inNumberFrames; ++frame) 
{ 
    Float32 *data = (Float32*)ioData->mBuffers[0].mData; 
    leftFloat = (data)[frame]; 
    leftNumber = [NSNumber numberWithDouble:leftFloat]; 
    (data)[frame] = 0; 

    // copy to right channel too 
    data = (Float32*)ioData->mBuffers[1].mData; 
    rightFloat = (data)[frame]; 
    rightNumber = [NSNumber numberWithDouble:(data)[frame]]; 
    (data)[frame] = 0; 

    [leftData addObject:leftNumber]; 
    [rightData addObject:rightNumber]; 

} 

return noErr; 
} 

Кроме того, если я не обнулять данные, которые я слышу шум во время воспроизведения, который говорит мне, что я искажая функцию mBuffers. Что я здесь делаю неправильно?

+1

Что делают массивы 'leftData' и' rightData'? Они не объявлены в фрагменте кода там. –

+0

Вы должны либо отображать данные где-нибудь (ввод в вашем случае), либо заполнить буфер чем-то (нули в вашем случае). В остальном все, что осталось в буфере, будет продолжать играть (шум в вашем случае). – user3078414

+0

Michael - leftData и rightData - объекты NSMutableArray, которые я использую только для быстрой доступности данных, чтобы увидеть, работает ли что-либо. –

ответ

0

Если захватив входные данные из AUGraph является задачей, критическая часть кода (более или менее) сводится к этой простейшей одноканальной демонстрационный пример:

OSStatus MyRenderProc(void *inRefCon, 
         AudioUnitRenderActionFlags *ioActionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList * ioData) 
{   
    Float32 buf [inNumberFrames]; //just for one channel! 

    MyMIDIPlayer *player = (MyMIDIPlayer *)inRefCon; 

    if (*ioActionFlags & kAudioUnitRenderAction_PostRender){ 
     static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8); 
     if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){ 

      Float32* data = (Float32 *)ioData->mBuffers[0].mData; //just for one channel! 

      memcpy(buf, data, inNumberFrames*sizeof(Float32)); 

      //do something with buff - there are nice examples of ExtAudioFileWriteAsync() 
     } 
    } 
    return noErr; 
} 

В setupAUGraph() этого обратного вызова можно настроить следующим образом:

void setupAUGraph(MyMIDIPlayer *player) 
{ 
    // the beginning follows the textbook example setup pattern 
    {… … …} 

    // this is the specific part 
    AURenderCallbackStruct input = {0}; 
    input.inputProc = MyRenderProc; 
    input.inputProcRefCon = player->instrumentUnit; 

    CheckError(AudioUnitAddRenderNotify(player->instrumentUnit, 
            MyRenderProc, 
            &player), 
      "AudioUnitAddRenderNotify Failed"); 

    // now initialize the graph (causes resources to be allocated) 
    CheckError(AUGraphInitialize(player->graph), 
      "AUGraphInitialize failed"); 
} 

Обратите внимание, что делают обратный вызов «стучит» соединение между выходом узла прибора и входом выходного узла, захватив WHA t происходит от восходящего потока. Обратный вызов просто копирует ioData в другой буфер, который можно сохранить. AFAIK, это самый простой способ доступа к ioData, который, как я знаю, работает, не нарушая API.

Также обратите внимание, что для проверки, если это работает для конкретной реализации, существуют очень эффективные простые методы C - нет необходимости в методах Objective-C внутри обратного вызова. Запуск с некоторыми NSArray s, добавление объектов и т. Д., Внутри обратного вызова в режиме реального времени C вносит риск проблем с приоритетом, которые впоследствии могут стать трудными для отладки. CoreAudio API написан по ошибке plain-C. В основе выполнения Obj-C много чего не происходит в потоке реального времени, не рискуя глюками (блокировки, управление памятью и т. Д.). Таким образом, было бы безопаснее избегать Obj-C на в реальном времени.
Надеюсь, это поможет.

+0

Да, я понимаю риск использования Objective-C. Здесь просто получить быстрый доступ к данным, чтобы увидеть, работает ли он. –

+0

Я добавил вызов AudioUnitRender() для обратного вызова, но теперь я получаю сообщение об ошибке: (-50) или (-10877) в зависимости от значения, которое я использую для параметра «kInputBus», как вы его называете. Я отлаживаю это сейчас. –

+0

Один вопрос у меня есть, если я могу сделать все это, используя вывод по умолчанию. Книга «Learning Core Audio», по-видимому, предполагает, что мне нужен общий вывод, чтобы получить доступ к данным, хотя он не указывает его явно. –

0

Обратный вызов рендеринга на выходе Аудиоузел не предназначен для записи выходных данных. Он должен предоставлять выходные данные (образец mBuffers) для воспроизводимого блока.

+0

. Тогда нет способа сделать это, используя вывод по умолчанию? Если это так, то как насчет с общим выходом? –

+0

В API «AUGraph» обратные вызовы визуализации используются как соединения между экземплярами компонента «AU». Единственный способ, которым я нашел, может работать (без нарушения API), будет регистрировать _callback_ с помощью механизма AudioUnitAddRenderNotify(), чтобы захватить данные из «восходящего» выхода AU. – user3078414