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 /** 42 Audio Unit plug-in client. Port of Destroy FX AU Utilities. 43 44 Copyright: Copyright (C) 2003-2008 Sophia Poirier 45 Copyright (C) 2016 Guillaume Piolat 46 */ 47 module dplug.au.dfxutil; 48 49 import core.stdc.stdio: snprintf; 50 51 import std..string; 52 import std.exception: assumeUnique; 53 54 import derelict.carbon; 55 56 import dplug.core.nogc; 57 58 //----------------------------------------------------------------------------- 59 // The following defines and implements CoreFoundation-like handling of 60 // an AUPreset container object: CFAUPreset 61 //----------------------------------------------------------------------------- 62 63 enum UInt32 kCFAUPreset_CurrentVersion = 0; 64 65 struct CFAUPreset 66 { 67 AUPreset auPreset; 68 uint version_; 69 CFAllocatorRef allocator; 70 CFIndex retainCount; 71 } 72 73 alias CFAUPresetRef = void*; 74 75 //----------------------------------------------------------------------------- 76 // create an instance of a CFAUPreset object 77 CFAUPresetRef CFAUPresetCreate(CFAllocatorRef inAllocator, SInt32 inPresetNumber, CFStringRef inPresetName) nothrow @nogc 78 { 79 CFAUPreset * newPreset = cast(CFAUPreset*) CFAllocatorAllocate(inAllocator, CFAUPreset.sizeof, 0); 80 if (newPreset != null) 81 { 82 newPreset.auPreset.presetNumber = inPresetNumber; 83 newPreset.auPreset.presetName = null; 84 // create our own a copy rather than retain the string, in case the input string is mutable, 85 // this will keep it from changing under our feet 86 if (inPresetName != null) 87 newPreset.auPreset.presetName = CFStringCreateCopy(inAllocator, inPresetName); 88 newPreset.version_ = kCFAUPreset_CurrentVersion; 89 newPreset.allocator = inAllocator; 90 newPreset.retainCount = 1; 91 } 92 return cast(CFAUPresetRef)newPreset; 93 } 94 95 extern(C) 96 { 97 //----------------------------------------------------------------------------- 98 // retain a reference of a CFAUPreset object 99 CFAUPresetRef CFAUPresetRetain(void* inPreset) nothrow @nogc 100 { 101 if (inPreset != null) 102 { 103 CFAUPreset * incomingPreset = cast(CFAUPreset*) inPreset; 104 // retain the input AUPreset's name string for this reference to the preset 105 if (incomingPreset.auPreset.presetName != null) 106 CFRetain(incomingPreset.auPreset.presetName); 107 incomingPreset.retainCount += 1; 108 } 109 return inPreset; 110 } 111 112 //----------------------------------------------------------------------------- 113 // release a reference of a CFAUPreset object 114 void CFAUPresetRelease(void* inPreset) nothrow @nogc 115 { 116 CFAUPreset* incomingPreset = cast(CFAUPreset*) inPreset; 117 // these situations shouldn't happen 118 if (inPreset == null) 119 return; 120 if (incomingPreset.retainCount <= 0) 121 return; 122 123 // first release the name string, CF-style, since it's a CFString 124 if (incomingPreset.auPreset.presetName != null) 125 CFRelease(incomingPreset.auPreset.presetName); 126 incomingPreset.retainCount -= 1; 127 // check if this is the end of this instance's life 128 if (incomingPreset.retainCount == 0) 129 { 130 // wipe out the data so that, if anyone tries to access stale memory later, it will be (semi)invalid 131 incomingPreset.auPreset.presetName = null; 132 incomingPreset.auPreset.presetNumber = 0; 133 // and finally, free the memory for the CFAUPreset struct 134 CFAllocatorDeallocate(incomingPreset.allocator, cast(void*)inPreset); 135 } 136 } 137 138 //----------------------------------------------------------------------------- 139 // This function is called when an item (an AUPreset) is added to the CFArray, 140 // or when a CFArray containing an AUPreset is retained. 141 const(void)* CFAUPresetArrayRetainCallBack(CFAllocatorRef inAllocator, const(void)* inPreset) nothrow @nogc 142 { 143 return CFAUPresetRetain(cast(void*)inPreset); // casting const away here! 144 } 145 146 //----------------------------------------------------------------------------- 147 // This function is called when an item (an AUPreset) is removed from the CFArray 148 // or when the array is released. 149 // Since a reference to the data belongs to the array, we need to release that here. 150 void CFAUPresetArrayReleaseCallBack(CFAllocatorRef inAllocator, const(void)* inPreset) nothrow @nogc 151 { 152 CFAUPresetRelease(cast(void*)inPreset); // casting const away here! 153 } 154 155 //----------------------------------------------------------------------------- 156 // This function is called when someone wants to compare to items (AUPresets) 157 // in the CFArray to see if they are equal or not. 158 // For our AUPresets, we will compare based on the preset number and the name string. 159 Boolean CFAUPresetArrayEqualCallBack(const(void)* inPreset1, const(void)* inPreset2) nothrow @nogc 160 { 161 AUPreset * preset1 = cast(AUPreset*) inPreset1; 162 AUPreset * preset2 = cast(AUPreset*) inPreset2; 163 // the two presets are only equal if they have the same preset number and 164 // if the two name strings are the same (which we rely on the CF function to compare) 165 return (preset1.presetNumber == preset2.presetNumber) && 166 (CFStringCompare(preset1.presetName, preset2.presetName, 0) == kCFCompareEqualTo); 167 } 168 169 //----------------------------------------------------------------------------- 170 // This function is called when someone wants to get a description of 171 // a particular item (an AUPreset) as though it were a CF type. 172 // That happens, for example, when using CFShow(). 173 // This will create and return a CFString that indicates that 174 // the object is an AUPreset and tells the preset number and preset name. 175 CFStringRef CFAUPresetArrayCopyDescriptionCallBack(const(void)* inPreset) nothrow 176 { 177 AUPreset * preset = cast(AUPreset*) inPreset; 178 return CFStringCreateWithFormat(kCFAllocatorDefault, null, 179 CFStrLocal.fromString("AUPreset:\npreset number = %d\npreset name = %@"), 180 cast(int)preset.presetNumber, preset.presetName); 181 } 182 } 183 184 CFArrayCallBacks getCFAUPresetArrayCallBacks() nothrow @nogc 185 { 186 CFArrayCallBacks result; 187 with(result) 188 { 189 version_ = 0; // currently, 0 is the only valid version value for this 190 retain = &CFAUPresetArrayRetainCallBack; 191 release = &CFAUPresetArrayReleaseCallBack; 192 copyDescription = &CFAUPresetArrayCopyDescriptionCallBack; 193 equal = &CFAUPresetArrayEqualCallBack; 194 } 195 return result; 196 } 197 198 199 // CoreFoundation helpers 200 201 struct CFStrLocal 202 { 203 nothrow: 204 @nogc: 205 206 CFStringRef parent; 207 alias parent this; 208 209 @disable this(); 210 @disable this(this); 211 212 static fromString(const(char)[] str) 213 { 214 CFStrLocal s = void; 215 s.parent = toCFString(str); 216 return s; 217 } 218 219 ~this() nothrow 220 { 221 CFRelease(parent); 222 } 223 } 224 225 226 /// Creates a CFString from an int give up its ownership. 227 CFStringRef convertIntToCFString(int number) nothrow @nogc 228 { 229 char[16] str; 230 snprintf(str.ptr, str.length, "%d", number); 231 return CFStringCreateWithCString(null, str.ptr, kCFStringEncodingUTF8); 232 } 233 234 /// Creates a CFString from a string and give up its ownership. 235 CFStringRef toCFString(const(char)[] str) nothrow @nogc 236 { 237 return CFStringCreateWithCString(null, CString(str).storage, kCFStringEncodingUTF8); 238 } 239 240 /// Create string from a CFString, and give up its ownership. 241 /// Such a string must be deallocated with `free`/`freeSlice`. 242 /// It is guaranteed to finish with a terminal zero character ('\0'). 243 string mallocStringFromCFString(CFStringRef cfStr) nothrow @nogc 244 { 245 int n = cast(int)CFStringGetLength(cfStr) + 1; 246 char[] buf = mallocSlice!char(n); 247 CFStringGetCString(cfStr, buf.ptr, n, kCFStringEncodingUTF8); 248 return assumeUnique(buf); 249 } 250 251 void putNumberInDict(CFMutableDictionaryRef pDict, const(char)[] key, void* pNumber, CFNumberType type) nothrow @nogc 252 { 253 CFStrLocal cfKey = CFStrLocal.fromString(key); 254 255 CFNumberRef pValue = CFNumberCreate(null, type, pNumber); 256 CFDictionarySetValue(pDict, cfKey, pValue); 257 CFRelease(pValue); 258 } 259 260 void putStrInDict(CFMutableDictionaryRef pDict, const(char)[] key, const(char)[] value) nothrow @nogc 261 { 262 CFStrLocal cfKey = CFStrLocal.fromString(key); 263 CFStrLocal cfValue = CFStrLocal.fromString(value); 264 CFDictionarySetValue(pDict, cfKey, cfValue); 265 } 266 267 void putDataInDict(CFMutableDictionaryRef pDict, const(char)[] key, ubyte[] pChunk) nothrow @nogc 268 { 269 CFStrLocal cfKey = CFStrLocal.fromString(key); 270 271 CFDataRef pData = CFDataCreate(null, pChunk.ptr, cast(CFIndex)(pChunk.length)); 272 CFDictionarySetValue(pDict, cfKey, pData); 273 CFRelease(pData); 274 } 275 276 277 bool getNumberFromDict(CFDictionaryRef pDict, const(char)[] key, void* pNumber, CFNumberType type) nothrow @nogc 278 { 279 CFStrLocal cfKey = CFStrLocal.fromString(key); 280 281 CFNumberRef pValue = cast(CFNumberRef) CFDictionaryGetValue(pDict, cfKey); 282 if (pValue) 283 { 284 CFNumberGetValue(pValue, type, pNumber); 285 return true; 286 } 287 return false; 288 } 289 290 /// Get a string in a dictionnary, and give up its ownership. 291 bool getStrFromDict(CFDictionaryRef pDict, const(char)[] key, out string value) nothrow @nogc 292 { 293 CFStrLocal cfKey = CFStrLocal.fromString(key); 294 295 CFStringRef pValue = cast(CFStringRef) CFDictionaryGetValue(pDict, cfKey); 296 if (pValue) 297 { 298 value = mallocStringFromCFString(pValue); 299 return true; 300 } 301 return false; 302 } 303 304 /// Gets data from a CFDataRef dictionnary entry and give up its ownership. 305 /// Must be deallocated with `free`/`freeSlice`. 306 bool getDataFromDict(CFDictionaryRef pDict, string key, out ubyte[] pChunk) nothrow @nogc 307 { 308 CFStrLocal cfKey = CFStrLocal.fromString(key); 309 CFDataRef pData = cast(CFDataRef) CFDictionaryGetValue(pDict, cfKey); 310 if (pData) 311 { 312 int n = cast(int)CFDataGetLength(pData); 313 pChunk = ( CFDataGetBytePtr(pData)[0..n] ).mallocDup; 314 return true; 315 } 316 return false; 317 }