1 /*
2 Copyright (C) 2016 Apple Inc. All Rights Reserved.
3 See LICENSE.txt for this sample’s licensing information
4
5 Abstract:
6 Part of Core Audio AUBase Classes
7 */
8
9 #include "AUMIDIBase.h"
10 #include <CoreMIDI/CoreMIDI.h>
11 #include "CAXException.h"
12
13 //temporaray location
14 enum
15 {
16 kMidiMessage_NoteOff = 0x80,
17 kMidiMessage_NoteOn = 0x90,
18 kMidiMessage_PolyPressure = 0xA0,
19 kMidiMessage_ControlChange = 0xB0,
20 kMidiMessage_ProgramChange = 0xC0,
21 kMidiMessage_ChannelPressure = 0xD0,
AUInstrumentBase(AudioComponentInstance inInstance,UInt32 numInputs,UInt32 numOutputs,UInt32 numGroups,UInt32 numParts)22 kMidiMessage_PitchWheel = 0xE0,
23
24 kMidiController_AllSoundOff = 120,
25 kMidiController_ResetAllControllers = 121,
26 kMidiController_AllNotesOff = 123
27 };
28
29 AUMIDIBase::AUMIDIBase(AUBase* inBase)
30 : mAUBaseInstance (*inBase)
31 {
32 #if CA_AUTO_MIDI_MAP
33 mMapManager = new CAAUMIDIMapManager();
34 #endif
35 }
36
37 AUMIDIBase::~AUMIDIBase()
38 {
39 #if CA_AUTO_MIDI_MAP
40 if (mMapManager)
41 delete mMapManager;
42 #endif
43 }
44
45 #if TARGET_API_MAC_OSX
~AUInstrumentBase()46 OSStatus AUMIDIBase::DelegateGetPropertyInfo(AudioUnitPropertyID inID,
47 AudioUnitScope inScope,
48 AudioUnitElement inElement,
49 UInt32 & outDataSize,
50 Boolean & outWritable)
51 {
52 OSStatus result = noErr;
53
54 switch (inID) {
55 #if !TARGET_OS_IPHONE
56 case kMusicDeviceProperty_MIDIXMLNames:
57 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
58 ca_require(inElement == 0, InvalidElement);
59 if (GetXMLNames(NULL) == noErr) {
60 outDataSize = sizeof(CFURLRef);
61 outWritable = false;
62 } else
63 result = kAudioUnitErr_InvalidProperty;
64 break;
65 #endif
66 #if CA_AUTO_MIDI_MAP
67 case kAudioUnitProperty_AllParameterMIDIMappings:
68 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
69 ca_require(inElement == 0, InvalidElement);
70 outWritable = true;
71 outDataSize = sizeof (AUParameterMIDIMapping)*mMapManager->NumMaps();
72 result = noErr;
73 break;
74
75 case kAudioUnitProperty_HotMapParameterMIDIMapping:
76 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
77 ca_require(inElement == 0, InvalidElement);
78 outWritable = true;
79 outDataSize = sizeof (AUParameterMIDIMapping);
80 result = noErr;
81 break;
82
83 case kAudioUnitProperty_AddParameterMIDIMapping:
84 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
85 ca_require(inElement == 0, InvalidElement);
86 outWritable = true;
87 outDataSize = sizeof (AUParameterMIDIMapping);
88 result = noErr;
89 break;
90
91 case kAudioUnitProperty_RemoveParameterMIDIMapping:
92 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
93 ca_require(inElement == 0, InvalidElement);
94 outWritable = true;
95 outDataSize = sizeof (AUParameterMIDIMapping);
96 result = noErr;
97 break;
98 #endif
99
100 default:
101 result = kAudioUnitErr_InvalidProperty;
102 break;
103 }
104 return result;
105
106 #if CA_AUTO_MIDI_MAP || (!TARGET_OS_IPHONE)
107 InvalidScope:
108 return kAudioUnitErr_InvalidScope;
109 InvalidElement:
110 return kAudioUnitErr_InvalidElement;
111 #endif
112 }
113
114 OSStatus AUMIDIBase::DelegateGetProperty( AudioUnitPropertyID inID,
115 AudioUnitScope inScope,
116 AudioUnitElement inElement,
117 void * outData)
118 {
119 OSStatus result;
120
121 switch (inID) {
122 #if !TARGET_OS_IPHONE
123 case kMusicDeviceProperty_MIDIXMLNames:
Initialize()124 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
125 ca_require(inElement == 0, InvalidElement);
126 result = GetXMLNames((CFURLRef *)outData);
127 break;
128 #endif
129 #if CA_AUTO_MIDI_MAP
130 case kAudioUnitProperty_AllParameterMIDIMappings:{
131 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
132 ca_require(inElement == 0, InvalidElement);
133 AUParameterMIDIMapping* maps = (static_cast<AUParameterMIDIMapping*>(outData));
134 mMapManager->GetMaps(maps);
135 // printf ("GETTING MAPS\n");
136 // mMapManager->Print();
137 result = noErr;
138 break;
139 }
140
141 case kAudioUnitProperty_HotMapParameterMIDIMapping:{
142 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
143 ca_require(inElement == 0, InvalidElement);
144 AUParameterMIDIMapping * map = (static_cast<AUParameterMIDIMapping*>(outData));
Cleanup()145 mMapManager->GetHotParameterMap (*map);
146 result = noErr;
147 break;
148 }
149 #endif
150
Reset(AudioUnitScope inScope,AudioUnitElement inElement)151 default:
152 result = kAudioUnitErr_InvalidProperty;
153 break;
154 }
155 return result;
156
157 #if CA_AUTO_MIDI_MAP || (!TARGET_OS_IPHONE)
158 InvalidScope:
159 return kAudioUnitErr_InvalidScope;
160 InvalidElement:
161 return kAudioUnitErr_InvalidElement;
162 #endif
163 }
164
165 OSStatus AUMIDIBase::DelegateSetProperty( AudioUnitPropertyID inID,
166 AudioUnitScope inScope,
167 AudioUnitElement inElement,
168 const void * inData,
169 UInt32 inDataSize)
170 {
171 OSStatus result;
172
173 switch (inID) {
174 #if CA_AUTO_MIDI_MAP
175 case kAudioUnitProperty_AddParameterMIDIMapping:{
176 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
177 ca_require(inElement == 0, InvalidElement);
178 AUParameterMIDIMapping * maps = (AUParameterMIDIMapping*)inData;
179 mMapManager->SortedInsertToParamaterMaps (maps, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
180 mAUBaseInstance.PropertyChanged (kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
181 result = noErr;
182 break;
PerformEvents(const AudioTimeStamp & inTimeStamp)183 }
184
185 case kAudioUnitProperty_RemoveParameterMIDIMapping:{
186 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
187 ca_require(inElement == 0, InvalidElement);
188 AUParameterMIDIMapping * maps = (AUParameterMIDIMapping*)inData;
189 bool didChange;
190 mMapManager->SortedRemoveFromParameterMaps(maps, (inDataSize / sizeof(AUParameterMIDIMapping)), didChange);
191 if (didChange)
192 mAUBaseInstance.PropertyChanged (kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
193 result = noErr;
194 break;
195 }
196
197 case kAudioUnitProperty_HotMapParameterMIDIMapping:{
198 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
199 ca_require(inElement == 0, InvalidElement);
200 AUParameterMIDIMapping & map = *((AUParameterMIDIMapping*)inData);
201 mMapManager->SetHotMapping (map);
202 result = noErr;
203 break;
204 }
205 case kAudioUnitProperty_AllParameterMIDIMappings:{
206 ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
207 ca_require(inElement == 0, InvalidElement);
208 AUParameterMIDIMapping * mappings = (AUParameterMIDIMapping*)inData;
209 mMapManager->ReplaceAllMaps (mappings, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
210 result = noErr;
211 break;
212 }
213 #endif
214
215 default:
216 result = kAudioUnitErr_InvalidProperty;
217 break;
218 }
219 return result;
220 #if CA_AUTO_MIDI_MAP
221 InvalidScope:
222 return kAudioUnitErr_InvalidScope;
223 InvalidElement:
224 return kAudioUnitErr_InvalidElement;
225 #endif
226 }
227
228
229
230 #endif //TARGET_API_MAC_OSX
231
232
233 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
234 #pragma mark ____MidiDispatch
235
236
237 inline const Byte * NextMIDIEvent(const Byte *event, const Byte *end)
238 {
239 Byte c = *event;
240 switch (c >> 4) {
Render(AudioUnitRenderActionFlags & ioActionFlags,const AudioTimeStamp & inTimeStamp,UInt32 inNumberFrames)241 default: // data byte -- assume in sysex
242 while ((*++event & 0x80) == 0 && event < end)
243 ;
244 break;
245 case 0x8:
246 case 0x9:
247 case 0xA:
248 case 0xB:
249 case 0xE:
250 event += 3;
251 break;
252 case 0xC:
253 case 0xD:
254 event += 2;
255 break;
256 case 0xF:
257 switch (c) {
258 case 0xF0:
259 while ((*++event & 0x80) == 0 && event < end)
260 ;
261 break;
262 case 0xF1:
263 case 0xF3:
264 event += 2;
265 break;
266 case 0xF2:
267 event += 3;
268 break;
269 default:
270 ++event;
271 break;
272 }
ValidFormat(AudioUnitScope inScope,AudioUnitElement inElement,const CAStreamBasicDescription & inNewFormat)273 }
274 return (event >= end) ? end : event;
275 }
276
277 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
278 // AUMIDIBase::HandleMIDIPacketList
279 //
280 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
281 OSStatus AUMIDIBase::HandleMIDIPacketList(const MIDIPacketList *pktlist)
282 {
283 if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized;
284
285 int nPackets = pktlist->numPackets;
286 const MIDIPacket *pkt = pktlist->packet;
287
288 while (nPackets-- > 0) {
289 const Byte *event = pkt->data, *packetEnd = event + pkt->length;
290 long startFrame = (long)pkt->timeStamp;
StreamFormatWritable(AudioUnitScope scope,AudioUnitElement element)291 while (event < packetEnd) {
292 Byte status = event[0];
293 if (status & 0x80) {
294 // really a status byte (not sysex continuation)
295 HandleMidiEvent(status & 0xF0, status & 0x0F, event[1], event[2], static_cast<UInt32>(startFrame));
296 // note that we're generating a bogus channel number for system messages (0xF0-FF)
297 }
298 event = NextMIDIEvent(event, packetEnd);
299 }
300 pkt = reinterpret_cast<const MIDIPacket *>(packetEnd);
301 }
302 return noErr;
303 }
304
GetPartElement(AudioUnitElement inPartElement)305 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306 // AUMIDIBase::HandleMidiEvent
307 //
308 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309 OSStatus AUMIDIBase::HandleMidiEvent(UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
310 {
311 if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized;
312
313 #if CA_AUTO_MIDI_MAP
314 // you potentially have a choice to make here - if a param mapping matches, do you still want to process the
315 // MIDI event or not. The default behaviour is to continue on with the MIDI event.
316 if (mMapManager->HandleHotMapping (status, channel, data1, mAUBaseInstance)) {
317 mAUBaseInstance.PropertyChanged (kAudioUnitProperty_HotMapParameterMIDIMapping, kAudioUnitScope_Global, 0);
318 }
319 else {
320 mMapManager->FindParameterMapEventMatch(status, channel, data1, data2, inStartFrame, mAUBaseInstance);
321 }
322 #endif
323
324 OSStatus result = noErr;
325
326 switch(status)
327 {
328 case kMidiMessage_NoteOn:
329 if(data2)
330 {
331 result = HandleNoteOn(channel, data1, data2, inStartFrame);
332 }
333 else
334 {
335 // zero velocity translates to note off
336 result = HandleNoteOff(channel, data1, data2, inStartFrame);
337 }
338 break;
339
340 case kMidiMessage_NoteOff:
341 result = HandleNoteOff(channel, data1, data2, inStartFrame);
342 break;
343
344 default:
345 result = HandleNonNoteEvent (status, channel, data1, data2, inStartFrame);
346 break;
347 }
348
349 return result;
350 }
351
352 OSStatus AUMIDIBase::HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
353 {
354 OSStatus result = noErr;
355
356 switch (status)
357 {
358 case kMidiMessage_PitchWheel:
359 result = HandlePitchWheel(channel, data1, data2, inStartFrame);
GetElForNoteID(NoteInstanceID inNoteID)360 break;
361
362 case kMidiMessage_ProgramChange:
363 result = HandleProgramChange(channel, data1);
364 break;
365
366 case kMidiMessage_ChannelPressure:
367 result = HandleChannelPressure(channel, data1, inStartFrame);
368 break;
369
370 case kMidiMessage_ControlChange:
371 {
372 switch (data1) {
373 case kMidiController_AllNotesOff:
374 result = HandleAllNotesOff(channel);
375 break;
StartNote(MusicDeviceInstrumentID inInstrument,MusicDeviceGroupID inGroupID,NoteInstanceID * outNoteInstanceID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams & inParams)376
377 case kMidiController_ResetAllControllers:
378 result = HandleResetAllControllers(channel);
379 break;
380
381 case kMidiController_AllSoundOff:
382 result = HandleAllSoundOff(channel);
383 break;
384
385 default:
386 result = HandleControlChange(channel, data1, data2, inStartFrame);
387 break;
388 }
389 break;
390 }
391 case kMidiMessage_PolyPressure:
392 result = HandlePolyPressure (channel, data1, data2, inStartFrame);
393 break;
394 }
395 return result;
396 }
397
398 OSStatus AUMIDIBase::SysEx (const UInt8 * inData,
399 UInt32 inLength)
400 {
401 if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized;
402
403 return HandleSysEx(inData, inLength );
404 }
405
406
407
408 #if TARGET_OS_MAC
409 #if __LP64__
410 // comp instance, parameters in forward order
411 #define PARAM(_typ, _name, _index, _nparams) \
412 _typ _name = *(_typ *)¶ms->params[_index + 1];
413 #else
414 // parameters in reverse order, then comp instance
415 #define PARAM(_typ, _name, _index, _nparams) \
416 _typ _name = *(_typ *)¶ms->params[_nparams - 1 - _index];
417 #endif
418 #elif TARGET_OS_WIN32
419 // (no comp instance), parameters in forward order
420 #define PARAM(_typ, _name, _index, _nparams) \
StopNote(MusicDeviceGroupID inGroupID,NoteInstanceID inNoteInstanceID,UInt32 inOffsetSampleFrame)421 _typ _name = *(_typ *)¶ms->params[_index];
422 #endif
423
424 #if !CA_USE_AUDIO_PLUGIN_ONLY
425 OSStatus AUMIDIBase::ComponentEntryDispatch( ComponentParameters * params,
426 AUMIDIBase * This)
427 {
428 if (This == NULL) return kAudio_ParamError;
429
430 OSStatus result;
431
432 switch (params->what) {
433 case kMusicDeviceMIDIEventSelect:
434 {
435 PARAM(UInt32, pbinStatus, 0, 4);
436 PARAM(UInt32, pbinData1, 1, 4);
437 PARAM(UInt32, pbinData2, 2, 4);
438 PARAM(UInt32, pbinOffsetSampleFrame, 3, 4);
439 result = This->MIDIEvent(pbinStatus, pbinData1, pbinData2, pbinOffsetSampleFrame);
440 }
441 break;
442 case kMusicDeviceSysExSelect:
443 {
444 PARAM(const UInt8 *, pbinData, 0, 2);
445 PARAM(UInt32, pbinLength, 1, 2);
446 result = This->SysEx(pbinData, pbinLength);
447 }
448 break;
449
450 default:
451 result = badComponentSelector;
452 break;
453 }
454
SendPedalEvent(MusicDeviceGroupID inGroupID,UInt32 inEventType,UInt32 inOffsetSampleFrame)455 return result;
456 }
457 #endif
458