繁体   English   中英

如何从外部音频接口访问带有核心音频的单个通道

[英]How to access individual channels with core audio from an external audio interface

我正在学习CoreAudio,并且只浏览了Apple文档中的一些示例,并弄清楚了如何进行设置以及不进行设置。 到目前为止,我已经能够连接到默认的已连接音频输入设备,并将其输出到默认的输出设备。 我连接了一个2通道接口,并且能够输出它的输入并输出它。

但是,我正在搜索他们的API参考和示例,但是找不到从我的界面访问各个输入通道的实质性内容。

我能够破解并从Render Callback函数的AudioBufferList中提取样本,并以此方式进行操作,但是我想知道是否存在正确的方法或更正式的方法来访问各个输入通道中的数据。

编辑:

这是从我正在使用的示例中找到的用户数据:

typedef struct MyAUGraphPlayer
{
    AudioStreamBasicDescription streamFormat;

    AUGraph graph;
    AudioUnit inputUnit;
    AudioUnit outputUnit;

    AudioBufferList * inputBuffer;
    CARingBuffer * ringBuffer;

    Float64 firstInputSampleTime;
    Float64 firstOutputSampleTime;
    Float64 inToOutSampleTimeOffset;

} MyAUGraphPlayer;

这就是我设置输入单元的方式:

void CreateInputUnit(MyAUGraphPlayer * player)
{
    //Generates a description that matches audio HAL
    AudioComponentDescription inputcd = {0};
    inputcd.componentType = kAudioUnitType_Output;
    inputcd.componentSubType = kAudioUnitSubType_HALOutput;
    inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;

    UInt32 deviceCount = AudioComponentCount ( &inputcd );

    printf("Found %d devices\n", deviceCount);

    AudioComponent comp = AudioComponentFindNext(NULL, &inputcd);

    if(comp == NULL) {
        printf("Can't get output unit\n");
        exit(1);
    }

    OSStatus status;
    status = AudioComponentInstanceNew(comp, &player->inputUnit);
    assert(status == noErr);

    //Explicitly enable Input and disable output
    UInt32 disableFlag = 0;
    UInt32 enableFlag = 1;
    AudioUnitScope outputBus = 0;
    AudioUnitScope inputBus = 1;

    status = AudioUnitSetProperty(player->inputUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Input,
                                inputBus,
                                &enableFlag,
                                sizeof(enableFlag))
    assert(status == noErr);

    status = AudioUnitSetProperty(player->inputUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Output,
                                outputBus,
                                &disableFlag,
                                sizeof(enableFlag));
    assert(status == noErr);

    printf("Finished enabling input and disabling output on an inputUnit\n");

    //Get the default Audio input Device
    AudioDeviceID defaultDevice = kAudioObjectUnknown;
    UInt32 propertySize = sizeof(defaultDevice);
    AudioObjectPropertyAddress defaultDeviceProperty;
    defaultDeviceProperty.mSelector =  kAudioHardwarePropertyDefaultInputDevice;
    defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
    defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;

    status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                      &defaultDeviceProperty,
                                      0,
                                      NULL,
                                      &propertySize,
                                      &defaultDevice);
    assert(status == noErr);


//Set the current device property of the AUHAL
    status = AudioUnitSetProperty(player->inputUnit,
                                kAudioOutputUnitProperty_CurrentDevice,
                                kAudioUnitScope_Global,
                                outputBus,
                                &defaultDevice,
                                sizeof(defaultDevice));
    assert(status == noErr);

    //Get the AudioStreamBasicDescription from Input AUHAL
    propertySize = sizeof(AudioStreamBasicDescription);
    status = AudioUnitGetProperty(player->inputUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                inputBus,
                                &player->streamFormat,
                                &propertySize);
    assert(status == noErr);


    //Adopt hardware input sample rate
    AudioStreamBasicDescription deviceFormat;
    status = AudioUnitGetProperty(player->inputUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input,
                                inputBus,
                                &deviceFormat,
                                &propertySize);
    assert(status == noErr);

    player->streamFormat.mSampleRate = deviceFormat.mSampleRate;

    printf("Sample Rate %f...\n", deviceFormat.mSampleRate);

    propertySize = sizeof(AudioStreamBasicDescription);
    status = AudioUnitSetProperty(player->inputUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                inputBus,
                                &player->streamFormat,
                                propertySize);
    assert(status == noErr);

    //Calculating Capture buffer size for an I/O unit
    UInt32 bufferSizeFrames = 0;
    propertySize = sizeof(UInt32);
    status = AudioUnitGetProperty(player->inputUnit,
                                kAudioDevicePropertyBufferFrameSize,
                                kAudioUnitScope_Global,
                                0,
                                &bufferSizeFrames,
                                &propertySize);
    assert(status == noErr);

    UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);

    //Create AudioBufferList to receive capture data
    UInt32 propSize = offsetof(AudioBufferList, mBuffers[0]) +
                    (sizeof(AudioBuffer) * player->streamFormat.mChannelsPerFrame);

    //Malloc buffer lists
    player->inputBuffer = (AudioBufferList *) malloc(propSize);
    player->inputBuffer->mNumberBuffers = player->streamFormat.mChannelsPerFrame;

    //Pre malloc buffers for AudioBufferLists
    for(UInt32 i = 0; i < player->inputBuffer->mNumberBuffers; i++){
        player->inputBuffer->mBuffers[i].mNumberChannels = 1;
        player->inputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
        player->inputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
    }

    //Create the ring buffer
    player->ringBuffer = new CARingBuffer();
    player->ringBuffer->Allocate(player->streamFormat.mChannelsPerFrame,
                             player->streamFormat.mBytesPerFrame,
                             bufferSizeFrames * 3);

    printf("Number of channels: %d\n", player->streamFormat.mChannelsPerFrame);
    printf("Number of buffers: %d\n", player->inputBuffer->mNumberBuffers);


    //Set render proc to supply samples
    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = InputRenderProc;
    callbackStruct.inputProcRefCon = player;

    status = AudioUnitSetProperty(player->inputUnit,
                                kAudioOutputUnitProperty_SetInputCallback,
                                kAudioUnitScope_Global,
                                0,
                                &callbackStruct,
                                sizeof(callbackStruct);
    assert(status == noErr);

    status = AudioUnitInitialize(player->inputUnit);
    assert(status == noErr);

    player->firstInputSampleTime = -1;
    player->inToOutSampleTimeOffset = -1;

    printf("Finished CreateInputUnit()\n");

}

所以这是我的渲染回调函数,在这里我要访问各个缓冲区。

OSStatus GraphRenderProc(void * inRefCon,
                     AudioUnitRenderActionFlags * ioActionFlags,
                     const AudioTimeStamp * inTimeStamp,
                     UInt32 inBusNumber,
                     UInt32 inNumberFrames,
                     AudioBufferList * ioData)
{
    MyAUGraphPlayer * player = (MyAUGraphPlayer *) inRefCon;

    if(player->firstOutputSampleTime < 0.0) {
        player->firstOutputSampleTime = inTimeStamp->mSampleTime;
        if((player->firstInputSampleTime > -1.0) &&
           (player->inToOutSampleTimeOffset < 0.0)) {
            player->inToOutSampleTimeOffset = player->firstInputSampleTime - player->firstOutputSampleTime;
        }
    }

    //Copy samples out of ring buffer
    OSStatus outputProcErr = noErr;

    outputProcErr = player->ringBuffer->Fetch(ioData,
                                          inNumberFrames,
                                          inTimeStamp->mSampleTime + player->inToOutSampleTimeOffset);


    //BUT THIS IS NOT HOW IT IS SUPPOSED TO WORK
    Float32 * data = (Float32 *) ioData->mBuffers[0].mData;
    Float32 * data2 = (Float32 *) ioData->mBuffers[1].mData;



    for(int frame = 0; frame < inNumberFrames; frame++)
    {
        Float32 sample =  data[frame] + data2[frame];
        data[frame] = data2[frame] = sample;
    }


    return outputProcErr;
}

尽管您的代码看起来似乎难以完成,但我将尝试回答您的问题:

您在回调中检索示例数据的概念没有错。 如果处理多声道音频设备,那将是不够的。 设备具有多少个通道,以及通过AudioStreamBasicDescription查询给定设备的通道布局,格式等。 该属性用于初始化其余处理链。 您可以在初始化时分配音频缓冲区,或者让程序为您完成(请阅读文档)。

如果您发现使用额外的缓冲区复制到仅用于数据处理和DSP更舒适,则可以在回调内部进行管理,如下所示(简化代码):

Float32 buf[streamFormat.mChanelsPerFrame][inNumberFrames];

for(int ch; ch < streamFormat.mChanelsPerFrame; ch++){
    Float32 data = (Float32 *)ioData->mBuffers[ch].mData;
    memcpy(buf[ch], data, inNumberFrames*sizeof(Float32));
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM