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