3 Abstract: Part of CoreAudio Utility Classes
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.
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.
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.
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.
44 Copyright (C) 2014 Apple Inc. All Rights Reserved.
47 #ifndef __CAAUMIDIMap_h_
48 #define __CAAUMIDIMap_h_
50 #include <AudioUnit/AudioUnitProperties.h>
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.
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.
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
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
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
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
87 AudioUnitScope mScope;
88 AudioUnitElement mElement;
89 AudioUnitParameterID mParameterID;
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;
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
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')
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
119 * The AUParameterMIDIMapping Structure
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)
130 (where n is 0-0xF to correspond to MIDI channels 1-16)
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
199 struct MIDIValueTransformer {
200 virtual double tolinear(double) = 0;
201 virtual double fromlinear(double) = 0;
204 virtual ~MIDIValueTransformer() { }
208 struct MIDILinearTransformer : public MIDIValueTransformer {
209 virtual double tolinear(double x) { return x; }
210 virtual double fromlinear(double x) { return x; }
213 struct MIDILogTransformer : public MIDIValueTransformer {
214 virtual double tolinear(double x) { return log(std::max(x, .00001)); }
215 virtual double fromlinear(double x) { return exp(x); }
218 struct MIDIExpTransformer : public MIDIValueTransformer {
219 virtual double tolinear(double x) { return exp(x); }
220 virtual double fromlinear(double x) { return log(std::max(x, .00001)); }
223 struct MIDISqrtTransformer : public MIDIValueTransformer {
224 virtual double tolinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
225 virtual double fromlinear(double x) { return x < 0. ? -(x * x) : x * x; }
228 struct MIDISquareTransformer : public MIDIValueTransformer {
229 virtual double tolinear(double x) { return x < 0. ? -(x * x) : x * x; }
230 virtual double fromlinear(double x) { return x < 0. ? -(sqrt(-x)) : sqrt(x); }
233 struct MIDICubeRtTransformer : public MIDIValueTransformer {
234 virtual double tolinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
235 virtual double fromlinear(double x) { return x * x * x; }
238 struct MIDICubeTransformer : public MIDIValueTransformer {
239 virtual double tolinear(double x) { return x * x * x; }
240 virtual double fromlinear(double x) { return x < 0. ? -(pow(-x, 1./3.)) : pow(x, 1./3.); }
244 class CAAUMIDIMap : public AUParameterMIDIMapping {
247 // variables for more efficient parsing of MIDI to Param value
250 MIDIValueTransformer *mTransType;
253 static MIDIValueTransformer *GetTransformer (UInt32 inFlags);
255 CAAUMIDIMap() { memset(this, 0, sizeof(CAAUMIDIMap)); }
256 CAAUMIDIMap (const AUParameterMIDIMapping& inMap)
258 memset(this, 0, sizeof(CAAUMIDIMap));
259 memcpy (this, &inMap, sizeof(inMap));
261 CAAUMIDIMap (AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterID inParam)
263 memset(this, 0, sizeof(CAAUMIDIMap));
265 mElement = inElement;
266 mParameterID = inParam;
270 bool IsValid () const { return mStatus != 0; }
272 // returns -1 if any channel bit is set
273 SInt32 Channel () const { return IsAnyChannel() ? -1 : (mStatus & 0xF); }
274 bool IsAnyChannel () const {
275 return mFlags & kAUParameterMIDIMapping_AnyChannelFlag;
277 // preserves the existing channel info in the status byte
278 // preserves any previously set mFlags value
279 void SetAnyChannel (bool inFlag)
282 mFlags |= kAUParameterMIDIMapping_AnyChannelFlag;
284 mFlags &= ~kAUParameterMIDIMapping_AnyChannelFlag;
287 bool IsAnyNote () const {
288 return (mFlags & kAUParameterMIDIMapping_AnyNoteFlag) != 0;
290 // preserves the existing key num in the mData1 byte
291 // preserves any previously set mFlags value
292 void SetAnyNote (bool inFlag)
295 mFlags |= kAUParameterMIDIMapping_AnyNoteFlag;
297 mFlags &= ~kAUParameterMIDIMapping_AnyNoteFlag;
300 bool IsToggle() const { return (mFlags & kAUParameterMIDIMapping_Toggle) != 0; }
301 void SetToggle (bool inFlag)
304 mFlags |= kAUParameterMIDIMapping_Toggle;
306 mFlags &= ~kAUParameterMIDIMapping_Toggle;
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)
314 mFlags |= kAUParameterMIDIMapping_Bipolar;
316 mFlags |= kAUParameterMIDIMapping_Bipolar_On;
318 mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
320 mFlags &= ~kAUParameterMIDIMapping_Bipolar;
321 mFlags &= ~kAUParameterMIDIMapping_Bipolar_On;
324 bool IsBipolar_OnValue () const { return (mFlags & kAUParameterMIDIMapping_Bipolar_On) != 0; }
326 bool IsSubRange () const { return (mFlags & kAUParameterMIDIMapping_SubRange) != 0; }
327 void SetSubRange (Float32 inStartValue, Float32 inStopValue)
329 mFlags |= kAUParameterMIDIMapping_SubRange;
331 mSubRangeMin = inStartValue;
332 mSubRangeMax = inStopValue;
335 void SetParamRange(Float32 minValue, Float32 maxValue)
337 mMinValue = minValue;
338 mMaxValue = maxValue;
341 // this will retain the subrange values previously set.
342 void SetSubRange (bool inFlag)
345 mFlags |= kAUParameterMIDIMapping_SubRange;
347 mFlags &= ~kAUParameterMIDIMapping_SubRange;
350 bool IsAnyValue() const{return !IsBipolar();}
351 bool IsOnValue() const{return IsBipolar_OnValue();}
352 bool IsOffValue() const{return IsBipolar();}
354 bool IsNoteOff () const { return ((mStatus & 0xF0) == 0x80); }
355 bool IsNoteOn () const { return ((mStatus & 0xF0) == 0x90); }
357 bool IsKeyPressure () const { return ((mStatus & 0xF0) == 0xA0); }
359 bool IsKeyEvent () const { return (mStatus > 0x7F) && (mStatus < 0xB0); }
361 bool IsPatchChange () const { return ((mStatus & 0xF0) == 0xC0); }
362 bool IsChannelPressure () const { return ((mStatus & 0xF0) == 0xD0); }
363 bool IsPitchBend () const { return ((mStatus & 0xF0) == 0xE0); }
364 bool IsControlChange () const { return ((mStatus & 0xF0) == 0xB0); }
367 void SetControllerOnValue(){SetBipolar(true,true);}
368 void SetControllerOffValue(){SetBipolar(true,false);}
369 void SetControllerAnyValue(){SetBipolar(false,false);}
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)
375 mStatus = 0x80 | (channel & 0xF);
377 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
381 void SetNoteOn (UInt8 key, SInt8 channel, bool anyChannel = false)
383 mStatus = 0x90 | (channel & 0xF);
385 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
388 void SetPolyKey (UInt8 key, SInt8 channel, bool anyChannel = false)
390 mStatus = 0xA0 | (channel & 0xF);
392 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
395 void SetControlChange (UInt8 controllerID, SInt8 channel, bool anyChannel = false)
397 mStatus = 0xB0 | (channel & 0xF);
398 mData1 = controllerID;
399 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
402 void SetPatchChange (UInt8 patchChange, SInt8 channel, bool anyChannel = false)
404 mStatus = 0xC0 | (channel & 0xF);
405 mData1 = patchChange;
406 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
409 void SetChannelPressure (SInt8 channel, bool anyChannel = false)
411 mStatus = 0xD0 | (channel & 0xF);
413 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
416 void SetPitchBend (SInt8 channel, bool anyChannel = false)
418 mStatus = 0xE0 | (channel & 0xF);
420 mFlags = (anyChannel ? kAUParameterMIDIMapping_AnyChannelFlag : 0);
424 Float32 ParamValueFromMIDILinear (Float32 inLinearValue) const
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);
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;
447 void Save (CFPropertyListRef &outData) const;
448 void Restore (CFDictionaryRef inData);
450 static void SaveAsMapPList (AudioUnit inUnit,
451 const AUParameterMIDIMapping * inMappings,
452 UInt32 inNumMappings,
453 CFPropertyListRef &outData,
454 CFStringRef inName = NULL);
456 // inNumMappings describes how much memory is allocated in outMappings
457 static void RestoreFromMapPList (const CFDictionaryRef inData,
458 AUParameterMIDIMapping * outMappings,
459 UInt32 inNumMappings);
461 static UInt32 NumberOfMaps (const CFDictionaryRef inData);
465 // these sorting operations sort for run-time efficiency based on the MIDI messages
466 inline bool operator== (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
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));
476 // reserved field comparisons - ignored until/if they are used
479 inline bool operator< (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
481 if ((a.mStatus & 0xF0) != (b.mStatus & 0xF0))
482 return ((a.mStatus & 0xF0) < (b.mStatus & 0xF0));
484 if (a.mData1 != b.mData1)
485 return (a.mData1 < b.mData1);
487 if ((a.mStatus & 0xF) != (b.mStatus & 0xf)) // now compare the channel
488 return ((a.mStatus & 0xF) < (b.mStatus & 0xf));
490 // reserved field comparisons - ignored until/if they are used
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));
500 class CompareMIDIMap {
501 int compare (const CAAUMIDIMap &a, const CAAUMIDIMap &b)
503 if ((a.mStatus & 0xF0) < (b.mStatus & 0xF0))
505 if ((a.mStatus & 0xF0) > (b.mStatus & 0xF0))
509 if (a.mStatus < 0xB0 || a.mStatus >= 0xD0)
511 if (a.mData1 > b.mData1) return 1;
512 if (a.mData1 < b.mData1) return -1;
517 bool operator() (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
518 return compare (a, b) < 0;
520 bool Finish (const CAAUMIDIMap &a, const CAAUMIDIMap &b) {
521 return compare (a, b) != 0;
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
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