1

I want to bounce a midi file offline, and as the PlaySequence example does exactly this, I am trying to understand it.
I keep reading everywhere that you need a callback function to do anything in CoreAudio, yet I cannot see any in this project.
I paste the loop containing the AudioUnitRender, thanks for your help!

    CAStreamBasicDescription clientFormat = CAStreamBasicDescription();
    size = sizeof(clientFormat);
    FailIf ((result = AudioUnitGetProperty (outputUnit,
                                                kAudioUnitProperty_StreamFormat,
                                                kAudioUnitScope_Output, 0,
                                                &clientFormat, &size)), fail, "AudioUnitGetProperty: kAudioUnitProperty_StreamFormat");
    size = sizeof(clientFormat);
    FailIf ((result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat)), fail, "ExtAudioFileSetProperty: kExtAudioFileProperty_ClientDataFormat");

    {
        MusicTimeStamp currentTime;
        AUOutputBL outputBuffer (clientFormat, numFrames);
        AudioTimeStamp tStamp;
        memset (&tStamp, 0, sizeof(AudioTimeStamp));
        tStamp.mFlags = kAudioTimeStampSampleTimeValid;
        int i = 0;
        int numTimesFor10Secs = (int)(10. / (numFrames / srate));
        do {
            outputBuffer.Prepare();
            AudioUnitRenderActionFlags actionFlags = 0;
            FailIf ((result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL())), fail, "AudioUnitRender");

            tStamp.mSampleTime += numFrames;

            FailIf ((result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL())), fail, "ExtAudioFileWrite");   

            FailIf ((result = MusicPlayerGetTime (player, &currentTime)), fail, "MusicPlayerGetTime");
            if (shouldPrint && (++i % numTimesFor10Secs == 0))
                printf ("current time: %6.2f beats\n", currentTime);
        } while (currentTime < sequenceLength);
    }
popisar
  • 379
  • 2
  • 3
  • 15

1 Answers1

1

I had a look at the project and you're right, it does not have a rendercallback. Render callbacks have their place withing coreaudio audiounit effect processing. The call to setup a render callback looks like this:

inline OSStatus     SetInputCallback (CAAudioUnit &inUnit, AURenderCallbackStruct &inInputCallback)
{
    return inUnit.SetProperty (kAudioUnitProperty_SetRenderCallback, 
                                            kAudioUnitScope_Input, 
                                            0,
                                            &inInputCallback, 
                                            sizeof(inInputCallback));
}

However this code is just one big main() which setups up the sequence, augraph, music player and then the filewriter WriteOutputFile in case there's an outputfile to bounce to.

I recommend you set some breakpoints at key methods, and walk through the code, watching what it does and looking at variables.

EDIT: Note, that in setting up your rendercallback on iOS on your RemoteIO (which doubles as input & output units), getting the correct stream format on the correct scope & bus element numbers in your setproperty calls can be tricky. Refer to this from the Apple docs.

enter image description here

ruoho ruotsi
  • 1,283
  • 14
  • 13
  • Thanks ruoho. I'm actually doing a more radical "analysis" with this code, as I'm porting it to iPhone and Swift. I have successfully set up the graph (I can listen to a MIDI file playing), but I am still in trouble when it comes to bouncing the MIDI file to an audio file. Everything seems fine, but the loop is not able to pull data : the MusicTimeStamp is always 0.0 – popisar Apr 08 '15 at 05:28
  • Okay, this code is grabbing the output unit (kAudioUnitType_Output) of the AUGraph and the render call writes the samples to a buffer which is written to disk. On iOS the RemoteIO is your "output unit", a rendercall back on the RemoteIO (note correct bus=0) will give you the samples that you can write to disk. For more details, see: http://stackoverflow.com/questions/8951458/how-to-write-output-of-augraph-to-a-file http://stackoverflow.com/questions/6930609/write-audio-to-disk-from-io-unit – ruoho ruotsi Apr 08 '15 at 05:39
  • I already knew the first link you gave me, but who knows why, when you pointed it out I saw it in another light. I was so distracted by the AudioBufferList that I didn't notice that my ASBD was a mess (multiple ones, "get" instead of "set"... you name it). If I got it well, you need ONE ASBD, and you have to manually apply it to every unit in your graph. There is no sort of "back propagation": if I assign an ASBD to the output node, this will not automatically extend to upstream nodes (mixer and synths, in my case), right? – popisar Apr 08 '15 at 09:30