Paul
Paul

Reputation: 63

No sound out of CoreAudio AUGraph

I am having trouble getting audio from an AUGraph using MixerUnit and OutputUnit. I have build a Graph that mixes 2 audiofiles. Here is the code where I create the graph. I don't have any errors.

NSLog(@"initializeAUGraph");

mAudioFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
                                                sampleRate:kSampleRate
                                                  channels:2
                                               interleaved:NO];

CheckError(NewAUGraph(&mGraph),"Error creating new graph");

AUNode outputNode;

AudioComponentDescription outputcd = {0};
outputcd.componentType = kAudioUnitType_Output;
outputcd.componentSubType = kAudioUnitSubType_DefaultOutput;
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;

AudioComponent comp = AudioComponentFindNext(NULL, &outputcd);
if (comp == NULL) {
    printf ("can't get output unit"); exit (-1);
}

AUNode mixerNode;

AudioComponentDescription mixerDescription = {0};
mixerDescription.componentType = kAudioUnitType_Mixer;
mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple;

CheckError(AUGraphAddNode(mGraph, &outputcd, &outputNode), "Error adding Output node to graph");

CheckError(AUGraphAddNode(mGraph, &mixerDescription, &mixerNode), "Error adding Mixer mode to graph");

CheckError(AUGraphOpen(mGraph), "AUGraphOpen failed");

CheckError(AUGraphConnectNodeInput(mGraph, mixerNode, 0, outputNode, 0),"AUGraphConnectModeInput");

CheckError(AUGraphNodeInfo(mGraph, mixerNode, NULL, &mMixer), "Error loading mixer node info");

CheckError(AUGraphNodeInfo(mGraph, outputNode, NULL, &mOutput), "Error loading output node info");

UInt32 numbuses = 2;

CheckError(AudioUnitSetProperty(mMixer, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &numbuses, sizeof(numbuses)) , "Error setting prop to mixer");

for (int i = 0; i < numbuses; ++i) {
    // setup render callback struct
    AURenderCallbackStruct renderCallbackStruct;
    renderCallbackStruct.inputProc = &renderAudioInput;
    renderCallbackStruct.inputProcRefCon = mSoundBuffer;

    // Set a callback for the specified node's specified input
    CheckError((AUGraphSetNodeInputCallback(mGraph, mixerNode, i, &renderCallbackStruct))," AUGraphSetNodeInputCallback failed in cycle");

    //Set the input stream format property

   CheckError(AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, mAudioFormat.streamDescription, sizeof(AudioStreamBasicDescription)),"Set proprety in cycle error");

}

CheckError(AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, mAudioFormat.streamDescription, sizeof(AudioStreamBasicDescription)), "AudioUnitSetProperty mixer stream format failed");

/*CheckError(AudioUnitSetProperty(mOutput, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, mAudioFormat.streamDescription, sizeof(AudioStreamBasicDescription)), "AudioUnitSetProperty output stream format failed");*/

CheckError(AUGraphInitialize(mGraph),"AUGraphInitialize failed");

Console output:

2018-05-23 01:19:22.676281+0300 Mixer2Mac[37249:1285521] loadAudioFiles
2018-05-23 01:19:22.676918+0300 Mixer2Mac[37249:1285521] 2017569 frames in GuitarMonoSTP.aif
2018-05-23 01:19:22.676939+0300 Mixer2Mac[37249:1285521] 2017569 frames after sample ratio multiplied in GuitarMonoSTP.aif
2018-05-23 01:19:22.682277+0300 Mixer2Mac[37249:1285521] 2017569 frames in DrumsMonoSTP.aif
2018-05-23 01:19:22.682303+0300 Mixer2Mac[37249:1285521] 2017569 frames after sample ratio multiplied in DrumsMonoSTP.aif
2018-05-23 01:19:22.687415+0300 Mixer2Mac[37249:1285521] initializeAUGraph
2018-05-23 01:19:23.860541+0300 Mixer2Mac[37249:1285521] startPlaying
2018-05-23 01:19:24.830917+0300 Mixer2Mac[37249:1285521] stopPlaying
2018-05-23 01:19:28.218856+0300 Mixer2Mac[37249:1285521] startPlaying
2018-05-23 01:20:13.009934+0300 Mixer2Mac[37249:1285693] Starting over at frame 0 for bus 0
2018-05-23 01:20:13.010091+0300 Mixer2Mac[37249:1285693] Starting over at frame 0 for bus 1

I see that the files are playing in the console but hear no sound. The app is for MacOs not for iOS. What can be the problem?

EDIT 1


Here is the Callback function. It is called. I can see it in the console. It prints "LOLLOL"

static OSStatus renderAudioInput(void *inRefCon, AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberOfFrames, AudioBufferList *ioData)
{
    NSLog(@"LOLLOLO");

    SoundBufferPtr soundBuffer = (SoundBufferPtr)inRefCon;

    //NSLog(@"numberOfFrames: %d", inNumberOfFrames);

    //Get the frame to start at and total number of samples
    UInt32 sample = soundBuffer[inBusNumber].sampleNumber;
    UInt32 startSample = sample;
    UInt32 bufferTotalSamples = soundBuffer[inBusNumber].numberOfFrames;

    //Get a reference to the input data buffer
    Float32 *inputData = soundBuffer[inBusNumber].data; // audio data buffer

    //Get references to the channel buffers
    Float32 *outLeft = (Float32 *)ioData->mBuffers[0].mData; // output audio buffer for Left channel
    Float32 *outRight = (Float32 *)ioData->mBuffers[1].mData; // output audio buffer for Right channel

    //Loop thru the number of frames and set the output data from the input data.
    //Use the left channel for bus 0 (guitar) and right channel for bus 1 (drums) to distiguish for example
    for (UInt32 i = 0; i < inNumberOfFrames; ++i) {

        if (inBusNumber == 0) {
            outLeft[i] = inputData[sample++];
            outRight[i] = 0;
        } else {    //inBusNumber == 1
            outLeft[i] = 0;
            outRight[i] = inputData[sample++];
        }

        //If the sample is beyond the total number of samples in the loop, start over at the beginning
        if (sample > bufferTotalSamples) {
            // start over from the beginning of the data, our audio simply loops
            sample = 0;
            NSLog(@"Starting over at frame 0 for bus %d", (int)inBusNumber);
        }
    }

    //Set the sample number in the sound buffer struct so we know which frame playback is on
    soundBuffer[inBusNumber].sampleNumber = sample;

    performFFT(&inputData[startSample], inNumberOfFrames, soundBuffer, inBusNumber);

    return noErr;
}

Output after NSLog("LOLLOL"):

2018-05-23 12:56:49.297251+0300 Mixer2Mac[39034:1368659] loadAudioFiles
2018-05-23 12:56:49.298417+0300 Mixer2Mac[39034:1368659] 2017569 frames in GuitarMonoSTP.aif
2018-05-23 12:56:49.298445+0300 Mixer2Mac[39034:1368659] 2017569 frames after sample ratio multiplied in GuitarMonoSTP.aif
2018-05-23 12:56:49.309126+0300 Mixer2Mac[39034:1368659] 2017569 frames in DrumsMonoSTP.aif
2018-05-23 12:56:49.309156+0300 Mixer2Mac[39034:1368659] 2017569 frames after sample ratio multiplied in DrumsMonoSTP.aif
2018-05-23 12:56:49.316572+0300 Mixer2Mac[39034:1368659] initializeAUGraph
2018-05-23 12:56:51.206301+0300 Mixer2Mac[39034:1368659] startPlaying
2018-05-23 12:56:51.229420+0300 Mixer2Mac[39034:1368816] LOLLOLO
2018-05-23 12:56:51.229692+0300 Mixer2Mac[39034:1368816] LOLLOLO
2018-05-23 12:56:51.240977+0300 Mixer2Mac[39034:1368816] LOLLOLO
2018-05-23 12:56:51.241162+0300 Mixer2Mac[39034:1368816] LOLLOLO

Upvotes: 3

Views: 479

Answers (1)

Gordon Childs
Gordon Childs

Reputation: 36074

New, improved answer This answer, of which yours is not quite a duplicate because you have two busses instead of one, points out that on macOS the mixer input volumes default to zero, and a comment adds that the output volume does too, so modify your code like so:

for (int i = 0; i < numbuses; ++i) {
    // setup render callback struct
    AURenderCallbackStruct renderCallbackStruct;
    renderCallbackStruct.inputProc = &renderAudioInput;
    renderCallbackStruct.inputProcRefCon = mSoundBuffer;

    // Set a callback for the specified node's specified input
    CheckError((AUGraphSetNodeInputCallback(mGraph, mixerNode, i, &renderCallbackStruct))," AUGraphSetNodeInputCallback failed in cycle");

    //Set the input stream format property

    CheckError(AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, mAudioFormat.streamDescription, sizeof(AudioStreamBasicDescription)),"Set proprety in cycle error");

    // NEW! mixer input volume defaults to zero on macOS. Why!?!?
    CheckError(AudioUnitSetParameter(mMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, i, 1.0, 0), "AudioUnitSetParameter failed");
}

// NEW! Output defaults to zero too!
CheckError(AudioUnitSetParameter(mMixer, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, 1.0, 0), "AudioUnitSetParameter failed");

The documentation seems to incorrectly says that kMultiChannelMixerParam_Volume defaults to 1.0:

Global scope. The value should be a number between 0.0 and 1.0. The default value is 1.0.

however that's for global scope.

Oh, you were starting it

You need to start the audio graph after initializing it:

OSStatus status = AUGraphStart(mGraph);

Upvotes: 1

Related Questions