summaryrefslogtreecommitdiff
path: root/libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h
diff options
context:
space:
mode:
Diffstat (limited to 'libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h')
-rw-r--r--libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h541
1 files changed, 541 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h b/libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h
new file mode 100644
index 0000000000..a53fdf70dc
--- /dev/null
+++ b/libs/appleutility/CoreAudio/PublicUtility/CAAUMIDIMap.h
@@ -0,0 +1,541 @@
+/*
+ File: CAAUMIDIMap.h
+ Abstract: Part of CoreAudio Utility Classes
+ Version: 1.1
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
+ Inc. ("Apple") in consideration of your agreement to the following
+ terms, and your use, installation, modification or redistribution of
+ this Apple software constitutes acceptance of these terms. If you do
+ not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
+ be used to endorse or promote products derived from the Apple Software
+ without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or
+ implied, are granted by Apple herein, including but not limited to any
+ patent rights that may be infringed by your derivative works or by other
+ works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2014 Apple Inc. All Rights Reserved.
+
+*/
+#ifndef __CAAUMIDIMap_h_
+#define __CAAUMIDIMap_h_
+
+#include <AudioUnit/AudioUnitProperties.h>
+#include <algorithm>
+
+/*
+enum {
+ kAUParameterMIDIMapping_AnyChannelFlag = (1L << 0),
+ // If this flag is set and mStatus is a MIDI channel message, then the MIDI channel number
+ // in the status byte is ignored; the mapping is from the specified MIDI message on ANY channel.
+
+ kAUParameterMIDIMapping_AnyNoteFlag = (1L << 1),
+ // If this flag is set and mStatus is a Note On, Note Off, or Polyphonic Pressure message,
+ // the message's note number is ignored; the mapping is from ANY note number.
+
+ kAUParameterMIDIMapping_SubRange = (1L << 2),
+ // set this flag if the midi control should map only to a sub-range of the parameter's value
+ // then specify that range in the mSubRangeMin and mSubRangeMax members
+
+ kAUParameterMIDIMapping_Toggle = (1L << 3),
+ // this is only useful for boolean typed parameters. When set, it means that the parameter's
+ // value should be toggled (if true, become false and vice versa) when the represented MIDI message
+ // is received
+
+ kAUParameterMIDIMapping_Bipolar = (1L << 4),
+ // this can be set to when mapping a MIDI Controller to indicate that the parameter (typically a boolean
+ // style parameter) will only have its value changed to either the on or off state of a MIDI controller message
+ // (0 < 64 is off, 64 < 127 is on) such as the sustain pedal. The seeting of the next flag
+ // (kAUParameterMIDIMapping_Bipolar_On) determine whether the parameter is mapped to the on or off
+ // state of the controller
+ kAUParameterMIDIMapping_Bipolar_On = (1L << 5)
+ // only a valid flag if kAUParameterMIDIMapping_Bipolar is set
+};
+
+// The reserved fields here are being used to reserve space (as well as align to 64 bit size) for future use
+// When/If these fields are used, the names of the fields will be changed to reflect their functionality
+// so, apps should NOT refer to these reserved fields directly by name
+typedef struct AUParameterMIDIMapping
+{
+ AudioUnitScope mScope;
+ AudioUnitElement mElement;
+ AudioUnitParameterID mParameterID;
+ UInt32 mFlags;
+ Float32 mSubRangeMin;
+ Float32 mSubRangeMax;
+ UInt8 mStatus;
+ UInt8 mData1;
+ UInt8 reserved1; // MUST be set to zero
+ UInt8 reserved2; // MUST be set to zero
+ UInt32 reserved3; // MUST be set to zero
+} AUParameterMIDIMapping;
+*/
+
+/*
+Parameter To MIDI Mapping Properties
+These properties are used to:
+Describe a current set of mappings between MIDI messages and Parameter value setting
+Create a mapping between a parameter and a MIDI message through either:
+- explicitly adding (or removing) the mapping
+- telling the AU to hot-map the next MIDI message to a specified Parameter
+ The same MIDI Message can map to one or more parameters
+ One Parameter can be mapped from multiple MIDI messages
+
+ In general usage, these properties only apply to AU's that implement the MIDI API
+ AU Instruments (type=='aumu') and Music Effects (type == 'aumf')
+
+ These properties are used in the Global scope. The scope and element members of the structure describe
+ the scope and element of the parameter. In all usages, mScope, mElement and mParameterID must be
+ correctly specified.
+
+
+ * The AUParameterMIDIMapping Structure
+
+ Command mStatus mData1
+ Note Off 0x8n Note Num
+ Note On 0x9n Note Num
+ Key Pressure 0xAn Note Num
+ Control Change 0xBn ControllerID
+ Patch Change 0xCn Patch Num
+ Channel Pressure DxDn 0 (Unused)
+ Pitch Bend 0xEn 0 (Unused)
+
+ (where n is 0-0xF to correspond to MIDI channels 1-16)
+
+ Details:
+
+ In general MIDI Commands can be mapped to either a specific channel as specified in the mStatus bit.
+ If the kAUParameterMIDIMapping_AnyChannelFlag bit is set mStatus is a MIDI channel message, then the
+ MIDI channel number in the status byte is ignored; the mapping is from the specified MIDI message on ANY channel.
+
+ For note commands (note on, note off, key pressure), the MIDI message can trigger either with just a specific
+ note number, or any note number if the kAUParameterMIDIMapping_AnyNoteFlag bit is set. In these instances, the
+ note number is used as the trigger value (for instance, a note message could be used to set the
+ cut off frequency of a filter).
+
+ The Properties:
+
+ kAudioUnitProperty_AllParameterMIDIMappings array of AUParameterMIDIMapping (read/write)
+ This property is used to both retreive and set the current mapping state between (some/many/all of) its parameters
+ and MIDI messages. When set, it should replace any previous mapped settings the AU had.
+
+ If this property is implemented by a non-MIDI capable AU (such as an 'aufx' type), then the property is
+ read only, and recommends a suggested set of mappings for the host to perform. In this case, it is the
+ host's responsibility to map MIDI message to the AU parameters. As described previously, there are a set
+ of default mappings (see AudioToolbox/AUMIDIController.h) that the host can recommend to the user
+ in this circumstance.
+
+ This property's size will be very dynamic, depending on the number of mappings currently in affect, so the
+ caller should always get the size of the property first before retrieving it. The AU should return an error
+ if the caller doesn't provide enough space to return all of the current mappings.
+
+ kAudioUnitProperty_AddParameterMIDIMapping array of AUParameterMIDIMapping (write only)
+ This property is used to Add mappings to the existing set of mappings the AU possesses. It does NOT replace
+ any existing mappings.
+
+ kAudioUnitProperty_RemoveParameterMIDIMapping array of AUParameterMIDIMapping (write only)
+ This property is used to remove the specified mappings from the AU. If a mapping is specified that does not
+ currently exist in the AU, then it should just be ignored.
+
+ kAudioUnitProperty_HotMapParameterMIDIMapping AUParameterMIDIMapping (read/write)
+ This property is used in two ways, determined by the value supplied by the caller.
+ (1) If a mapping struct is provided, then that struct provides *all* of the information that the AU should
+ use to map the parameter, *except* for the MIDI message. The AU should then listen for the next MIDI message
+ and associate that MIDI message with the supplied AUParameter mapping. When this MIDI message is received and
+ the mapping made, the AU should also issue a notification on this property
+ (kAudioUnitProperty_HotMapParameterMIDIMapping) to indicate to the host that the mapping has been made. The host
+ can then retrieve the mapping that was made by getting the value of this property.
+
+ To avoid possible confusion, it is recommended that once the host has retrieved this mapping (if it is
+ presenting a UI to describe the mappings for example), that it then clears the mapping state as described next.
+
+ Thus, the only time this property will return a valid value is when the AU has made a mapping. If the AU's mapping
+ state has been cleared (or it has not been asked to make a mapping), then the AU should return
+ kAudioUnitErr_InvalidPropertyValue if the host tries to read this value.
+
+ (2) If the value passed in is NULL, then if the AU had a parameter that it was in the process of mapping, it
+ should disregard that (stop listening to the MIDI messages to create a mapping) and discard the partially
+ mapped struct. If the value is NULL and the AU is not in the process of mapping, the AU can ignore the request.
+
+ At all times, the _AllMappings property will completely describe the current known state of the AU's mappings
+ of MIDI messages to parameters.
+*/
+
+
+/*
+ When mapping, it is recommended that LSB controllers are in general not mapped (ie. the controller range of 32 < 64)
+ as many host parsers will map 14 bit control values. If you know (or can present an option) that the host deals with
+ 7 bit controllers only, then these controller ID's can be mapped of course.
+*/
+
+
+struct MIDIValueTransformer {
+ virtual double tolinear(double) = 0;
+ virtual double fromlinear(double) = 0;
+#if DEBUG
+ // suppress warning
+ virtual ~MIDIValueTransformer() { }
+#endif
+};
+
+struct MIDILinearTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return x; }
+ virtual double fromlinear(double x) { return x; }
+};
+
+struct MIDILogTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return log(std::max(x, .00001)); }
+ virtual double fromlinear(double x) { return exp(x); }
+};
+
+struct MIDIExpTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return exp(x); }
+ virtual double fromlinear(double x) { return log(std::max(x, .00001)); }
+};
+
+struct MIDISqrtTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
+ virtual double fromlinear(double x) { return x < 0. ? -(x * x) : x * x; }
+};
+
+struct MIDISquareTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return x < 0. ? -(x * x) : x * x; }
+ virtual double fromlinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
+};
+
+struct MIDICubeRtTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
+ virtual double fromlinear(double x) { return x * x * x; }
+};
+
+struct MIDICubeTransformer : public MIDIValueTransformer {
+ virtual double tolinear(double x) { return x * x * x; }
+ virtual double fromlinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
+};
+
+
+class CAAUMIDIMap : public AUParameterMIDIMapping {
+
+public:
+// variables for more efficient parsing of MIDI to Param value
+ Float32 mMinValue;
+ Float32 mMaxValue;
+ MIDIValueTransformer *mTransType;
+
+// methods
+ static MIDIValueTransformer *GetTransformer (UInt32 inFlags);
+
+ CAAUMIDIMap() { memset(this, 0, sizeof(CAAUMIDIMap)); }
+ CAAUMIDIMap (const AUParameterMIDIMapping& inMap)
+ {
+ memset(this, 0, sizeof(CAAUMIDIMap));
+ memcpy (this, &inMap, sizeof(inMap));
+ }
+ CAAUMIDIMap (AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterID inParam)
+ {
+ memset(this, 0, sizeof(CAAUMIDIMap));
+ mScope = inScope;
+ mElement = inElement;
+ mParameterID = inParam;
+ }
+
+
+ bool IsValid () const { return mStatus != 0; }
+
+ // returns -1 if any channel bit is set
+ SInt32 Channel () const { return IsAnyChannel() ? -1 : (mStatus & 0xF); }
+ bool IsAnyChannel () const {
+ return mFlags & kAUParameterMIDIMapping_AnyChannelFlag;
+ }
+ // preserves the existing channel info in the status byte
+ // preserves any previously set mFlags value
+ void SetAnyChannel (bool inFlag)
+ {
+ if (inFlag)
+ mFlags |= kAUParameterMIDIMapping_AnyChannelFlag;
+ else
+ mFlags &= ~kAUParameterMIDIMapping_AnyChannelFlag;
+ }
+
+ bool IsAnyNote () const {
+ return (mFlags & kAUParameterMIDIMapping_AnyNoteFlag) != 0;
+ }
+ // preserves the existing key num in the mData1 byte
+ // preserves any previously set mFlags value
+ void SetAnyNote (bool inFlag)
+ {
+ if (inFlag)
+ mFlags |= kAUParameterMIDIMapping_AnyNoteFlag;
+ else
+ mFlags &= ~kAUParameterMIDIMapping_AnyNoteFlag;
+ }
+
+ bool IsToggle() const { return (mFlags & kAUParameterMIDIMapping_Toggle) != 0; }
+ void SetToggle (bool inFlag)
+ {
+ if (inFlag)
+ mFlags |= kAUParameterMIDIMapping_Toggle;
+ else
+ mFlags &= ~kAUParameterMIDIMapping_Toggle;
+ }
+
+ bool IsBipolar() const { return (mFlags & kAUParameterMIDIMapping_Bipolar) != 0; }
+ // inUseOnValue is valid ONLY if inFlag is true
+ void SetBipolar (bool inFlag, bool inUseOnValue = false)
+ {
+ if (inFlag) {
+ mFlags |= kAUParameterMIDIMapping_Bipolar;
+ if (inUseOnValue)
+ mFlags |= kAUParameterMIDIMapping_Bipolar_On;
+ else
+ mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
+ } else {
+ mFlags &= ~kAUParameterMIDIMapping_Bipolar;
+ mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
+ }
+ }
+ bool IsBipolar_OnValue () const { return (mFlags & kAUParameterMIDIMapping_Bipolar_On) != 0; }
+
+ bool IsSubRange () const { return (mFlags & kAUParameterMIDIMapping_SubRange) != 0; }
+ void SetSubRange (Float32 inStartValue, Float32 inStopValue)
+ {
+ mFlags |= kAUParameterMIDIMapping_SubRange;
+
+ mSubRangeMin = inStartValue;
+ mSubRangeMax = inStopValue;
+ }
+
+ void SetParamRange(Float32 minValue, Float32 maxValue)
+ {
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ // this will retain the subrange values previously set.
+ void SetSubRange (bool inFlag)
+ {
+ if (inFlag)
+ mFlags |= kAUParameterMIDIMapping_SubRange;
+ else
+ mFlags &= ~kAUParameterMIDIMapping_SubRange;
+ }
+
+ bool IsAnyValue() const{return !IsBipolar();}
+ bool IsOnValue() const{return IsBipolar_OnValue();}
+ bool IsOffValue() const{return IsBipolar();}
+
+ bool IsNoteOff () const { return ((mStatus & 0xF0) == 0x80); }
+ bool IsNoteOn () const { return ((mStatus & 0xF0) == 0x90); }
+
+ bool IsKeyPressure () const { return ((mStatus & 0xF0) == 0xA0); }
+
+ bool IsKeyEvent () const { return (mStatus > 0x7F) && (mStatus < 0xB0); }
+
+ bool IsPatchChange () const { return ((mStatus & 0xF0) == 0xC0); }
+ bool IsChannelPressure () const { return ((mStatus & 0xF0) == 0xD0); }
+ bool IsPitchBend () const { return ((mStatus & 0xF0) == 0xE0); }
+ bool IsControlChange () const { return ((mStatus & 0xF0) == 0xB0); }
+
+
+ void SetControllerOnValue(){SetBipolar(true,true);}
+ void SetControllerOffValue(){SetBipolar(true,false);}
+ void SetControllerAnyValue(){SetBipolar(false,false);}
+
+ // All of these Set calls will reset the mFlags field based on the
+ // anyChannel param value
+ void SetNoteOff (UInt8 key, SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0x80 | (channel & 0xF);
+ mData1 = key;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+
+ }
+
+ void SetNoteOn (UInt8 key, SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0x90 | (channel & 0xF);
+ mData1 = key;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+ void SetPolyKey (UInt8 key, SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0xA0 | (channel & 0xF);
+ mData1 = key;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+ void SetControlChange (UInt8 controllerID, SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0xB0 | (channel & 0xF);
+ mData1 = controllerID;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+ void SetPatchChange (UInt8 patchChange, SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0xC0 | (channel & 0xF);
+ mData1 = patchChange;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+ void SetChannelPressure (SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0xD0 | (channel & 0xF);
+ mData1 = 0;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+ void SetPitchBend (SInt8 channel, bool anyChannel = false)
+ {
+ mStatus = 0xE0 | (channel & 0xF);
+ mData1 = 0;
+ mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
+ }
+
+
+ Float32 ParamValueFromMIDILinear (Float32 inLinearValue) const
+ {
+ Float32 low, high;
+ if (IsSubRange()){
+ low = mSubRangeMin;
+ high = mSubRangeMax;
+ }
+ else {
+ low = mMinValue;
+ high = mMaxValue;
+ }
+
+
+ // WE ARE ASSUMING YOU HAVE SET THIS UP PROPERLY!!!!! (or this will crash cause it will be NULL)
+ return (Float32)mTransType->fromlinear((inLinearValue * (high - low)) + low);
+ }
+
+
+ // The CALLER of this method must ensure that the status byte's MIDI Command (ignoring the channel) matches!!!
+ bool MIDI_Matches (UInt8 inChannel, UInt8 inData1, UInt8 inData2, Float32 &outLinear) const;
+
+ void Print () const;
+
+ void Save (CFPropertyListRef &outData) const;
+ void Restore (CFDictionaryRef inData);
+
+ static void SaveAsMapPList (AudioUnit inUnit,
+ const AUParameterMIDIMapping * inMappings,
+ UInt32 inNumMappings,
+ CFPropertyListRef &outData,
+ CFStringRef inName = NULL);
+
+ // inNumMappings describes how much memory is allocated in outMappings
+ static void RestoreFromMapPList (const CFDictionaryRef inData,
+ AUParameterMIDIMapping * outMappings,
+ UInt32 inNumMappings);
+
+ static UInt32 NumberOfMaps (const CFDictionaryRef inData);
+};
+
+
+ // these sorting operations sort for run-time efficiency based on the MIDI messages
+inline bool operator== (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
+{
+ // ignore channel first
+ return (((a.mStatus & 0xF0) == (b.mStatus & 0xF0))
+ && (a.mData1 == b.mData1)
+ && ((a.mStatus & 0xF) == (b.mStatus & 0xf)) // now compare the channel
+ && (a.mParameterID == b.mParameterID)
+ && (a.mElement == b.mElement)
+ && (a.mScope == b.mScope));
+
+ // reserved field comparisons - ignored until/if they are used
+}
+
+inline bool operator< (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
+{
+ if ((a.mStatus & 0xF0) != (b.mStatus & 0xF0))
+ return ((a.mStatus & 0xF0) < (b.mStatus & 0xF0));
+
+ if (a.mData1 != b.mData1)
+ return (a.mData1 < b.mData1);
+
+ if ((a.mStatus & 0xF) != (b.mStatus & 0xf)) // now compare the channel
+ return ((a.mStatus & 0xF) < (b.mStatus & 0xf));
+
+// reserved field comparisons - ignored until/if they are used
+
+// we're sorting this by MIDI, so we don't really care how the rest is sorted
+ return ((a.mParameterID < b.mParameterID)
+ && (a.mElement < b.mElement)
+ && (a.mScope < b.mScope));
+}
+
+
+
+class CompareMIDIMap {
+ int compare (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
+ {
+ if ((a.mStatus & 0xF0) < (b.mStatus & 0xF0))
+ return -1;
+ if ((a.mStatus & 0xF0) > (b.mStatus & 0xF0))
+ return 1;
+
+ // note event
+ if (a.mStatus < 0xB0 || a.mStatus >= 0xD0)
+ return 0;
+ if (a.mData1 > b.mData1) return 1;
+ if (a.mData1 < b.mData1) return -1;
+ return 0;
+ }
+
+public:
+ bool operator() (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
+ return compare (a, b) < 0;
+ }
+ bool Finish (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
+ return compare (a, b) != 0;
+ }
+};
+
+
+/*
+ usage: To find potential mapped events for a given status byte, where mMMapEvents is a sorted vec
+ CompareMIDIMap comparObj;
+ sortVecIter lower_iter = std::lower_bound(mMMapEvents.begin(), mMMapEvents.end(), inStatusByte, compareObj);
+ for (;lower_iter < mMMapEvents.end(); ++lower_iter) {
+ // then, see if we go out of the status byte range, using the Finish method
+ if (compareObj.Finish(map, tempMap)) // tempMap is a CAAUMIDIMap object with the status/dataByte 1 set
+ break;
+ // ...
+ }
+
+ in the for loop you call the MIDI_Matches call, to see if the MIDI event matches a given AUMIDIParam mapping
+ special note: you HAVE to transform note on (with vel zero) events to the note off status byte
+*/
+
+#endif