AUGraph使用RemoteIO和MultiChannelMixer进行录制和播放

时间:2012-11-09 13:34:44

标签: objective-c audio core-audio record playback

我遇到了问题;)

我想从Mic录制音频并将其写入带有扩展音频文件服务的文件系统,并播放录制的Stuff。 如果我只使用带有两个回调的remoteIO,那么只读一个用于写入它。

对于Volumen Control,我想使用MultiChannelMixer和AUGraph。 您是否可以使用相同的调音台和RemoteIO实现播放录制?

我认为它必须如下:

RemotIO Input   ->        -> Write Callback
                    Mixer
RemoteIO Output <-        <- Read Callback

我创建两个AUNOD(RemoteIO和MultiChannelMixer),我如何设置Call​​backs和一个Callback从麦克风传送AudioData的连接,另一个从文件中读取数据,两个路径都通过混音器?

读写不是问题,只是节点的配置!

......以及CAShow的输出:

AudioUnitGraph 0x8AEE000:
  Member Nodes:
    node 1: 'aumx' 'mcmx' 'appl', instance 0x865a510 O I
    node 2: 'auou' 'rioc' 'appl', instance 0x865d0a0 O I
  Connections:
    node   1 bus   0 => node   2 bus   0  [ 2 ch,  44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer]
    node   2 bus   1 => node   1 bus   1  [ 2 ch,  44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer]
  Input Callbacks:
    {0x4150, 0x7573340} => node   2 bus   1  [2 ch, 44100 Hz]
    {0x4330, 0x7573340} => node   1 bus   0  [2 ch, 44100 Hz]
  CurrentState:
    mLastUpdateError=0, eventsToProcess=F, isRunning=F

这是设置代码:

    OSStatus setupErr = noErr;

AudioComponentDescription mixerDescription;
AudioComponentDescription ioDescription;

// the AUNodes
AUNode mixerNode;
AUNode ioNode;

// the graph
setupErr = NewAUGraph(&_graph);
NSAssert(setupErr == noErr, @"Couldn't create graph");

// the mixer
mixerDescription.componentFlags = 0;
mixerDescription.componentFlagsMask = 0;
mixerDescription.componentType = kAudioUnitType_Mixer;
mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple;

// the io 
ioDescription.componentFlags = 0;
ioDescription.componentFlagsMask = 0;
ioDescription.componentType = kAudioUnitType_Output;
ioDescription.componentSubType = kAudioUnitSubType_RemoteIO;
ioDescription.componentManufacturer = kAudioUnitManufacturer_Apple;

// add mixer Node
setupErr = AUGraphAddNode(self.graph, &mixerDescription, &mixerNode);
NSAssert(setupErr == noErr, @"Couldn't create master mixer");

// add io Node
setupErr = AUGraphAddNode(self.graph, &ioDescription, &ioNode);
NSAssert(setupErr == noErr, @"Couldn't create io node");

// open Graph
setupErr = AUGraphOpen(self.graph);
NSAssert(setupErr == noErr, @"Couldn't open graph");

// get the mixer info
setupErr = AUGraphNodeInfo(self.graph, mixerNode, &mixerDescription, &_mixer);
NSAssert(setupErr == noErr, @"Couldn't get master mixer info");

// get the io info
setupErr = AUGraphNodeInfo(self.graph, ioNode, &ioDescription, &_io);
NSAssert(setupErr == noErr, @"Couldn't get io Node info");

// enable io input
UInt32 enableFlag = 1;
setupErr = AudioUnitSetProperty(self.io, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enableFlag, sizeof(enableFlag));
NSAssert(setupErr == noErr, @"Couldn't enable io input");

// set up the mixers input callbacks
AURenderCallbackStruct outputCallbackStruct;
outputCallbackStruct.inputProc = outputCallback;
outputCallbackStruct.inputProcRefCon = self;

AURenderCallbackStruct inputCallbackStruct;
inputCallbackStruct.inputProc = inputCallback;
inputCallbackStruct.inputProcRefCon = self;

setupErr = AUGraphConnectNodeInput(self.graph, mixerNode, 0, ioNode, 0);
NSAssert(setupErr == noErr, @"Couldn't connect mixer output to io output");
setupErr = AUGraphConnectNodeInput(self.graph, ioNode, 1, mixerNode, 1);
NSAssert(setupErr == noErr, @"Couldn't connect io input to mixer input");

// set output Callback
setupErr = AUGraphSetNodeInputCallback(self.graph, ioNode, 1, &outputCallbackStruct);
NSAssert(setupErr == noErr, @"Error setting io output callback");

// set input Callback
setupErr = AUGraphSetNodeInputCallback(self.graph, mixerNode, 0, &inputCallbackStruct);
NSAssert(setupErr == noErr, @"Error setting mixer input callback");

// describe format
AudioStreamBasicDescription audioFormat = {0};
audioFormat.mSampleRate                 = 44100.00;
audioFormat.mFormatID                   = kAudioFormatLinearPCM;
audioFormat.mFormatFlags                = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket            = 1;
audioFormat.mChannelsPerFrame           = 2;
audioFormat.mBitsPerChannel             = 16;
audioFormat.mBytesPerPacket             = 4;
audioFormat.mBytesPerFrame              = 4;

// set the rio input properties
setupErr = AudioUnitSetProperty(self.io, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat));
NSAssert(setupErr == noErr, @"Error setting RIO input property");

// set the rio output properties
setupErr = AudioUnitSetProperty(self.io, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat));
NSAssert(setupErr == noErr, @"Error setting RIO output property");

// set the master fader output properties
setupErr = AudioUnitSetProperty(self.mixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Global, kOutputBus, &audioFormat, sizeof(audioFormat));
NSAssert(setupErr == noErr, @"Error setting master output property");

// set the master fader input properties
setupErr = AudioUnitSetProperty(self.mixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Global, kOutputBus, &audioFormat, sizeof(audioFormat));
NSAssert(setupErr == noErr, @"Error setting master input1 property");

// initialize Graph
setupErr = AUGraphInitialize(self.graph);
NSAssert(setupErr == noErr, @"Error initializing graph - error code");

CAShow(self.graph);

// start Graph
setupErr = AUGraphStart(self.graph);
NSAssert(setupErr == noErr, @"Error starting graph. - error code");

我希望你理解我的问题:) 感谢..

更新 更多描述我的问题的东西!

录制:RemoteIO InputScope Bus 0 - &gt;混频器输入总线0 - &gt;混频器输出总线0 - &gt;写回叫 - &gt;文件 播放:文件 - &gt;阅读回调 - &gt;混频器输入总线1 - &gt;混频器输出总线0 - &gt; RemoteIO OutputScope总线1

Connection Plan

1 个答案:

答案 0 :(得分:7)

您需要创建具有三个节点(单位)的AUGraph:

  1. 文件播放器(kAudioUnitSubType_AudioFilePlayer)
  2. RemoteIO
  3. 混合
  4. 像这样连接它们:

    AUGraphConnectNodeInput(m_graph, m_player, 0, m_mixerNode, 0); // player -> mixer
    AUGraphConnectNodeInput(m_graph, m_mixerNode, 0, m_rioNode, 0); // mixer -> output
    AUGraphConnectNodeInput(m_graph, m_rioNode, 1, m_mixerNode, 1); // input -> mixer
    

    在RIO上启用输入:

    UInt32 enable = 1;
    AudioUnitSetProperty(m_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enable, sizeof(UInt32));
    

    获取混音器输出格式并将其设置为扩展音频文件的客户端格式:

    AudioStreamBasicDescription mixerASBD;
    UInt32 prop = sizeof(mixerASBD);
    AudioUnitGetProperty(m_mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &mixerASBD, &prop);
    ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(mixerASBD), &mixerASBD);
    

    定义渲染回调:

    static OSStatus mixerCallBack(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber,UInt32 inNumberFrames, AudioBufferList *ioData) {
        if ((*ioActionFlags) & kAudioUnitRenderAction_PostRender)
            return (ExtAudioFileWrite(extAudioFile, inNumberFrames, ioData));
    
        return noErr;
    }
    

    为混音器的输出数据添加回调:

    AudioUnitAddRenderNotify(m_mixerUnit, mixerCallBack, NULL);
    

    这就是全部。您需要安排音频文件到FilePlayer单元播放。