2016-09-06 5 views
2

Я работаю над проектом iOS и должен записывать вход с микрофона и преобразовывать его в ULaw (для отправки потока данных). Я использую AUGraph с узлом конвертера для достижения этого. Граф создан успешно и инициализирован, однако в моем обратном вызове рендеринга буфера ioData всегда содержит NULL, даже мысль inNumberFrame содержит значение 93. Я думаю, что это может иметь что-то из-за неправильного размера буферов конвертера формата, но я могу что происходит.AUGraph FormatConverter (AUConverter) render notify содержит буфер NULL ioData

Вот код:

OSStatus status; 

// ************************** DEFINE AUDIO STREAM FORMATS ****************************** 

double currentSampleRate; 
currentSampleRate = [[AVAudioSession sharedInstance] sampleRate]; 

// Describe stream format 
AudioStreamBasicDescription streamAudioFormat = {0}; 
streamAudioFormat.mSampleRate    = 8000.00; 
streamAudioFormat.mFormatID    = kAudioFormatULaw; 
streamAudioFormat.mFormatFlags   = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; 
streamAudioFormat.mFramesPerPacket  = 1; 
streamAudioFormat.mChannelsPerFrame  = 1; 
streamAudioFormat.mBitsPerChannel   = 8; 
streamAudioFormat.mBytesPerPacket   = 1; 
streamAudioFormat.mBytesPerFrame   = streamAudioFormat.mBytesPerPacket * streamAudioFormat.mFramesPerPacket; 


// ************************** SETUP SEND AUDIO ****************************** 

AUNode ioSendNode; 
AUNode convertToULAWNode; 
AUNode convertToLPCMNode; 
AudioUnit convertToULAWUnit; 
AudioUnit convertToLPCMUnit; 

status = NewAUGraph(&singleChannelSendGraph); 
if (status != noErr) 
{ 
    NSLog(@"Unable to create send audio graph."); 
    return; 
} 

AudioComponentDescription ioDesc = {0}; 
ioDesc.componentType = kAudioUnitType_Output; 
ioDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 
ioDesc.componentManufacturer = kAudioUnitManufacturer_Apple; 
ioDesc.componentFlags = 0; 
ioDesc.componentFlagsMask = 0; 

status = AUGraphAddNode(singleChannelSendGraph, &ioDesc, &ioSendNode); 
if (status != noErr) 
{ 
    NSLog(@"Unable to add IO node."); 
    return; 
} 

AudioComponentDescription converterDesc = {0}; 
converterDesc.componentType = kAudioUnitType_FormatConverter; 
converterDesc.componentSubType = kAudioUnitSubType_AUConverter; 
converterDesc.componentManufacturer = kAudioUnitManufacturer_Apple; 
converterDesc.componentFlags = 0; 
converterDesc.componentFlagsMask = 0; 

status = AUGraphAddNode(singleChannelSendGraph, &converterDesc, &convertToULAWNode); 
if (status != noErr) 
{ 
    NSLog(@"Unable to add ULAW converter node."); 
    return; 
} 

status = AUGraphAddNode(singleChannelSendGraph, &converterDesc, &convertToLPCMNode); 
if (status != noErr) 
{ 
    NSLog(@"Unable to add LPCM converter node."); 
    return; 
} 

status = AUGraphOpen(singleChannelSendGraph); 
if (status != noErr) 
{ 
    return; 
} 

// get the io audio unit 
status = AUGraphNodeInfo(singleChannelSendGraph, ioSendNode, NULL, &ioSendUnit); 
if (status != noErr) 
{ 
    NSLog(@"Unable to get IO unit."); 
    return; 
} 

UInt32 enableInput = 1; 
status = AudioUnitSetProperty (ioSendUnit, 
           kAudioOutputUnitProperty_EnableIO, 
           kAudioUnitScope_Input, 
           1,  // microphone bus 
           &enableInput, 
           sizeof (enableInput) 
           ); 
if (status != noErr) 
{ 
    return; 
} 

UInt32 sizeASBD = sizeof(AudioStreamBasicDescription); 
AudioStreamBasicDescription ioASBDin; 
AudioStreamBasicDescription ioASBDout; 
status = AudioUnitGetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &ioASBDin, &sizeASBD); 
if (status != noErr) 
{ 
    NSLog(@"Unable to get IO stream input format."); 
    return; 
} 

status = AudioUnitGetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioASBDout, &sizeASBD); 
if (status != noErr) 
{ 
    NSLog(@"Unable to get IO stream output format."); 
    return; 
} 

ioASBDin.mSampleRate = currentSampleRate; 
ioASBDout.mSampleRate = currentSampleRate; 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set IO stream output format."); 
    return; 
} 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioASBDin, sizeof(ioASBDin)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set IO stream input format."); 
    return; 
} 

// get the converter audio unit 
status = AUGraphNodeInfo(singleChannelSendGraph, convertToULAWNode, NULL, &convertToULAWUnit); 
if (status != noErr) 
{ 
    NSLog(@"Unable to get ULAW converter unit."); 
    return; 
} 

status = AudioUnitSetProperty(convertToULAWUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioASBDin, sizeof(ioASBDin)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set ULAW stream input format."); 
    return; 
} 

status = AudioUnitSetProperty(convertToULAWUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamAudioFormat, sizeof(streamAudioFormat)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set ULAW stream output format."); 
    return; 
} 

// get the converter audio unit 
status = AUGraphNodeInfo(singleChannelSendGraph, convertToLPCMNode, NULL, &convertToLPCMUnit); 
if (status != noErr) 
{ 
    NSLog(@"Unable to get LPCM converter unit."); 
    return; 
} 

status = AudioUnitSetProperty(convertToLPCMUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamAudioFormat, sizeof(streamAudioFormat)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set LPCM stream input format."); 
    return; 
} 

status = AudioUnitSetProperty(convertToLPCMUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioASBDin, sizeof(ioASBDin)); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set LPCM stream output format."); 
    return; 
} 

status = AUGraphConnectNodeInput(singleChannelSendGraph, ioSendNode, 1, convertToULAWNode, 0); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set ULAW node input."); 
    return; 
} 

status = AUGraphConnectNodeInput(singleChannelSendGraph, convertToULAWNode, 0, convertToLPCMNode, 0); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set LPCM node input."); 
    return; 
} 

status = AUGraphConnectNodeInput(singleChannelSendGraph, convertToLPCMNode, 0, ioSendNode, 0); 
if (status != noErr) 
{ 
    NSLog(@"Unable to set IO node input."); 
    return; 
} 

status = AudioUnitAddRenderNotify(convertToULAWUnit, &outputULAWCallback, (__bridge void*)self); 
if (status != noErr) 
{ 
    NSLog(@"Unable to add ULAW render notify."); 
    return; 
} 

status = AUGraphInitialize(singleChannelSendGraph); 
if (status != noErr) 
{ 
    NSLog(@"Unable to initialize send graph."); 
    return; 
} 

CAShow (singleChannelSendGraph); 

}

и граф узлов инициализируется как:

Member Nodes: 
node 1: 'auou' 'vpio' 'appl', instance 0x7fd5faf8fac0 O I 
node 2: 'aufc' 'conv' 'appl', instance 0x7fd5fad05420 O I 
node 3: 'aufc' 'conv' 'appl', instance 0x7fd5fad05810 O I 
Connections: 
node 1 bus 1 => node 2 bus 0 [ 1 ch, 44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer] 
node 2 bus 0 => node 3 bus 0 [ 1 ch, 8000 Hz, 'ulaw' (0x0000000C) 8 bits/channel, 1 bytes/packet, 1 frames/packet, 1 bytes/frame] 
node 3 bus 0 => node 1 bus 0 [ 1 ch, 44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer] 

и визуализации уведомляет обратного вызова:

static OSStatus outputULAWCallback(void *inRefCon, 
          AudioUnitRenderActionFlags *ioActionFlags, 
          const AudioTimeStamp *inTimeStamp, 
          UInt32 inBusNumber, 
          UInt32 inNumberFrames, 
          AudioBufferList *ioData) 
{ 
    AudioManager *audioManager = (__bridge AudioManager*)inRefCon; 
    if ((*ioActionFlags) & kAudioUnitRenderAction_PostRender) 
    { 
     if (!audioManager.mute && ioData->mBuffers[0].mData != NULL) 
     { 
      TPCircularBufferProduceBytes(audioManager.activeChannel == 0 ? audioManager.channel1StreamOutBufferPtr : audioManager.channel2StreamOutBufferPtr, 
            ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize); 

      // do not want to playback our audio into local speaker 
      SilenceData(ioData); 
     } 
    } 

    return noErr; 
} 

Примечание: если я посылаю вход микрофона непосредственно на выход (пропуская узлы преобразователя), я слышу выходной сигнал, поэтому я знаю, что AUGraph работает.

У меня есть настройка AUGraph для приема ULaw из потока и запуска через конвертер для воспроизведения через динамики, и это работает без проблем.

Просто не могу понять, почему преобразователь не работает и не возвращает данные.

Имеет ли кто-либо опыт в этом вопросе?

ответ

1

UPDATE
Так что вы звоните AUGraphStart в другом месте, но преобразователь ULAW отказывается делать преобразование общей ставки для вас :(Вы можете добавить еще один преобразователь частоты на графике или просто получить единицу vpio, чтобы сделать это для вас. Изменение этого кода

ioASBDin.mSampleRate = currentSampleRate; // change me to 8000Hz 
ioASBDout.mSampleRate = currentSampleRate; // delete me, I'm ignored 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin)); 

в

ioASBDin.mSampleRate = streamAudioFormat.mSampleRate; // a.k.a 8000Hz 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin)); 

сделает весь граф делать 8KHZ и дать вам ненулевые ioData буферов :

AudioUnitGraph 0xCA51000: 
    Member Nodes: 
    node 1: 'auou' 'vpio' 'appl', instance 0x7b5bb320 O I 
    node 2: 'aufc' 'conv' 'appl', instance 0x7c878d50 O I 
    node 3: 'aufc' 'conv' 'appl', instance 0x7c875eb0 O I 
Connections: 
    node 1 bus 1 => node 2 bus 0 [ 1 ch, 8000 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer] 
    node 2 bus 0 => node 3 bus 0 [ 1 ch, 8000 Hz, 'ulaw' (0x0000000C) 8 bits/channel, 1 bytes/packet, 1 frames/packet, 1 bytes/frame] 
    node 3 bus 0 => node 1 bus 0 [ 1 ch, 8000 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer] 
CurrentState: 
    mLastUpdateError=0, eventsToProcess=F, isInitialized=T, isRunning=T (1) 

старый ответ

Вы должны

  1. AUGraphStart ваш график
  2. Изменить ULAW mSampleRate на 11025, 22050 или 44100

тогда вы будете смотреть не-нуль l ioData в фазе kAudioUnitRenderAction_PostRender.

Преобразование в 8kHz или даже 16kHz ulaw кажется чем-то, что может сделать аудио конвертер. Я понятия не имею, почему это не работает, но когда вы устанавливаете частоту дискретизации на что-либо, кроме значений в пункте 2., конвертер ulaw сообщает об ошибках kAUGraphErr_CannotDoInCurrentContext (-10863), что для меня не имеет смысла.

+0

Вызов AUGraphStart выполнялся в другой части кода, поэтому график работал. Очевидно, мне нужно, чтобы он был преобразован в 8 кГц, поэтому переход на что-либо еще не сработает. – DRourke

+0

Очевидное отношение. Проверьте обновленный ответ. –

+0

Изменение частоты дискретизации на блоке vpio до 8 кГц работает. Вы могли бы подумать, что он использует ту же логику преобразования, которую использует conv unit, но что-то должно быть иначе. Спасибо за вашу помощь! – DRourke

 Смежные вопросы

  • Нет связанных вопросов^_^