542 lines
22 KiB
C
542 lines
22 KiB
C
|
/*
|
||
|
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
|