0
votes

I am learning CoreAudio and I am just going through some of the examples on Apple's documentation and figuring out how to set things up and what not. So far I am able to connect to the default connected audio input device and output it to the default output device. I connected a 2 channel interface and was able to output the input from it and output it as well.

However I was searching through their API references and examples, but could not find any thing substantial to access the individual input channels from my interface.

I was able to hack away and extract the samples from the AudioBufferList in my Render Callback function and manipulate it that way but I am wondering if there is a correct way or a more official way of accessing the data from each individual input channels.

EDIT:

This is the user data that I found from an example that I am using:

typedef struct MyAUGraphPlayer
{
    AudioStreamBasicDescription streamFormat;

    AUGraph graph;
    AudioUnit inputUnit;
    AudioUnit outputUnit;

    AudioBufferList * inputBuffer;
    CARingBuffer * ringBuffer;

    Float64 firstInputSampleTime;
    Float64 firstOutputSampleTime;
    Float64 inToOutSampleTimeOffset;

} MyAUGraphPlayer;

This is how i set up the input unit:

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

}

So this is my render callback function where I am accessing the individual buffers. :

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;
}
1
If you don't post at least an example of what you've done, you let people guess and judge upon your wording...user3078414
i added some example code. let me know if you need anything elsemjl007

1 Answers

0
votes

Although your code looks overly complicated for the task it seems to manage, I'll try to answer your question:

There's nothing wrong with your concept of retrieving sample data inside a callback. It would though be insufficient if dealing with multichannel audio devices. How many channels the device has and which is the channel layout, format etc. you query through AudioStreamBasicDescription for given device. This property serves for the initialization of the rest of your processing chain. You allocate audio buffers on initialization, or let the program do it for you (please read documentation).

In case you find more comfortable using extra buffers to copy to just for data crunching and DSP, you can manage it inside your callback like this (simplified code):

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