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