OSStatus callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
AudioBufferList list;

// redundant
list.mNumberBuffers = 1;
list.mBuffers[0].mData = sampleBuffer;
list.mBuffers[0].mDataByteSize = 2 * inNumberFrames;
list.mBuffers[0].mNumberChannels = 1;

ioData = &list;

AudioUnitRender(instance, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);

// the sample buffer now contains samples you can work with

return 0;
}

void init() {
AudioComponentDescription desc;

desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = 0;

AudioComponent component = AudioComponentFindNext(NULL, &desc);

AudioComponentInstanceNew(component, &instance);

UInt32 enableIO;

sampleBuffer = malloc(4 * 1024);

enableIO = 1;
AudioUnitSetProperty(instance,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1, // input element
&enableIO,
sizeof(enableIO));

enableIO = 0;
AudioUnitSetProperty(instance,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
0, //output element
&enableIO,
sizeof(enableIO));

UInt32 shouldAllocateBuffer = 1;
AudioUnitSetProperty(instance, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Global, 1, &shouldAllocateBuffer, sizeof(shouldAllocateBuffer));

AudioStreamBasicDescription format;

format.mBitsPerChannel = 16;
format.mBytesPerFrame = 2;
format.mBytesPerPacket = 2;
format.mChannelsPerFrame = 1;
format.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; //kAudioFormatFlagsCanonical + (kAudioUnitSampleFractionBits << kLinearPCMFormatFlagsSampleFractionShift);
format.mFormatID = kAudioFormatLinearPCM;
format.mFramesPerPacket = 1;
format.mReserved = 0;
format.mSampleRate = 8000.0;

//set format to output scope
err = AudioUnitSetProperty(instance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof(AudioStreamBasicDescription));

AURenderCallbackStruct callback_struct;

callback_struct.inputProc = callback;
callback_struct.inputProcRefCon = instance;

err = AudioUnitSetProperty(instance, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback_struct, sizeof(callback_struct));

err = AudioUnitInitialize(instance);

err = AudioOutputUnitStart(instance);
}