1 /*
2     Destroy FX AU Utilities is a collection of helpful utility functions
3     for creating and hosting Audio Unit plugins.
4     Copyright (C) 2003-2008  Sophia Poirier
5     All rights reserved.
6 
7     Redistribution and use in source and binary forms, with or without
8     modification, are permitted provided that the following conditions
9     are met:
10 
11     *   Redistributions of source code must retain the above
12         copyright notice, this list of conditions and the
13         following disclaimer.
14     *   Redistributions in binary form must reproduce the above
15         copyright notice, this list of conditions and the
16         following disclaimer in the documentation and/or other
17         materials provided with the distribution.
18     *   Neither the name of Destroy FX nor the names of its
19         contributors may be used to endorse or promote products
20         derived from this software without specific prior
21         written permission.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26     FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34     OF THE POSSIBILITY OF SUCH DAMAGE.
35 
36     To contact the author, please visit http://destroyfx.org/
37     and use the contact form.
38 */
39 // this is a modified version of dfx-au-utilities.h keeping only CFAUPreset related functionality
40 
41 // Copyright (C) 2016 Auburn Sounds
42 module dplug.au.dfxutil;
43 
44 import core.stdc.stdio: snprintf;
45 
46 import std.string;
47 import std.exception: assumeUnique;
48 
49 import derelict.carbon;
50 
51 import dplug.core.nogc;
52 
53 //-----------------------------------------------------------------------------
54 // The following defines and implements CoreFoundation-like handling of
55 // an AUPreset container object:  CFAUPreset
56 //-----------------------------------------------------------------------------
57 
58 enum UInt32 kCFAUPreset_CurrentVersion = 0;
59 
60 struct CFAUPreset
61 {
62     AUPreset auPreset;
63     uint version_;
64     CFAllocatorRef allocator;
65     CFIndex retainCount;
66 }
67 
68 alias CFAUPresetRef = void*;
69 
70 //-----------------------------------------------------------------------------
71 // create an instance of a CFAUPreset object
72 CFAUPresetRef CFAUPresetCreate(CFAllocatorRef inAllocator, SInt32 inPresetNumber, CFStringRef inPresetName) nothrow @nogc
73 {
74     CFAUPreset * newPreset = cast(CFAUPreset*) CFAllocatorAllocate(inAllocator, CFAUPreset.sizeof, 0);
75     if (newPreset != null)
76     {
77         newPreset.auPreset.presetNumber = inPresetNumber;
78         newPreset.auPreset.presetName = null;
79         // create our own a copy rather than retain the string, in case the input string is mutable,
80         // this will keep it from changing under our feet
81         if (inPresetName != null)
82             newPreset.auPreset.presetName = CFStringCreateCopy(inAllocator, inPresetName);
83         newPreset.version_ = kCFAUPreset_CurrentVersion;
84         newPreset.allocator = inAllocator;
85         newPreset.retainCount = 1;
86     }
87     return cast(CFAUPresetRef)newPreset;
88 }
89 
90 extern(C)
91 {
92     //-----------------------------------------------------------------------------
93     // retain a reference of a CFAUPreset object
94     CFAUPresetRef CFAUPresetRetain(void* inPreset) nothrow @nogc
95     {
96         if (inPreset != null)
97         {
98             CFAUPreset * incomingPreset = cast(CFAUPreset*) inPreset;
99             // retain the input AUPreset's name string for this reference to the preset
100             if (incomingPreset.auPreset.presetName != null)
101                 CFRetain(incomingPreset.auPreset.presetName);
102             incomingPreset.retainCount += 1;
103         }
104         return inPreset;
105     }
106 
107     //-----------------------------------------------------------------------------
108     // release a reference of a CFAUPreset object
109     void CFAUPresetRelease(void* inPreset) nothrow @nogc
110     {
111         CFAUPreset* incomingPreset = cast(CFAUPreset*) inPreset;
112         // these situations shouldn't happen
113         if (inPreset == null)
114             return;
115         if (incomingPreset.retainCount <= 0)
116             return;
117 
118         // first release the name string, CF-style, since it's a CFString
119         if (incomingPreset.auPreset.presetName != null)
120             CFRelease(incomingPreset.auPreset.presetName);
121         incomingPreset.retainCount -= 1;
122         // check if this is the end of this instance's life
123         if (incomingPreset.retainCount == 0)
124         {
125             // wipe out the data so that, if anyone tries to access stale memory later, it will be (semi)invalid
126             incomingPreset.auPreset.presetName = null;
127             incomingPreset.auPreset.presetNumber = 0;
128             // and finally, free the memory for the CFAUPreset struct
129             CFAllocatorDeallocate(incomingPreset.allocator, cast(void*)inPreset);
130         }
131     }
132 
133     //-----------------------------------------------------------------------------
134     // This function is called when an item (an AUPreset) is added to the CFArray,
135     // or when a CFArray containing an AUPreset is retained.
136     const(void)* CFAUPresetArrayRetainCallBack(CFAllocatorRef inAllocator, const(void)* inPreset) nothrow @nogc
137     {
138         return CFAUPresetRetain(cast(void*)inPreset); // casting const away here!
139     }
140 
141     //-----------------------------------------------------------------------------
142     // This function is called when an item (an AUPreset) is removed from the CFArray
143     // or when the array is released.
144     // Since a reference to the data belongs to the array, we need to release that here.
145     void CFAUPresetArrayReleaseCallBack(CFAllocatorRef inAllocator, const(void)* inPreset) nothrow @nogc
146     {
147         CFAUPresetRelease(cast(void*)inPreset); // casting const away here!
148     }
149 
150     //-----------------------------------------------------------------------------
151     // This function is called when someone wants to compare to items (AUPresets)
152     // in the CFArray to see if they are equal or not.
153     // For our AUPresets, we will compare based on the preset number and the name string.
154     Boolean CFAUPresetArrayEqualCallBack(const(void)* inPreset1, const(void)* inPreset2) nothrow @nogc
155     {
156         AUPreset * preset1 = cast(AUPreset*) inPreset1;
157         AUPreset * preset2 = cast(AUPreset*) inPreset2;
158         // the two presets are only equal if they have the same preset number and
159         // if the two name strings are the same (which we rely on the CF function to compare)
160         return (preset1.presetNumber == preset2.presetNumber) &&
161                 (CFStringCompare(preset1.presetName, preset2.presetName, 0) == kCFCompareEqualTo);
162     }
163 
164     //-----------------------------------------------------------------------------
165     // This function is called when someone wants to get a description of
166     // a particular item (an AUPreset) as though it were a CF type.
167     // That happens, for example, when using CFShow().
168     // This will create and return a CFString that indicates that
169     // the object is an AUPreset and tells the preset number and preset name.
170     CFStringRef CFAUPresetArrayCopyDescriptionCallBack(const(void)* inPreset) nothrow
171     {
172         AUPreset * preset = cast(AUPreset*) inPreset;
173         return CFStringCreateWithFormat(kCFAllocatorDefault, null,
174                                         CFStrLocal.fromString("AUPreset:\npreset number = %d\npreset name = %@"),
175                                         cast(int)preset.presetNumber, preset.presetName);
176     }
177 }
178 
179 CFArrayCallBacks getCFAUPresetArrayCallBacks() nothrow @nogc
180 {
181     CFArrayCallBacks result;
182     with(result)
183     {
184         version_ = 0; // currently, 0 is the only valid version value for this
185         retain = &CFAUPresetArrayRetainCallBack;
186         release = &CFAUPresetArrayReleaseCallBack;
187         copyDescription = &CFAUPresetArrayCopyDescriptionCallBack;
188         equal = &CFAUPresetArrayEqualCallBack;
189     }
190     return result;
191 }
192 
193 
194 // CoreFoundation helpers
195 
196 struct CFStrLocal
197 {
198 nothrow:
199 @nogc:
200 
201     CFStringRef parent;
202     alias parent this;
203 
204     @disable this();
205     @disable this(this);
206 
207     static fromString(const(char)[] str)
208     {
209         CFStrLocal s = void;
210         s.parent = toCFString(str);
211         return s;
212     }
213 
214     ~this() nothrow
215     {
216         CFRelease(parent);
217     }
218 }
219 
220 
221 /// Creates a CFString from an int give up its ownership.
222 CFStringRef convertIntToCFString(int number) nothrow @nogc
223 {
224     char[16] str;
225     snprintf(str.ptr, str.length, "%d", number); 
226     return CFStringCreateWithCString(null, str.ptr, kCFStringEncodingUTF8);
227 }
228 
229 /// Creates a CFString from a string and give up its ownership.
230 CFStringRef toCFString(const(char)[] str) nothrow @nogc
231 {
232     return CFStringCreateWithCString(null, CString(str).storage, kCFStringEncodingUTF8);
233 }
234 
235 /// Create string from a CFString, and give up its ownership.
236 /// Such a string must be deallocated with `free`/`freeSlice`.
237 /// It is guaranteed to finish with a terminal zero character ('\0').
238 string mallocStringFromCFString(CFStringRef cfStr) nothrow @nogc
239 {
240     int n = cast(int)CFStringGetLength(cfStr) + 1;
241     char[] buf = mallocSlice!char(n);
242     CFStringGetCString(cfStr, buf.ptr, n, kCFStringEncodingUTF8);
243     return assumeUnique(buf);
244 }
245 
246 void putNumberInDict(CFMutableDictionaryRef pDict, const(char)[] key, void* pNumber, CFNumberType type) nothrow @nogc
247 {
248     CFStrLocal cfKey = CFStrLocal.fromString(key);
249 
250     CFNumberRef pValue = CFNumberCreate(null, type, pNumber);
251     CFDictionarySetValue(pDict, cfKey, pValue);
252     CFRelease(pValue);
253 }
254 
255 void putStrInDict(CFMutableDictionaryRef pDict, const(char)[] key, const(char)[] value) nothrow @nogc
256 {
257     CFStrLocal cfKey = CFStrLocal.fromString(key);
258     CFStrLocal cfValue = CFStrLocal.fromString(value);
259     CFDictionarySetValue(pDict, cfKey, cfValue);
260 }
261 
262 void putDataInDict(CFMutableDictionaryRef pDict, const(char)[] key, ubyte[] pChunk) nothrow @nogc
263 {
264     CFStrLocal cfKey = CFStrLocal.fromString(key);
265 
266     CFDataRef pData = CFDataCreate(null, pChunk.ptr, cast(CFIndex)(pChunk.length));
267     CFDictionarySetValue(pDict, cfKey, pData);
268     CFRelease(pData);
269 }
270 
271 
272 bool getNumberFromDict(CFDictionaryRef pDict, const(char)[] key, void* pNumber, CFNumberType type) nothrow @nogc
273 {
274     CFStrLocal cfKey = CFStrLocal.fromString(key);
275 
276     CFNumberRef pValue = cast(CFNumberRef) CFDictionaryGetValue(pDict, cfKey);
277     if (pValue)
278     {
279         CFNumberGetValue(pValue, type, pNumber);
280         return true;
281     }
282     return false;
283 }
284 
285 /// Get a string in a dictionnary, and give up its ownership.
286 bool getStrFromDict(CFDictionaryRef pDict, const(char)[] key, out string value) nothrow @nogc
287 {
288     CFStrLocal cfKey = CFStrLocal.fromString(key);
289 
290     CFStringRef pValue = cast(CFStringRef) CFDictionaryGetValue(pDict, cfKey);
291     if (pValue)
292     {
293         value = mallocStringFromCFString(pValue);
294         return true;
295     }
296     return false;
297 }
298 
299 /// Gets data from a CFDataRef dictionnary entry and give up its ownership.
300 /// Must be deallocated with `free`/`freeSlice`.
301 bool getDataFromDict(CFDictionaryRef pDict, string key, out ubyte[] pChunk) nothrow @nogc
302 {
303     CFStrLocal cfKey = CFStrLocal.fromString(key);
304     CFDataRef pData = cast(CFDataRef) CFDictionaryGetValue(pDict, cfKey);
305     if (pData)
306     {
307         int n = cast(int)CFDataGetLength(pData);
308         pChunk = ( CFDataGetBytePtr(pData)[0..n] ).mallocDup;
309         return true;
310     }
311     return false;
312 }