AudioUnitInstrumentExample/SinSynthWithMidi.cpp
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Instrument AU |
*/ |
/* |
This is a subclass of SinSynth that demonstrates how to use the midi output properties kAudioUnitProperty_MIDIOutputCallbackInfo, |
and kAudioUnitProperty_MIDIOutputCallback defined in AudioUnitProperties.h. |
Using these properties, the SinSynthWithMidi simply passes through the midi data it receives. Use of these properties requires host support. |
To build a version of the SinSynth with this functionality, activate the "SinSynth with MIDI Output" target in Xcode. |
*/ |
#include "SinSynth.h" |
#include <CoreMIDI/CoreMIDI.h> |
#include <vector> |
typedef struct MIDIMessageInfoStruct { |
UInt8 status; |
UInt8 channel; |
UInt8 data1; |
UInt8 data2; |
UInt32 startFrame; |
} MIDIMessageInfoStruct; |
class MIDIOutputCallbackHelper |
{ |
enum { kSizeofMIDIBuffer = 512 }; |
public: |
MIDIOutputCallbackHelper() |
{ |
mMIDIMessageList.reserve (64); |
mMIDICallbackStruct.midiOutputCallback = NULL; |
mMIDIBuffer = new Byte[kSizeofMIDIBuffer]; |
} |
~MIDIOutputCallbackHelper() |
{ |
delete [] mMIDIBuffer; |
} |
void SetCallbackInfo (AUMIDIOutputCallback & callback, void *userData) |
{ |
mMIDICallbackStruct.midiOutputCallback = callback; |
mMIDICallbackStruct.userData = userData; |
} |
void AddMIDIEvent (UInt8 status, |
UInt8 channel, |
UInt8 data1, |
UInt8 data2, |
UInt32 inStartFrame ); |
void FireAtTimeStamp(const AudioTimeStamp &inTimeStamp); |
private: |
MIDIPacketList * PacketList() |
{ |
return (MIDIPacketList *)mMIDIBuffer; |
} |
Byte * mMIDIBuffer; |
AUMIDIOutputCallbackStruct mMIDICallbackStruct; |
typedef std::vector<MIDIMessageInfoStruct> MIDIMessageList; |
MIDIMessageList mMIDIMessageList; |
}; |
class SinSynthWithMidi : public SinSynth { |
public: |
SinSynthWithMidi(AudioUnit inComponentInstance); |
virtual ~SinSynthWithMidi(); |
virtual OSStatus GetPropertyInfo( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
UInt32 & outDataSize, |
Boolean & outWritable); |
virtual OSStatus GetProperty( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
void * outData); |
virtual OSStatus SetProperty( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
const void * inData, |
UInt32 inDataSize); |
OSStatus HandleMidiEvent( UInt8 status, |
UInt8 channel, |
UInt8 data1, |
UInt8 data2, |
UInt32 inStartFrame); |
OSStatus Render( AudioUnitRenderActionFlags & ioActionFlags, |
const AudioTimeStamp & inTimeStamp, |
UInt32 inNumberFrames); |
private: |
MIDIOutputCallbackHelper mCallbackHelper; |
TestNote mTestNotes[kNumNotes]; |
}; |
#pragma mark MIDIOutputCallbackHelper Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
void MIDIOutputCallbackHelper::AddMIDIEvent(UInt8 status, |
UInt8 channel, |
UInt8 data1, |
UInt8 data2, |
UInt32 inStartFrame) |
{ |
MIDIMessageInfoStruct info = {status, channel, data1, data2, inStartFrame}; |
mMIDIMessageList.push_back(info); |
} |
void MIDIOutputCallbackHelper::FireAtTimeStamp(const AudioTimeStamp &inTimeStamp) |
{ |
if (!mMIDIMessageList.empty()) |
{ |
if (mMIDICallbackStruct.midiOutputCallback) |
{ |
// synthesize the packet list and call the MIDIOutputCallback |
// iterate through the vector and get each item |
MIDIPacketList *pktlist = PacketList(); |
MIDIPacket *pkt = MIDIPacketListInit(pktlist); |
for (MIDIMessageList::iterator iter = mMIDIMessageList.begin(); iter != mMIDIMessageList.end(); iter++) |
{ |
const MIDIMessageInfoStruct & item = *iter; |
Byte midiStatusByte = item.status + item.channel; |
const Byte data[3] = { midiStatusByte, item.data1, item.data2 }; |
UInt32 midiDataCount = ((item.status == 0xC || item.status == 0xD) ? 2 : 3); |
pkt = MIDIPacketListAdd (pktlist, |
kSizeofMIDIBuffer, |
pkt, |
item.startFrame, |
midiDataCount, |
data); |
if (!pkt) |
{ |
// send what we have and then clear the buffer and then go through this again |
// issue the callback with what we got |
OSStatus result = (*mMIDICallbackStruct.midiOutputCallback) (mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist); |
if (result != noErr) |
printf("error calling output callback: %d", (int) result); |
// clear stuff we've already processed, and fire again |
mMIDIMessageList.erase (mMIDIMessageList.begin(), iter); |
FireAtTimeStamp(inTimeStamp); |
return; |
} |
} |
// fire callback |
OSStatus result = (*mMIDICallbackStruct.midiOutputCallback) (mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist); |
if (result != noErr) |
printf("error calling output callback: %d", (int) result); |
} |
mMIDIMessageList.clear(); |
} |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
#pragma mark SinSynthWithMidi Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
AUDIOCOMPONENT_ENTRY(AUMusicDeviceFactory, SinSynthWithMidi) |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// SinSynthWithMidi::SinSynthWithMidi |
// |
// This synth has No inputs, One output |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
SinSynthWithMidi::SinSynthWithMidi(AudioUnit inComponentInstance) |
: SinSynth(inComponentInstance) |
{ |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// SinSynthWithMidi::~SinSynthWithMidi |
// |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
SinSynthWithMidi::~SinSynthWithMidi() |
{} |
OSStatus SinSynthWithMidi::GetPropertyInfo( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
UInt32 & outDataSize, |
Boolean & outWritable) |
{ |
if (inScope == kAudioUnitScope_Global) { |
if (inID == kAudioUnitProperty_MIDIOutputCallbackInfo) { |
outDataSize = sizeof(CFArrayRef); |
outWritable = false; |
return noErr; |
} else if (inID == kAudioUnitProperty_MIDIOutputCallback) { |
outDataSize = sizeof(AUMIDIOutputCallbackStruct); |
outWritable = true; |
return noErr; |
} |
} |
return SinSynth::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); |
} |
OSStatus SinSynthWithMidi::GetProperty( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
void * outData) |
{ |
if (inScope == kAudioUnitScope_Global) |
{ |
if (inID == kAudioUnitProperty_MIDIOutputCallbackInfo) { |
CFStringRef strs[1]; |
strs[0] = CFSTR("MIDI Callback"); |
CFArrayRef callbackArray = CFArrayCreate(NULL, (const void **)strs, 1, &kCFTypeArrayCallBacks); |
*(CFArrayRef *)outData = callbackArray; |
return noErr; |
} |
} |
return SinSynth::GetProperty (inID, inScope, inElement, outData); |
} |
OSStatus SinSynthWithMidi::SetProperty( AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
const void * inData, |
UInt32 inDataSize) |
{ |
if (inScope == kAudioUnitScope_Global) |
{ |
if (inID == kAudioUnitProperty_MIDIOutputCallback) { |
if (inDataSize < sizeof(AUMIDIOutputCallbackStruct)) return kAudioUnitErr_InvalidPropertyValue; |
AUMIDIOutputCallbackStruct *callbackStruct = (AUMIDIOutputCallbackStruct *)inData; |
mCallbackHelper.SetCallbackInfo(callbackStruct->midiOutputCallback, callbackStruct->userData); |
return noErr; |
} |
} |
return SinSynth::SetProperty(inID, inScope, inElement, inData, inDataSize); |
} |
OSStatus SinSynthWithMidi::HandleMidiEvent(UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) |
{ |
// snag the midi event and then store it in a vector |
mCallbackHelper.AddMIDIEvent(status, channel, data1, data2, inStartFrame); |
return AUMIDIBase::HandleMidiEvent(status, channel, data1, data2, inStartFrame); |
} |
OSStatus SinSynthWithMidi::Render( AudioUnitRenderActionFlags & ioActionFlags, |
const AudioTimeStamp & inTimeStamp, |
UInt32 inNumberFrames) |
{ |
OSStatus result = AUInstrumentBase::Render(ioActionFlags, inTimeStamp, inNumberFrames); |
if (result == noErr) { |
mCallbackHelper.FireAtTimeStamp(inTimeStamp); |
} |
return result; |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19