rich.e
rich.e

Reputation: 3750

Audio Units: getting generated audio into a render callback

I've been trying without success to manipulate the samples produced by kAudioUnitType_Generator audio units by attaching an AURenderCallbackStruct to the input of the audio unit right after. I managed to get this working on OS X using the following simple graph:

(input callback) -> multichannel mixer -> (intput callback) -> default output

But I've failed with the following (even simpler) graphs that start with a generator unit:

speech synthesis -> (intput callback) -> default output           | fails in render callback with kAudioUnitErr_Uninitialized
audiofile player -> (intput callback) -> default output           | fails when scheduling file region with kAudioUnitErr_Uninitialized

I've tried just about everything I can think of, from setting ASBD format to sample rates, but I always get these errors. Does anyone know how to setup a graph where we can manipulate samples from these nice generator units?

Below is the failing render callback function and graph instantiation method for the attempt using speech synthesis. The audiofile player is almost identical for this, except setting up the file playback, of course. Both of these setups work if I remove the callback and add an AUGraphConnectNodeInput in it's place...

static OSStatus RenderCallback(void *inRefCon,
                                    AudioUnitRenderActionFlags *ioActionFlags,
                                    const AudioTimeStamp *inTimeStamp,
                                    UInt32 inBusNumber,
                                    UInt32 inNumberFrames,
                                    AudioBufferList *ioData) {

    AppDelegate *app = (AppDelegate *)inRefCon;
    AudioUnit inputUnit = app->_speechUnit;
    OSStatus status = noErr;

    status = AudioUnitRender(inputUnit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);
    // *** ERROR *** kAudioUnitErr_Uninitialized, code: -10867

    // ... insert processing code here...

    return status;
}

- (int)createSynthGraph {
    AUGRAPH_CHECK( NewAUGraph( &_graph ) );

    AUNode speechNode, outputNode;

    // speech synthesizer
    AudioComponentDescription speechCD = {0};
    speechCD.componentType = kAudioUnitType_Generator;
    speechCD.componentSubType = kAudioUnitSubType_SpeechSynthesis;
    speechCD.componentManufacturer = kAudioUnitManufacturer_Apple;

    // output device (speakers)
    AudioComponentDescription outputCD = {0};
    outputCD.componentType = kAudioUnitType_Output;
    outputCD.componentSubType = kAudioUnitSubType_DefaultOutput;
    outputCD.componentManufacturer = kAudioUnitManufacturer_Apple;

    AUGRAPH_CHECK( AUGraphAddNode( _graph, &outputCD, &outputNode ) );
    AUGRAPH_CHECK( AUGraphAddNode( _graph, &speechCD, &speechNode ) );

    AUGRAPH_CHECK( AUGraphOpen( _graph ) );
    AUGRAPH_CHECK( AUGraphNodeInfo( _graph, outputNode, NULL, &_outputUnit ) );
    AUGRAPH_CHECK( AUGraphNodeInfo( _graph, speechNode, NULL, &_speechUnit ) );

    // setup stream formats:
    AudioStreamBasicDescription streamFormat = [self streamFormat];
    AU_CHECK( AudioUnitSetProperty( _speechUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, sizeof(streamFormat) ) );

    // setup callback:
    AURenderCallbackStruct callback;
    callback.inputProc = RenderCallback;
    callback.inputProcRefCon = self;
    AUGRAPH_CHECK( AUGraphSetNodeInputCallback ( _graph, outputNode, 0, &callback ) );

    // init and start
    AUGRAPH_CHECK( AUGraphInitialize( _graph ) );
    AUGRAPH_CHECK( AUGraphStart( _graph ) );
    return 0;
}

Upvotes: 3

Views: 1939

Answers (2)

Vlad
Vlad

Reputation: 7260

kAudioUnitErr_Uninitialized error appears when your audio unit is not initialized. Just initialize your graph before setting problem properties. This will initialize all opened audio units in graph. From AUGraphInitialize discussion in AUGraph.h:

AudioUnitInitialize() is called on each opened node/AudioUnit (get ready to render) and SubGraph that are involved in a interaction.

Upvotes: 1

iluvcapra
iluvcapra

Reputation: 9464

You must connect these nodes together with AUGraphConnectNodeInput. The AUGraph will not initialize AudioUnits within it unless they are connected. Units in a graph that are not connected to other units will not be initialized.

You could also try manually initing them before starting the graph.

Upvotes: 1

Related Questions