alternative new version of the AppleUtility library
[ardour.git] / libs / appleutility / CoreAudio / PublicUtility / CAAUParameter.cpp
1 /*
2      File: CAAUParameter.cpp
3  Abstract: CAAUParameter.h
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 #include "CAAUParameter.h"
48
49 CAAUParameter::CAAUParameter() 
50 {
51         memset(this, 0, sizeof(CAAUParameter));
52 }
53
54 CAAUParameter::CAAUParameter(AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element)
55 {
56         memset(this, 0, sizeof(CAAUParameter));
57         Init (au, param, scope, element);
58 }
59
60 CAAUParameter::CAAUParameter (AudioUnitParameter &inParam)
61 {
62         memset(this, 0, sizeof(CAAUParameter));
63         Init (inParam.mAudioUnit, inParam.mParameterID, inParam.mScope, inParam.mElement);
64 }
65
66 CAAUParameter::CAAUParameter(const CAAUParameter &a) 
67 {
68         memset(this, 0, sizeof(CAAUParameter));
69         *this = a;
70 }
71
72 CAAUParameter & CAAUParameter::operator = (const CAAUParameter &a)
73 {
74         if (mParamName) CFRelease(mParamName);
75         if (mParamTag) CFRelease(mParamTag);
76         if (mNamedParams) CFRelease(mNamedParams);
77         
78         memcpy(this, &a, sizeof(CAAUParameter));
79
80         if (mParamName) CFRetain(mParamName);
81         if (mParamTag) CFRetain(mParamTag);
82         if (mNamedParams) CFRetain(mNamedParams);
83         
84         return *this;
85 }
86
87 CAAUParameter::~CAAUParameter()
88 {
89         if (mParamName) CFRelease(mParamName);
90         if (mParamTag) CFRelease(mParamTag);
91         if (mNamedParams) CFRelease (mNamedParams);
92 }
93
94 void            CAAUParameter::Init (AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element)
95 {
96         mAudioUnit = au;
97         mParameterID = param;
98         mScope = scope;
99         mElement = element;
100         
101         UInt32 propertySize = sizeof(mParamInfo);
102         OSStatus err = AudioUnitGetProperty(au, kAudioUnitProperty_ParameterInfo,
103                         scope, param, &mParamInfo, &propertySize);
104         if (err)
105                 memset(&mParamInfo, 0, sizeof(mParamInfo));
106         if (mParamInfo.flags & kAudioUnitParameterFlag_HasCFNameString) {
107                 mParamName = mParamInfo.cfNameString;
108                 if (!(mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease)) 
109                         CFRetain (mParamName);
110         } else
111                 mParamName = CFStringCreateWithCString(NULL, mParamInfo.name, kCFStringEncodingUTF8);
112         
113         const char* str = 0;
114         switch (mParamInfo.unit)
115         {
116                 case kAudioUnitParameterUnit_Boolean:
117                         str = "T/F";
118                         break;
119                 case kAudioUnitParameterUnit_Percent:
120                 case kAudioUnitParameterUnit_EqualPowerCrossfade:
121                         str = "%";
122                         break;
123                 case kAudioUnitParameterUnit_Seconds:
124                         str = "Secs";
125                         break;
126                 case kAudioUnitParameterUnit_SampleFrames:
127                         str = "Samps";
128                         break;
129                 case kAudioUnitParameterUnit_Phase:
130                 case kAudioUnitParameterUnit_Degrees:
131                         str = "Degr.";
132                         break;
133                 case kAudioUnitParameterUnit_Hertz:
134                         str = "Hz";
135                         break;
136                 case kAudioUnitParameterUnit_Cents:
137                 case kAudioUnitParameterUnit_AbsoluteCents:
138                         str = "Cents";
139                         break;
140                 case kAudioUnitParameterUnit_RelativeSemiTones:
141                         str = "S-T";
142                         break;
143                 case kAudioUnitParameterUnit_MIDINoteNumber:
144                 case kAudioUnitParameterUnit_MIDIController:
145                         str = "MIDI";
146                                 //these are inclusive, so add one value here
147                         mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue);
148                         break;
149                 case kAudioUnitParameterUnit_Decibels:
150                         str = "dB";
151                         break;
152                 case kAudioUnitParameterUnit_MixerFaderCurve1:
153                 case kAudioUnitParameterUnit_LinearGain:
154                         str = "Gain";
155                         break;
156                 case kAudioUnitParameterUnit_Pan:
157                         str = "L/R";
158                         break;
159                 case kAudioUnitParameterUnit_Meters:
160                         str = "Mtrs";
161                         break;
162                 case kAudioUnitParameterUnit_Octaves:
163                         str = "8ve";
164                         break;
165                 case kAudioUnitParameterUnit_BPM:
166                         str = "BPM";
167                         break;
168                 case kAudioUnitParameterUnit_Beats:
169                         str = "Beats";
170                         break;
171                 case kAudioUnitParameterUnit_Milliseconds:
172                         str = "msecs";
173                         break;
174                 case kAudioUnitParameterUnit_Ratio:
175                         str = "Ratio";
176                         break;
177                 case kAudioUnitParameterUnit_Indexed:
178                         {
179                                 propertySize = sizeof(mNamedParams);
180                                 err = AudioUnitGetProperty (au, 
181                                                                         kAudioUnitProperty_ParameterValueStrings,
182                                                                         scope, 
183                                                                         param, 
184                                                                         &mNamedParams, 
185                                                                         &propertySize);
186                                 if (!err && mNamedParams) {
187                                         mNumIndexedParams = CFArrayGetCount(mNamedParams);
188                                 } else {
189                                                 //these are inclusive, so add one value here
190                                         mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue);
191                                 }
192                                 str = NULL;
193                         }
194                         break;
195                 case kAudioUnitParameterUnit_CustomUnit:
196                 {
197                         CFStringRef unitName = mParamInfo.unitName;
198                         static char paramStr[256];
199                         CFStringGetCString (unitName, paramStr, 256, kCFStringEncodingUTF8);
200                         if (mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease)
201                                 CFRelease (unitName);
202                         str = paramStr;
203                         break;
204                 }
205                 case kAudioUnitParameterUnit_Generic:
206                 case kAudioUnitParameterUnit_Rate:
207                 default:
208                         str = NULL;
209                         break;
210         }
211         
212         if (str)
213                 mParamTag = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
214         else
215                 mParamTag = NULL;
216 }
217
218
219 Float32         CAAUParameter::GetValue() const
220 {
221         Float32 value = 0.;
222         //OSStatus err = 
223         AudioUnitGetParameter(mAudioUnit, mParameterID, mScope, mElement, &value);
224         return value;
225 }
226
227 CFStringRef CreateLocalizedStringForParameterValue ( double                                     inParameterValue,
228                                                                                                          const CAAUParameter *  inParameter,
229                                                                                                          UInt32                                 inDigits,
230                                                                                                          UInt32                                 minDigits) {    
231         if (!inParameter) return nil;
232
233         AudioUnitParameterInfo info = inParameter->ParamInfo();
234         int pow10;
235
236         switch (info.unit) {
237                 case kAudioUnitParameterUnit_Hertz:
238                         // number of significant digits based on value
239                         pow10 = int(log10(fmax(inParameterValue, .000001)));
240                         break;
241                 default:
242                         // number of significant digits based on parameter range
243                         pow10 = int(log10(fmax(double(info.maxValue - info.minValue), .000001)));
244                         break;
245         }
246
247         // pow10        range                   nDigitsAfterDecimal
248         //      -2              .0100-.0999             4
249         //      -1              .100-.999               3
250         //      0               1.00-9.99               2
251         //      1               10.0-99.9               1
252         //      2               100-999                 0
253         //      3               1000-9990               -1
254         //      4               10000-99900             -2
255         
256         int nDigitsAfterDecimal = inDigits - (pow10 + 1);
257         if (nDigitsAfterDecimal < 0)
258                 nDigitsAfterDecimal = 0;        // the least number of digits possible is zero
259
260         if (info.flags & kAudioUnitParameterFlag_IsHighResolution)
261                 nDigitsAfterDecimal = 4;
262         
263         CFLocaleRef currentLocale = CFLocaleCopyCurrent(); 
264         CFNumberFormatterRef numberFormatter = CFNumberFormatterCreate (NULL, currentLocale, kCFNumberFormatterDecimalStyle);
265         
266         CFNumberRef maxFractionDigits = CFNumberCreate (NULL, kCFNumberIntType, &nDigitsAfterDecimal);
267         
268         if (nDigitsAfterDecimal > 0)
269                 nDigitsAfterDecimal = minDigits;
270                 
271         CFNumberRef minFractionDigits = CFNumberCreate (NULL, kCFNumberIntType, &nDigitsAfterDecimal); 
272
273         CFNumberFormatterSetProperty (numberFormatter, kCFNumberFormatterMinFractionDigits, minFractionDigits); 
274         CFNumberFormatterSetProperty (numberFormatter, kCFNumberFormatterMaxFractionDigits, maxFractionDigits); 
275         CFStringRef formattedNumberString = CFNumberFormatterCreateStringWithValue (NULL, numberFormatter, kCFNumberDoubleType, &inParameterValue); 
276
277         CFRelease(currentLocale); 
278         CFRelease(numberFormatter); 
279         CFRelease(maxFractionDigits);
280         CFRelease(minFractionDigits);
281
282         return formattedNumberString;
283 }
284
285 CFStringRef CreateLocalizedStringForParameterValue ( double                                     inParameterValue,
286                                                                                                          const CAAUParameter *  inParameter,
287                                                                                                          UInt32                                 inDigits) {     
288         return CreateLocalizedStringForParameterValue (inParameterValue, inParameter, inDigits, 1);
289 }
290
291 double ValueForLocalizedParameterString (CFStringRef string, const CAAUParameter * inParameter) {
292         CFLocaleRef currentLocale = CFLocaleCopyCurrent(); 
293         CFNumberFormatterRef numberFormatter = CFNumberFormatterCreate (NULL, currentLocale, kCFNumberFormatterDecimalStyle);
294
295         double value = 0;
296         Boolean worked = CFNumberFormatterGetValueFromString (numberFormatter, string, NULL, kCFNumberDoubleType, &value);
297         
298         CFRelease(currentLocale);
299         CFRelease(numberFormatter);
300         
301         if (worked)
302                 return value;
303         else {
304                 AudioUnitParameterInfo info = inParameter->ParamInfo();
305                 return info.defaultValue;
306         }
307 }
308
309 CFStringRef CAAUParameter::GetStringFromValueCopy(const Float32 *value) const
310 {
311         if (HasNamedParams())
312         {
313                 Float32 val = (value == NULL ? GetValue() : *value);
314                 int index = int(mParamInfo.minValue) + int(val);
315                 CFStringRef str = GetParamName (index);
316                 if (str) {
317                         CFRetain (str);
318                         return str;
319                 }
320         }
321         else if (ValuesHaveStrings()) 
322         {
323                 AudioUnitParameterStringFromValue stringValue;
324                 stringValue.inParamID = mParameterID;
325                 stringValue.inValue = value;
326                 stringValue.outString = NULL;
327                 UInt32 propertySize = sizeof(stringValue);
328                 
329                 OSStatus err = AudioUnitGetProperty (mAudioUnit, 
330                                                                                         kAudioUnitProperty_ParameterStringFromValue,
331                                                                                         mScope, 
332                                                                                         0, 
333                                                                                         &stringValue, 
334                                                                                         &propertySize);
335                 
336                 if (!err && stringValue.outString != NULL)
337                         return stringValue.outString;
338         }
339         
340         Float32 val = (value == NULL ? GetValue() : *value);
341         AudioUnitParameterUnit unit = this->ParamInfo().unit;
342         if (unit ==  kAudioUnitParameterUnit_Cents || unit == kAudioUnitParameterUnit_AbsoluteCents)
343                 return CreateLocalizedStringForParameterValue(val, this, 4, 0);
344         else
345                 return CreateLocalizedStringForParameterValue(val, this, 4);
346 }
347
348 Float32 CAAUParameter::GetValueFromString(CFStringRef str) const
349 {
350         if (ValuesHaveStrings()) 
351         {
352                 AudioUnitParameterValueFromString valueString;
353                 valueString.inParamID = mParameterID;
354                 valueString.inString = str;
355                 UInt32 propertySize = sizeof(valueString);
356                 
357                 OSStatus err = AudioUnitGetProperty (mAudioUnit, 
358                                                                                 kAudioUnitProperty_ParameterValueFromString,
359                                                                                 mScope, 
360                                                                                 0, 
361                                                                                 &valueString, 
362                                                                                 &propertySize);
363                                                                                 
364                 if (!err) {
365                         return valueString.outValue;
366                 }
367         }
368         
369         return (Float32) ValueForLocalizedParameterString(str, this);
370 }
371
372 void            CAAUParameter::SetValue(        AUParameterListenerRef          inListener, 
373                                                                         void *                                                  inObject,
374                                                                         Float32                                                 inValue) const
375 {
376     // clip inValue as: maxValue >= inValue >= minValue before setting
377     Float32 valueToSet = inValue;
378     if (valueToSet > mParamInfo.maxValue)
379         valueToSet = mParamInfo.maxValue;
380     if (valueToSet < mParamInfo.minValue)
381         valueToSet = mParamInfo.minValue;
382     
383         AUParameterSet(inListener, inObject, this, valueToSet, 0);
384 }
385
386 #if DEBUG
387 void    CAAUParameter::Print() const
388 {
389         UInt32 clump = 0;
390         GetClumpID (clump);
391         
392         UInt32 len = static_cast<UInt32>(CFStringGetLength(mParamName));
393         char* chars = (char*)malloc (len * 2); // give us plenty of room for unichar chars
394         if (!CFStringGetCString (mParamName, chars, len * 2, kCFStringEncodingUTF8))
395                 chars[0] = 0;
396         
397         printf ("ID: %ld, Clump: %u, Name: %s\n", (long unsigned int) mParameterID, (unsigned int) clump, chars);
398         free (chars);
399 }
400 #endif