How to write output of AUGraph to a file?
I had a epiphany with regards to Audio Units just now which helped me solve my own problem. I had a misconception about how audio unit connections and render callbacks work. I thought they were completely separate things but it turns out that a connection is just short hand for a render callback.
Doing an kAudioUnitProperty_MakeConnection from the output of audio unit A to the input of audio unit B is the same as doing kAudioUnitProperty_SetRenderCallback on the input of unit B and having the callback function call AudioUnitRender on the output of audio unit A.
I tested this by doing a make connection after setting my render callback and the render callback was no longer invoked.
Therefore, I was able to solve my problem by doing the following:
AURenderCallbackStruct callbackStruct = {0};
callbackStruct.inputProc = MyAURenderCallback;
callbackStruct.inputProcRefCon = mixerUnit;
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callbackStruct,
sizeof(callbackStruct));
And them my callback function did something like this:
OSStatus MyAURenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioUnit mixerUnit = (AudioUnit)inRefCon;
AudioUnitRender(mixerUnit,
actionFlags,
inTimeStamp,
0,
inNumberFrames,
ioData);
ExtAudioFileWriteAsync(outputFile,
inNumberFrames,
ioData);
return noErr;
}
This probably should have been obvious to me but since it wasn't I'll bet there are others that were confused in the same way so hopefully this is helpful to them too.
I'm still not sure why I had trouble with the AUGraphAddRenderNotify callback. I will dig deeper into this later but for now I found a solution that seems to work.
AUGraph Mixing & Rendering to file, as a background task? (Objective-C)
Eventually I just got an external company to do the audio processing which supplied an library which would output the microphone through the speakers, throw some eq's over it and merges it eventually.
CoreAudio's a bitch :(
Callback function of output unit of audio graph - can't read data
If capturing input data from an AUGraph
is the task, the critical part of code (more or less) boils down to this simplest one-channel demo example:
OSStatus MyRenderProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
Float32 buf [inNumberFrames]; //just for one channel!
MyMIDIPlayer *player = (MyMIDIPlayer *)inRefCon;
if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){
Float32* data = (Float32 *)ioData->mBuffers[0].mData; //just for one channel!
memcpy(buf, data, inNumberFrames*sizeof(Float32));
//do something with buff - there are nice examples of ExtAudioFileWriteAsync()
}
}
return noErr;
}
In the setupAUGraph()
this callback can be set up in the following way:
void setupAUGraph(MyMIDIPlayer *player)
{
// the beginning follows the textbook example setup pattern
{… … …}
// this is the specific part
AURenderCallbackStruct input = {0};
input.inputProc = MyRenderProc;
input.inputProcRefCon = player->instrumentUnit;
CheckError(AudioUnitAddRenderNotify(player->instrumentUnit,
MyRenderProc,
&player),
"AudioUnitAddRenderNotify Failed");
// now initialize the graph (causes resources to be allocated)
CheckError(AUGraphInitialize(player->graph),
"AUGraphInitialize failed");
}
Please note that the render callback "taps" the connection between the output of instrument node and input of output node, capturing what comes from upstream. The callback just copies ioData
into some other buffer which can be saved. AFAIK, this is the simplest way of accessing ioData
that I know it works, without breaking the API.
Please also note there are very efficient plain-C methods for testing if this works for a specific implementation - no need for Objective-C methods inside the callback. Fiddling with some NSArray
s, adding objects etc, inside a plain-C real-time callback introduces risk of priority issues which may later become difficult to debug. CoreAudio API is written in plain-C for a reason. At the heart of Obj-C runtime there's much of what can't take place on the real time thread without risking glitches (locks, memory management, etc). So, it would be safer to keep off Obj-C on a real time thread.
Hope this can help.
IOS, No output when AUGraphConnectNodeInput is set
The problem might be that you are setting two inputs to your IO node (remote or voice processing), and no inputs to your audio graph.
AUNode CAShow Output
Difficult to tell without seeing the code in play, but I believe those are referring to "Opened" and "Initialized".
AUGraph Record and Play with RemoteIO and MultiChannelMixer
You need create AUGraph with three nodes (units):
- File Player (kAudioUnitSubType_AudioFilePlayer)
- RemoteIO
- Mixer
Connect them like this:
AUGraphConnectNodeInput(m_graph, m_player, 0, m_mixerNode, 0); // player -> mixer
AUGraphConnectNodeInput(m_graph, m_mixerNode, 0, m_rioNode, 0); // mixer -> output
AUGraphConnectNodeInput(m_graph, m_rioNode, 1, m_mixerNode, 1); // input -> mixer
Enable input on RIO:
UInt32 enable = 1;
AudioUnitSetProperty(m_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enable, sizeof(UInt32));
Get mixer output format and set it as client format for Extended Audio File:
AudioStreamBasicDescription mixerASBD;
UInt32 prop = sizeof(mixerASBD);
AudioUnitGetProperty(m_mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &mixerASBD, &prop);
ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(mixerASBD), &mixerASBD);
Define render callback:
static OSStatus mixerCallBack(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber,UInt32 inNumberFrames, AudioBufferList *ioData) {
if ((*ioActionFlags) & kAudioUnitRenderAction_PostRender)
return (ExtAudioFileWrite(extAudioFile, inNumberFrames, ioData));
return noErr;
}
Add callback for output data from mixer:
AudioUnitAddRenderNotify(m_mixerUnit, mixerCallBack, NULL);
That's all. You need to schedule audio files to FilePlayer unit to play.
Related Topics
Does iPhone Support Hardware-Accelerated Aes Encryption
How to Change a Swiftui Color to Uicolor
Custom Scenekit Geometry in Swift on iOS Not Working But Equivalent Objective C Code Does
Apple Push Notification Service Server Certificate Update
Gamecenter iOS 9 Gamecenter Gklocalplayerlistener Methods Not Called
Cannot Invoke Initializer for Type 'Double' with an Argument List of Type '(String)'
How to Clear/Reset All Coredata in One-To-Many Relationship
Uipickerview: Nsattributedstring Not Available in iOS 7
Onfocuschange Not Triggered in Swiftui Tvos
Swift - Get Date from Day of Year
Core Data Predicate:Unimplemented SQL Generation for Predicate
Swift Error: Editor Placeholder in Source File
Font Size on Universal Storyboard
The 'Pods' Target Has Transitive Dependencies That Include Static Binaries' When Installing Gcm
Warning in Custom Map Annotations Iphone
Arkit Session Paused and Not Resuming
Expandable Uitableview Cell Using Autolayout Results in Uiviewalertforunsatisfiableconstraints