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 }