1 /*
2      File: CAAUMIDIMap.h
3  Abstract: Part of CoreAudio Utility Classes
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 #ifndef __CAAUMIDIMap_h_
48 #define __CAAUMIDIMap_h_
49 
50 #include <AudioUnit/AudioUnitProperties.h>
51 #include <algorithm>
52 
53 /*
54 enum {
55 	kAUParameterMIDIMapping_AnyChannelFlag		= (1L << 0),
56 		// If this flag is set and mStatus is a MIDI channel message, then the MIDI channel number
57 		// in the status byte is ignored; the mapping is from the specified MIDI message on ANY channel.
58 
59 	kAUParameterMIDIMapping_AnyNoteFlag			= (1L << 1),
60 		// If this flag is set and mStatus is a Note On, Note Off, or Polyphonic Pressure message,
61 		// the message's note number is ignored; the mapping is from ANY note number.
62 
63 	kAUParameterMIDIMapping_SubRange			= (1L << 2),
64 		// set this flag if the midi control should map only to a sub-range of the parameter's value
65 		// then specify that range in the mSubRangeMin and mSubRangeMax members
66 
67 	kAUParameterMIDIMapping_Toggle				= (1L << 3),
68 		// this is only useful for boolean typed parameters. When set, it means that the parameter's
69 		// value should be toggled (if true, become false and vice versa) when the represented MIDI message
70 		// is received
71 
72 	kAUParameterMIDIMapping_Bipolar				= (1L << 4),
73 		// this can be set to when mapping a MIDI Controller to indicate that the parameter (typically a boolean
74 		// style parameter) will only have its value changed to either the on or off state of a MIDI controller message
75 		// (0 < 64 is off, 64 < 127 is on) such as the sustain pedal. The seeting of the next flag
76 		// (kAUParameterMIDIMapping_Bipolar_On) determine whether the parameter is mapped to the on or off
77 		// state of the controller
78 	kAUParameterMIDIMapping_Bipolar_On			= (1L << 5)
79 		// only a valid flag if kAUParameterMIDIMapping_Bipolar is set
80 };
81 
82 // The reserved fields here are being used to reserve space (as well as align to 64 bit size) for future use
83 // When/If these fields are used, the names of the fields will be changed to reflect their functionality
84 // so, apps should NOT refer to these reserved fields directly by name
85 typedef struct AUParameterMIDIMapping
86 {
87 	AudioUnitScope			mScope;
88 	AudioUnitElement		mElement;
89 	AudioUnitParameterID	mParameterID;
90 	UInt32					mFlags;
91 	Float32					mSubRangeMin;
92 	Float32					mSubRangeMax;
93 	UInt8					mStatus;
94 	UInt8					mData1;
95 	UInt8					reserved1; // MUST be set to zero
96 	UInt8					reserved2; // MUST be set to zero
97 	UInt32					reserved3; // MUST be set to zero
98 } AUParameterMIDIMapping;
99 */
100 
101 /*
102 Parameter To MIDI Mapping Properties
103 These properties are used to:
104 Describe a current set of mappings between MIDI messages and Parameter value setting
105 Create a mapping between a parameter and a MIDI message through either:
106 - explicitly adding (or removing) the mapping
107 - telling the AU to hot-map the next MIDI message to a specified Parameter
108 	The same MIDI Message can map to one or more parameters
109 	One Parameter can be mapped from multiple MIDI messages
110 
111 	In general usage, these properties only apply to AU's that implement the MIDI API
112 	AU Instruments (type=='aumu') and Music Effects (type == 'aumf')
113 
114 	These properties are used in the Global scope. The scope and element members of the structure describe
115 	the scope and element of the parameter. In all usages, mScope, mElement and mParameterID must be
116 	correctly specified.
117 
118 
119 	* The AUParameterMIDIMapping Structure
120 
121 	Command				mStatus			mData1
122 	Note Off			0x8n			Note Num
123 	Note On				0x9n			Note Num
124 	Key Pressure		0xAn			Note Num
125 	Control Change		0xBn			ControllerID
126 	Patch Change		0xCn			Patch Num
127 	Channel Pressure	DxDn			0 (Unused)
128 	Pitch Bend			0xEn			0 (Unused)
129 
130 	(where n is 0-0xF to correspond to MIDI channels 1-16)
131 
132 		Details:
133 
134 	In general MIDI Commands can be mapped to either a specific channel as specified in the mStatus bit.
135 	If the kAUParameterMIDIMapping_AnyChannelFlag bit is set mStatus is a MIDI channel message, then the
136 	MIDI channel number in the status byte is ignored; the mapping is from the specified MIDI message on ANY channel.
137 
138 	For note commands (note on, note off, key pressure), the MIDI message can trigger either with just a specific
139 	note number, or any note number if the kAUParameterMIDIMapping_AnyNoteFlag bit is set. In these instances, the
140 	note number is used as the trigger value (for instance, a note message could be used to set the
141 											  cut off frequency of a filter).
142 
143  The Properties:
144 
145 	kAudioUnitProperty_AllParameterMIDIMappings							array of AUParameterMIDIMapping (read/write)
146 	This property is used to both retreive and set the current mapping state between (some/many/all of) its parameters
147 	and MIDI messages. When set, it should replace any previous mapped settings the AU had.
148 
149 	If this property is implemented by a non-MIDI capable AU (such as an 'aufx' type), then the property is
150 	read only, and recommends a suggested set of mappings for the host to perform. In this case, it is the
151 	host's responsibility to map MIDI message to the AU parameters. As described previously, there are a set
152 	of default mappings (see AudioToolbox/AUMIDIController.h) that the host can recommend to the user
153 	in this circumstance.
154 
155 	This property's size will be very dynamic, depending on the number of mappings currently in affect, so the
156 	caller should always get the size of the property first before retrieving it. The AU should return an error
157 	if the caller doesn't provide enough space to return all of the current mappings.
158 
159 	kAudioUnitProperty_AddParameterMIDIMapping							array of AUParameterMIDIMapping (write only)
160 	This property is used to Add mappings to the existing set of mappings the AU possesses. It does NOT replace
161 	any existing mappings.
162 
163 	kAudioUnitProperty_RemoveParameterMIDIMapping						array of AUParameterMIDIMapping (write only)
164 	This property is used to remove the specified mappings from the AU. If a mapping is specified that does not
165 	currently exist in the AU, then it should just be ignored.
166 
167 	kAudioUnitProperty_HotMapParameterMIDIMapping								AUParameterMIDIMapping (read/write)
168 	This property is used in two ways, determined by the value supplied by the caller.
169 	(1) If a mapping struct is provided, then that struct provides *all* of the information that the AU should
170 	use to map the parameter, *except* for the MIDI message. The AU should then listen for the next MIDI message
171 	and associate that MIDI message with the supplied AUParameter mapping. When this MIDI message is received and
172 	the mapping made, the AU should also issue a notification on this property
173 	(kAudioUnitProperty_HotMapParameterMIDIMapping) to indicate to the host that the mapping has been made. The host
174 	can then retrieve the mapping that was made by getting the value of this property.
175 
176 	To avoid possible confusion, it is recommended that once the host has retrieved this mapping (if it is
177 	presenting a UI to describe the mappings for example), that it then clears the mapping state as described next.
178 
179 	Thus, the only time this property will return a valid value is when the AU has made a mapping. If the AU's mapping
180 	state has been cleared (or it has not been asked to make a mapping), then the AU should return
181 	kAudioUnitErr_InvalidPropertyValue if the host tries to read this value.
182 
183 	(2) If the value passed in is NULL, then if the AU had a parameter that it was in the process of mapping, it
184 	should disregard that (stop listening to the MIDI messages to create a mapping) and discard the partially
185 	mapped struct. If the value is NULL and the AU is not in the process of mapping, the AU can ignore the request.
186 
187 	At all times, the _AllMappings property will completely describe the current known state of the AU's mappings
188 	of MIDI messages to parameters.
189 */
190 
191 
192 /*
193 	When mapping, it is recommended that LSB controllers are in general not mapped (ie. the controller range of 32 < 64)
194 	as many host parsers will map 14 bit control values. If you know (or can present an option) that the host deals with
195 	7 bit controllers only, then these controller ID's can be mapped of course.
196 */
197 
198 
199 struct MIDIValueTransformer {
200 	virtual double  tolinear(double) = 0;
201 	virtual double  fromlinear(double) = 0;
202 #if DEBUG
203 	// suppress warning
~MIDIValueTransformerMIDIValueTransformer204 	virtual ~MIDIValueTransformer() { }
205 #endif
206 };
207 
208 struct MIDILinearTransformer : public MIDIValueTransformer {
tolinearMIDILinearTransformer209 	virtual double  tolinear(double x) { return x; }
fromlinearMIDILinearTransformer210 	virtual double  fromlinear(double x) { return x; }
211 };
212 
213 struct MIDILogTransformer : public MIDIValueTransformer {
tolinearMIDILogTransformer214 	virtual double  tolinear(double x) { return log(std::max(x, .00001)); }
fromlinearMIDILogTransformer215 	virtual double  fromlinear(double x) { return exp(x); }
216 };
217 
218 struct MIDIExpTransformer : public MIDIValueTransformer {
tolinearMIDIExpTransformer219 	virtual double  tolinear(double x) { return exp(x); }
fromlinearMIDIExpTransformer220 	virtual double  fromlinear(double x) { return log(std::max(x, .00001)); }
221 };
222 
223 struct MIDISqrtTransformer : public MIDIValueTransformer {
tolinearMIDISqrtTransformer224 	virtual double  tolinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
fromlinearMIDISqrtTransformer225 	virtual double  fromlinear(double x) { return x < 0. ? -(x * x) : x * x; }
226 };
227 
228 struct MIDISquareTransformer : public MIDIValueTransformer {
tolinearMIDISquareTransformer229 	virtual double  tolinear(double x) { return x < 0. ? -(x * x) : x * x; }
fromlinearMIDISquareTransformer230 	virtual double  fromlinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
231 };
232 
233 struct MIDICubeRtTransformer : public MIDIValueTransformer {
tolinearMIDICubeRtTransformer234 	virtual double  tolinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
fromlinearMIDICubeRtTransformer235 	virtual double  fromlinear(double x) { return x * x * x; }
236 };
237 
238 struct MIDICubeTransformer : public MIDIValueTransformer {
tolinearMIDICubeTransformer239 	virtual double  tolinear(double x) { return x * x * x; }
fromlinearMIDICubeTransformer240 	virtual double  fromlinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
241 };
242 
243 
244 class CAAUMIDIMap : public AUParameterMIDIMapping {
245 
246 public:
247 // variables for more efficient parsing of MIDI to Param value
248 	Float32						mMinValue;
249 	Float32						mMaxValue;
250 	MIDIValueTransformer		*mTransType;
251 
252 // methods
253 	static MIDIValueTransformer *GetTransformer (UInt32 inFlags);
254 
CAAUMIDIMap()255 								CAAUMIDIMap() { memset(this, 0, sizeof(CAAUMIDIMap)); }
CAAUMIDIMap(const AUParameterMIDIMapping & inMap)256 								CAAUMIDIMap (const AUParameterMIDIMapping& inMap)
257 								{
258 									memset(this, 0, sizeof(CAAUMIDIMap));
259 									memcpy (this, &inMap, sizeof(inMap));
260 								}
CAAUMIDIMap(AudioUnitScope inScope,AudioUnitElement inElement,AudioUnitParameterID inParam)261 								CAAUMIDIMap (AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterID inParam)
262 								{
263 									memset(this, 0, sizeof(CAAUMIDIMap));
264 									mScope = inScope;
265 									mElement = inElement;
266 									mParameterID = inParam;
267 								}
268 
269 
IsValid()270 	bool						IsValid () const { return mStatus != 0; }
271 
272 	// returns -1 if any channel bit is set
Channel()273 	SInt32						Channel () const { return IsAnyChannel() ? -1 : (mStatus & 0xF); }
IsAnyChannel()274 	bool						IsAnyChannel () const {
275 									return mFlags & kAUParameterMIDIMapping_AnyChannelFlag;
276 								}
277 									// preserves the existing channel info in the status byte
278 									// preserves any previously set mFlags value
SetAnyChannel(bool inFlag)279 	void						SetAnyChannel (bool inFlag)
280 								{
281 									if (inFlag)
282 										mFlags |= kAUParameterMIDIMapping_AnyChannelFlag;
283 									else
284 										mFlags &= ~kAUParameterMIDIMapping_AnyChannelFlag;
285 								}
286 
IsAnyNote()287 	bool						IsAnyNote () const {
288 									return (mFlags & kAUParameterMIDIMapping_AnyNoteFlag) != 0;
289 								}
290 									// preserves the existing key num in the mData1 byte
291 									// preserves any previously set mFlags value
SetAnyNote(bool inFlag)292 	void						SetAnyNote (bool inFlag)
293 								{
294 									if (inFlag)
295 										mFlags |= kAUParameterMIDIMapping_AnyNoteFlag;
296 									else
297 										mFlags &= ~kAUParameterMIDIMapping_AnyNoteFlag;
298 								}
299 
IsToggle()300 	bool						IsToggle() const { return (mFlags & kAUParameterMIDIMapping_Toggle) != 0; }
SetToggle(bool inFlag)301 	void						SetToggle (bool inFlag)
302 								{
303 									if (inFlag)
304 										mFlags |= kAUParameterMIDIMapping_Toggle;
305 									else
306 										mFlags &= ~kAUParameterMIDIMapping_Toggle;
307 								}
308 
IsBipolar()309 	bool						IsBipolar() const { return (mFlags & kAUParameterMIDIMapping_Bipolar) != 0; }
310 									// inUseOnValue is valid ONLY if inFlag is true
311 	void						SetBipolar (bool inFlag, bool inUseOnValue = false)
312 								{
313 									if (inFlag) {
314 										mFlags |= kAUParameterMIDIMapping_Bipolar;
315 										if (inUseOnValue)
316 											mFlags |= kAUParameterMIDIMapping_Bipolar_On;
317 										else
318 											mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
319 									} else {
320 										mFlags &= ~kAUParameterMIDIMapping_Bipolar;
321 										mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
322 									}
323 								}
IsBipolar_OnValue()324 	bool						IsBipolar_OnValue () const { return (mFlags & kAUParameterMIDIMapping_Bipolar_On) != 0; }
325 
IsSubRange()326 	bool						IsSubRange () const { return (mFlags & kAUParameterMIDIMapping_SubRange) != 0; }
SetSubRange(Float32 inStartValue,Float32 inStopValue)327 	void						SetSubRange (Float32 inStartValue, Float32 inStopValue)
328 								{
329 									mFlags |= kAUParameterMIDIMapping_SubRange;
330 
331 									mSubRangeMin = inStartValue;
332 									mSubRangeMax = inStopValue;
333 								}
334 
SetParamRange(Float32 minValue,Float32 maxValue)335 	void						SetParamRange(Float32 minValue, Float32 maxValue)
336 								{
337 									mMinValue = minValue;
338 									mMaxValue = maxValue;
339 								}
340 
341 								// this will retain the subrange values previously set.
SetSubRange(bool inFlag)342 	void						SetSubRange (bool inFlag)
343 								{
344 									if (inFlag)
345 										mFlags |= kAUParameterMIDIMapping_SubRange;
346 									else
347 										mFlags &= ~kAUParameterMIDIMapping_SubRange;
348 								}
349 
IsAnyValue()350 	bool						IsAnyValue() const{return !IsBipolar();}
IsOnValue()351 	bool						IsOnValue() const{return IsBipolar_OnValue();}
IsOffValue()352 	bool						IsOffValue() const{return IsBipolar();}
353 
IsNoteOff()354 	bool						IsNoteOff () const { return ((mStatus & 0xF0) == 0x80); }
IsNoteOn()355 	bool						IsNoteOn () const { return ((mStatus & 0xF0) == 0x90); }
356 
IsKeyPressure()357 	bool						IsKeyPressure () const { return ((mStatus & 0xF0) == 0xA0); }
358 
IsKeyEvent()359 	bool						IsKeyEvent () const { return (mStatus > 0x7F) && (mStatus < 0xB0); }
360 
IsPatchChange()361 	bool						IsPatchChange () const { return ((mStatus & 0xF0) == 0xC0); }
IsChannelPressure()362 	bool						IsChannelPressure () const { return ((mStatus & 0xF0) == 0xD0); }
IsPitchBend()363 	bool						IsPitchBend () const { return ((mStatus & 0xF0) == 0xE0); }
IsControlChange()364 	bool						IsControlChange () const { return ((mStatus & 0xF0) == 0xB0); }
365 
366 
SetControllerOnValue()367 	void						SetControllerOnValue(){SetBipolar(true,true);}
SetControllerOffValue()368 	void						SetControllerOffValue(){SetBipolar(true,false);}
SetControllerAnyValue()369 	void						SetControllerAnyValue(){SetBipolar(false,false);}
370 
371 	// All of these Set calls will reset the mFlags field based on the
372 	// anyChannel param value
373 	void						SetNoteOff (UInt8 key, SInt8 channel, bool anyChannel = false)
374 								{
375 									mStatus = 0x80 | (channel & 0xF);
376 									mData1 = key;
377 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
378 
379 								}
380 
381 	void						SetNoteOn (UInt8 key, SInt8 channel, bool anyChannel = false)
382 								{
383 									mStatus = 0x90 | (channel & 0xF);
384 									mData1 = key;
385 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
386 								}
387 
388 	void						SetPolyKey (UInt8 key, SInt8 channel, bool anyChannel = false)
389 								{
390 									mStatus = 0xA0 | (channel & 0xF);
391 									mData1 = key;
392 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
393 								}
394 
395 	void						SetControlChange (UInt8 controllerID, SInt8 channel, bool anyChannel = false)
396 								{
397 									mStatus = 0xB0 | (channel & 0xF);
398 									mData1 = controllerID;
399 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
400 								}
401 
402 	void						SetPatchChange (UInt8 patchChange, SInt8 channel, bool anyChannel = false)
403 								{
404 									mStatus = 0xC0 | (channel & 0xF);
405 									mData1 = patchChange;
406 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
407 								}
408 
409 	void						SetChannelPressure (SInt8 channel, bool anyChannel = false)
410 								{
411 									mStatus = 0xD0 | (channel & 0xF);
412 									mData1 = 0;
413 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
414 								}
415 
416 	void						SetPitchBend (SInt8 channel, bool anyChannel = false)
417 								{
418 									mStatus = 0xE0 | (channel & 0xF);
419 									mData1 = 0;
420 									mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
421 								}
422 
423 
ParamValueFromMIDILinear(Float32 inLinearValue)424 	Float32						ParamValueFromMIDILinear (Float32		inLinearValue) const
425 	{
426 								Float32 low, high;
427 								if (IsSubRange()){
428 									low = mSubRangeMin;
429 									high = mSubRangeMax;
430 								}
431 								else {
432 									low = mMinValue;
433 									high = mMaxValue;
434 								}
435 
436 
437 								// WE ARE ASSUMING YOU HAVE SET THIS UP PROPERLY!!!!! (or this will crash cause it will be NULL)
438 								return (Float32)mTransType->fromlinear((inLinearValue * (high - low)) + low);
439 	}
440 
441 
442 		// The CALLER of this method must ensure that the status byte's MIDI Command (ignoring the channel) matches!!!
443 	bool						MIDI_Matches (UInt8 inChannel, UInt8 inData1, UInt8 inData2, Float32 &outLinear) const;
444 
445 	void						Print () const;
446 
447 	void						Save (CFPropertyListRef &outData) const;
448 	void						Restore (CFDictionaryRef inData);
449 
450 	static void					SaveAsMapPList (AudioUnit						inUnit,
451 											const AUParameterMIDIMapping		* inMappings,
452 											UInt32								inNumMappings,
453 											CFPropertyListRef					&outData,
454 											CFStringRef							inName = NULL);
455 
456 									// inNumMappings describes how much memory is allocated in outMappings
457 	static void					RestoreFromMapPList (const CFDictionaryRef			inData,
458 														AUParameterMIDIMapping		* outMappings,
459 														UInt32						inNumMappings);
460 
461 	static UInt32				NumberOfMaps (const CFDictionaryRef inData);
462 };
463 
464 
465 	// these sorting operations sort for run-time efficiency based on the MIDI messages
466 inline bool operator== (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
467 {
468 		// ignore channel first
469 	return (((a.mStatus & 0xF0) == (b.mStatus & 0xF0))
470 			&& (a.mData1 == b.mData1)
471 			&& ((a.mStatus & 0xF) == (b.mStatus & 0xf))  // now compare the channel
472 			&&  (a.mParameterID == b.mParameterID)
473 			&& (a.mElement == b.mElement)
474 			&& (a.mScope == b.mScope));
475 
476 	// reserved field comparisons - ignored until/if they are used
477 }
478 
479 inline bool operator< (const CAAUMIDIMap	&a, const CAAUMIDIMap &b)
480 {
481 	if ((a.mStatus & 0xF0) != (b.mStatus & 0xF0))
482 		return ((a.mStatus & 0xF0) < (b.mStatus & 0xF0));
483 
484 	if (a.mData1 != b.mData1)
485 		return (a.mData1 < b.mData1);
486 
487 	if ((a.mStatus & 0xF) != (b.mStatus & 0xf))  // now compare the channel
488 		return ((a.mStatus & 0xF) < (b.mStatus & 0xf));
489 
490 // reserved field comparisons - ignored until/if they are used
491 
492 //		we're sorting this by MIDI, so we don't really care how the rest is sorted
493 	return	((a.mParameterID < b.mParameterID)
494 				&& (a.mElement < b.mElement)
495 				&& (a.mScope < b.mScope));
496 }
497 
498 
499 
500 class CompareMIDIMap {
compare(const CAAUMIDIMap & a,const CAAUMIDIMap & b)501 	int compare (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
502 	{
503 		if ((a.mStatus & 0xF0) < (b.mStatus & 0xF0))
504 			return -1;
505 		if ((a.mStatus & 0xF0) > (b.mStatus & 0xF0))
506 			return 1;
507 
508 			// note event
509 		if (a.mStatus < 0xB0 || a.mStatus >= 0xD0)
510 			return 0;
511 		if (a.mData1 > b.mData1) return 1;
512 		if (a.mData1 < b.mData1) return -1;
513 		return 0;
514 	}
515 
516 public:
operator()517 	bool operator() (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
518 		return compare (a, b) < 0;
519 	}
Finish(const CAAUMIDIMap & a,const CAAUMIDIMap & b)520 	bool Finish (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
521 		return compare (a, b) != 0;
522 	}
523 };
524 
525 
526 /*
527 	usage: To find potential mapped events for a given status byte, where mMMapEvents is a sorted vec
528 	CompareMIDIMap comparObj;
529 	sortVecIter lower_iter = std::lower_bound(mMMapEvents.begin(), mMMapEvents.end(), inStatusByte, compareObj);
530 	for (;lower_iter < mMMapEvents.end(); ++lower_iter) {
531 		// then, see if we go out of the status byte range, using the Finish method
532 		if (compareObj.Finish(map, tempMap)) // tempMap is a CAAUMIDIMap object with the status/dataByte 1 set
533 			break;
534 	// ...
535 	}
536 
537 	in the for loop you call the MIDI_Matches call, to see if the MIDI event matches a given AUMIDIParam mapping
538 	special note: you HAVE to transform note on (with vel zero) events to the note off status byte
539 */
540 
541 #endif
542