1 //----------------------------------------------------------------------------- 2 // LICENSE 3 // (c) 2004-2018, Steinberg Media Technologies GmbH, All Rights Reserved 4 // (c) 2018, Guillaume Piolat (contact@auburnsounds.com) 5 //----------------------------------------------------------------------------- 6 // 7 // This Software Development Kit is licensed under the terms of the General 8 // Public License (GPL) Version 3. 9 // 10 // Details of that license can be found at: www.gnu.org/licenses/gpl-3.0.html 11 //----------------------------------------------------------------------------- 12 module dplug.vst3.ipluginbase; 13 14 version(VST3): 15 16 import core.stdc.stdlib; 17 import core.stdc.string; 18 19 import dplug.core.nogc; 20 21 import dplug.vst3.ftypes; 22 23 24 //debug = logVST3Client; 25 26 //------------------------------------------------------------------------ 27 /** Basic interface to a Plug-in component. 28 - [plug imp] 29 - initialize/terminate the Plug-in component 30 31 The host uses this interface to initialize and to terminate the Plug-in component. 32 The context that is passed to the initialize method contains any interface to the 33 host that the Plug-in will need to work. These interfaces can vary from category to category. 34 A list of supported host context interfaces should be included in the documentation 35 of a specific category. */ 36 interface IPluginBase: IUnknown 37 { 38 public: 39 nothrow: 40 @nogc: 41 /** The host passes a number of interfaces as context to initialize the Plug-in class. 42 @note Extensive memory allocations etc. should be performed in this method rather than in the class' constructor! 43 If the method does NOT return kResultOk, the object is released immediately. In this case terminate is not called! */ 44 tresult initialize(FUnknown context); 45 46 /** This function is called before the Plug-in is unloaded and can be used for 47 cleanups. You have to release all references to any host application interfaces. */ 48 tresult terminate(); 49 50 immutable __gshared TUID iid = INLINE_UID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625); 51 } 52 53 54 // Basic Information about the class factory of the Plug-in. 55 56 struct PFactoryInfo 57 { 58 nothrow: 59 @nogc: 60 alias FactoryFlags = int; 61 enum : FactoryFlags 62 { 63 kNoFlags = 0, ///< Nothing 64 kClassesDiscardable = 1 << 0, ///< The number of exported classes can change each time the Module is loaded. If this flag is set, the host does not cache class information. This leads to a longer startup time because the host always has to load the Module to get the current class information. 65 kLicenseCheck = 1 << 1, ///< Class IDs of components are interpreted as Syncrosoft-License (LICENCE_UID). Loaded in a Steinberg host, the module will not be loaded when the license is not valid 66 kComponentNonDiscardable = 1 << 3, ///< Component won't be unloaded until process exit 67 kUnicode = 1 << 4 ///< Components have entirely unicode encoded strings. (True for VST 3 Plug-ins so far) 68 } 69 70 enum 71 { 72 kURLSize = 256, 73 kEmailSize = 128, 74 kNameSize = 64 75 } 76 77 char8[kNameSize] vendor; ///< e.g. "Steinberg Media Technologies" 78 char8[kURLSize] url; ///< e.g. "http://www.steinberg.de" 79 char8[kEmailSize] email; ///< e.g. "info@steinberg.de" 80 int32 flags; ///< (see above) 81 82 this (const(char8)* _vendor, const(char8)* _url, const(char8)* _email, int32 _flags) 83 { 84 strncpy8 (vendor.ptr, _vendor, kNameSize); 85 strncpy8 (url.ptr, _url, kURLSize); 86 strncpy8 (email.ptr, _email, kEmailSize); 87 flags = _flags; 88 flags |= kUnicode; 89 } 90 } 91 92 //------------------------------------------------------------------------ 93 /** Basic Information about a class provided by the Plug-in. 94 \ingroup pluginBase 95 */ 96 //------------------------------------------------------------------------ 97 struct PClassInfo 98 { 99 nothrow: 100 @nogc: 101 enum // ClassCardinality 102 { 103 kManyInstances = 0x7FFFFFFF 104 } 105 106 enum 107 { 108 kCategorySize = 32, 109 kNameSize = 64 110 } 111 112 TUID cid; ///< Class ID 16 Byte class GUID 113 int32 cardinality; ///< cardinality of the class, set to kManyInstances (see \ref ClassCardinality) 114 char8[kCategorySize] category; ///< class category, host uses this to categorize interfaces 115 char8[kNameSize] name; ///< class name, visible to the user 116 117 this(const TUID _cid, int32 _cardinality, const(char8)* _category, const(char8)* _name) 118 { 119 cid[] = 0; 120 cardinality = 0; 121 category[] = '\0'; 122 name[] = '\0'; 123 cid = _cid; 124 125 if (_category) 126 strncpy8 (category.ptr, _category, kCategorySize); 127 if (_name) 128 strncpy8 (name.ptr, _name, kNameSize); 129 cardinality = _cardinality; 130 } 131 } 132 133 134 //------------------------------------------------------------------------ 135 // IPluginFactory interface declaration 136 //------------------------------------------------------------------------ 137 /** Class factory that any Plug-in defines for creating class instances. 138 \ingroup pluginBase 139 - [plug imp] 140 141 From the host's point of view a Plug-in module is a factory which can create 142 a certain kind of object(s). The interface IPluginFactory provides methods 143 to get information about the classes exported by the Plug-in and a 144 mechanism to create instances of these classes (that usually define the IPluginBase interface). 145 146 <b> An implementation is provided in public.sdk/source/common/pluginfactory.cpp </b> 147 \see GetPluginFactory 148 */ 149 150 interface IPluginFactory : IUnknown 151 { 152 public: 153 nothrow: 154 @nogc: 155 /** Fill a PFactoryInfo structure with information about the Plug-in vendor. */ 156 tresult getFactoryInfo (PFactoryInfo* info); 157 158 /** Returns the number of exported classes by this factory. 159 If you are using the CPluginFactory implementation provided by the SDK, it returns the number of classes you registered with CPluginFactory::registerClass. */ 160 int32 countClasses (); 161 162 /** Fill a PClassInfo structure with information about the class at the specified index. */ 163 tresult getClassInfo (int32 index, PClassInfo* info); 164 165 /** Create a new class instance. */ 166 tresult createInstance (FIDString cid, FIDString _iid, void** obj); 167 168 __gshared immutable TUID iid = INLINE_UID(0x7A4D811C, 0x52114A1F, 0xAED9D2EE, 0x0B43BF9F); 169 } 170 171 // Version 2 of Basic Information about a class provided by the Plug-in. 172 struct PClassInfo2 173 { 174 nothrow: 175 @nogc: 176 TUID cid; ///< Class ID 16 Byte class GUID 177 int32 cardinality; ///< cardinality of the class, set to kManyInstances (see \ref ClassCardinality) 178 char8[PClassInfo.kCategorySize] category; ///< class category, host uses this to categorize interfaces 179 char8[PClassInfo.kNameSize] name; ///< class name, visible to the user 180 181 enum { 182 kVendorSize = 64, 183 kVersionSize = 64, 184 kSubCategoriesSize = 128 185 }; 186 187 uint32 classFlags; ///< flags used for a specific category, must be defined where category is defined 188 char8[kSubCategoriesSize] subCategories; ///< module specific subcategories, can be more than one, logically added by the \c OR operator 189 char8[kVendorSize] vendor; ///< overwrite vendor information from factory info 190 char8[kVersionSize] version_; ///< Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) 191 char8[kVersionSize] sdkVersion; ///< SDK version used to build this class (e.g. "VST 3.0") 192 193 this (const TUID _cid, int32 _cardinality, const(char8)* _category, const(char8)* _name, 194 int32 _classFlags, const(char8)* _subCategories, const(char8)* _vendor, const(char8)* _version, 195 const(char8)* _sdkVersion) 196 { 197 cardinality = 0; 198 category[] = '\0'; 199 name[] = '\0'; 200 classFlags = 0; 201 subCategories[] = '\0'; 202 vendor[] = '\0'; 203 version_[] = '\0'; 204 sdkVersion[] = '\0'; 205 cid = _cid; 206 207 cardinality = _cardinality; 208 if (_category) 209 strncpy8 (category.ptr, _category, PClassInfo.kCategorySize); 210 if (_name) 211 strncpy8 (name.ptr, _name, PClassInfo.kNameSize); 212 classFlags = cast(uint)(_classFlags); 213 if (_subCategories) 214 strncpy8 (subCategories.ptr, _subCategories, kSubCategoriesSize); 215 if (_vendor) 216 strncpy8 (vendor.ptr, _vendor, kVendorSize); 217 if (_version) 218 strncpy8 (version_.ptr, _version, kVersionSize); 219 if (_sdkVersion) 220 strncpy8 (sdkVersion.ptr, _sdkVersion, kVersionSize); 221 } 222 } 223 224 225 interface IPluginFactory2 : IPluginFactory 226 { 227 public: 228 nothrow: 229 @nogc: 230 /** Returns the class info (version 2) for a given index. */ 231 tresult getClassInfo2 (int32 index, PClassInfo2* info); 232 233 __gshared immutable TUID iid = INLINE_UID(0x0007B650, 0xF24B4C0B, 0xA464EDB9, 0xF00B2ABB); 234 } 235 236 //------------------------------------------------------------------------ 237 /** Unicode Version of Basic Information about a class provided by the Plug-in */ 238 //------------------------------------------------------------------------ 239 struct PClassInfoW 240 { 241 nothrow: 242 @nogc: 243 TUID cid; ///< see \ref PClassInfo 244 int32 cardinality; ///< see \ref PClassInfo 245 char8[PClassInfo.kCategorySize] category; ///< see \ref PClassInfo 246 char16[PClassInfo.kNameSize] name; ///< see \ref PClassInfo 247 248 enum 249 { 250 kVendorSize = 64, 251 kVersionSize = 64, 252 kSubCategoriesSize = 128 253 } 254 255 uint32 classFlags = 0; ///< flags used for a specific category, must be defined where category is defined 256 char8[kSubCategoriesSize] subCategories;///< module specific subcategories, can be more than one, logically added by the \c OR operator 257 char16[kVendorSize] vendor; ///< overwrite vendor information from factory info 258 char16[kVersionSize] version_; ///< Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) 259 char16[kVersionSize] sdkVersion; ///< SDK version used to build this class (e.g. "VST 3.0") 260 261 this (const TUID _cid, int32 _cardinality, const(char8)* _category, const(char16)* _name, 262 int32 _classFlags, const(char8)* _subCategories, const(char16)* _vendor, const(char16)* _version, 263 const(char16)* _sdkVersion) 264 { 265 cid = _cid; 266 cardinality = _cardinality; 267 category[] = '\0'; 268 name[] = '\0'; 269 vendor[] = '\0'; 270 version_[] = '\0'; 271 sdkVersion[] = '\0'; 272 if (_category) 273 strncpy8 (category.ptr, _category, PClassInfo.kCategorySize); 274 if (_name) 275 strncpy16 (name.ptr, _name, PClassInfo.kNameSize); 276 classFlags = cast(uint)(_classFlags); 277 if (_subCategories) 278 strncpy8 (subCategories.ptr, _subCategories, kSubCategoriesSize); 279 if (_vendor) 280 strncpy16 (vendor.ptr, _vendor, kVendorSize); 281 if (_version) 282 strncpy16 (version_.ptr, _version, kVersionSize); 283 if (_sdkVersion) 284 strncpy16 (sdkVersion.ptr, _sdkVersion, kVersionSize); 285 } 286 287 288 void fromAscii (ref const(PClassInfo2) ci2) 289 { 290 cid = ci2.cid; 291 cardinality = ci2.cardinality; 292 strncpy8 (category.ptr, ci2.category.ptr, PClassInfo.kCategorySize); 293 str8ToStr16 (name.ptr, ci2.name.ptr, PClassInfo.kNameSize); 294 classFlags = ci2.classFlags; 295 strncpy8 (subCategories.ptr, ci2.subCategories.ptr, kSubCategoriesSize); 296 str8ToStr16 (vendor.ptr, ci2.vendor.ptr, kVendorSize); 297 str8ToStr16 (version_.ptr, ci2.version_.ptr, kVersionSize); 298 str8ToStr16 (sdkVersion.ptr, ci2.sdkVersion.ptr, kVersionSize); 299 } 300 } 301 302 interface IPluginFactory3 : IPluginFactory2 303 { 304 public: 305 nothrow: 306 @nogc: 307 308 /** Returns the unicode class info for a given index. */ 309 tresult getClassInfoUnicode (int32 index, PClassInfoW* info); 310 311 /** Receives information about host*/ 312 tresult setHostContext (FUnknown* context); 313 314 __gshared immutable TUID iid = INLINE_UID(0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931); 315 } 316 317 __gshared IPluginFactory gPluginFactory = null; 318 319 extern(C) 320 { 321 alias PCreateFun = FUnknown function(void*) nothrow @nogc; 322 } 323 324 class CPluginFactory : IPluginFactory3 325 { 326 public: 327 nothrow: 328 @nogc: 329 330 this(ref const PFactoryInfo info) 331 { 332 debug(logVST3Client) debugLog(">CPluginFactory.this".ptr); 333 debug(logVST3Client) scope(exit) debugLog("<CPluginFactory.this".ptr); 334 factoryInfo = info; 335 } 336 337 ~this () 338 { 339 debug(logVST3Client) debugLog(">CPluginFactory.~this".ptr); 340 debug(logVST3Client) scope(exit) debugLog("<CPluginFactory.~this".ptr); 341 342 if (gPluginFactory is this) 343 gPluginFactory = null; 344 345 if (classes) 346 { 347 free (classes); 348 classes = null; 349 } 350 } 351 352 /** Registers a Plug-in class with classInfo version 1, returns true for success. */ 353 bool registerClass (const(PClassInfo)* info, 354 PCreateFun createFunc, 355 void* context = null) 356 { 357 debug(logVST3Client) debugLog(">registerClass".ptr); 358 debug(logVST3Client) scope(exit) debugLog("<registerClass".ptr); 359 360 if (!info || !createFunc) 361 return false; 362 363 PClassInfo2 info2; 364 memcpy (&info2, info, PClassInfo.sizeof); 365 return registerClass(&info2, createFunc, context); 366 } 367 368 /** Registers a Plug-in class with classInfo version 2, returns true for success. */ 369 bool registerClass (const(PClassInfo2)* info, 370 PCreateFun createFunc, 371 void* context = null) 372 { 373 if (!info || !createFunc) 374 return false; 375 376 if (classCount >= maxClassCount) 377 { 378 if (!growClasses ()) 379 return false; 380 } 381 382 PClassEntry* entry = &classes[classCount]; 383 entry.info8 = *info; 384 entry.info16.fromAscii (*info); 385 entry.createFunc = createFunc; 386 entry.context = context; 387 entry.isUnicode = false; 388 classCount++; 389 return true; 390 } 391 392 /** Registers a Plug-in class with classInfo Unicode version, returns true for success. */ 393 bool registerClass (const(PClassInfoW)* info, 394 PCreateFun createFunc, 395 void* context = null) 396 { 397 if (!info || !createFunc) 398 return false; 399 400 if (classCount >= maxClassCount) 401 { 402 if (!growClasses ()) 403 return false; 404 } 405 406 PClassEntry* entry = &classes[classCount]; 407 entry.info16 = *info; 408 entry.createFunc = createFunc; 409 entry.context = context; 410 entry.isUnicode = true; 411 classCount++; 412 return true; 413 } 414 415 mixin QUERY_INTERFACE_SPECIAL_CASE_IUNKNOWN!(IPluginFactory, IPluginFactory2, IPluginFactory3); 416 417 mixin IMPLEMENT_REFCOUNT; 418 419 //---from IPluginFactory------ 420 extern(Windows) override tresult getFactoryInfo (PFactoryInfo* info) 421 { 422 debug(logVST3Client) debugLog(">getFactoryInfo".ptr); 423 debug(logVST3Client) scope(exit) debugLog("<getFactoryInfo".ptr); 424 if (info) 425 memcpy (info, &factoryInfo, PFactoryInfo.sizeof); 426 return kResultOk; 427 } 428 429 extern(Windows) override int32 countClasses () 430 { 431 return classCount; 432 } 433 434 extern(Windows) override tresult getClassInfo (int32 index, PClassInfo* info) 435 { 436 debug(logVST3Client) debugLog(">getClassInfo".ptr); 437 debug(logVST3Client) scope(exit) debugLog("<getClassInfo".ptr); 438 if (info && (index >= 0 && index < classCount)) 439 { 440 if (classes[index].isUnicode) 441 { 442 memset (info, 0, PClassInfo.sizeof); 443 return kResultFalse; 444 } 445 446 memcpy (info, &classes[index].info8, PClassInfo.sizeof); 447 return kResultOk; 448 } 449 return kInvalidArgument; 450 } 451 452 extern(Windows) override tresult createInstance (FIDString cid, FIDString _iid, void** obj) 453 { 454 debug(logVST3Client) debugLog(">createInstance".ptr); 455 debug(logVST3Client) scope(exit) debugLog("<createInstance".ptr); 456 457 for (int32 i = 0; i < classCount; i++) 458 { 459 if (memcmp (classes[i].info16.cid.ptr, cid, TUID.sizeof ) == 0) 460 { 461 FUnknown instance = classes[i].createFunc (classes[i].context); 462 if (instance) 463 { 464 TUID* iid = cast(TUID*)_iid; 465 if (instance.queryInterface(*iid, obj) == kResultOk) 466 { 467 instance.release (); 468 return kResultOk; 469 } 470 else 471 instance.release (); 472 } 473 break; 474 } 475 } 476 477 *obj = null; 478 return kNoInterface; 479 } 480 481 //---from IPluginFactory2----- 482 extern(Windows) override tresult getClassInfo2 (int32 index, PClassInfo2* info) 483 { 484 debug(logVST3Client) debugLog(">getClassInfo2".ptr); 485 debug(logVST3Client) scope(exit) debugLog("<getClassInfo2".ptr); 486 487 if (info && (index >= 0 && index < classCount)) 488 { 489 if (classes[index].isUnicode) 490 { 491 memset (info, 0, PClassInfo2.sizeof); 492 return kResultFalse; 493 } 494 495 memcpy (info, &classes[index].info8, PClassInfo2.sizeof); 496 return kResultOk; 497 } 498 return kInvalidArgument; 499 } 500 501 //---from IPluginFactory3----- 502 extern(Windows) override tresult getClassInfoUnicode (int32 index, PClassInfoW* info) 503 { 504 debug(logVST3Client) debugLog(">getClassInfoUnicode".ptr); 505 debug(logVST3Client) scope(exit) debugLog("<getClassInfoUnicode".ptr); 506 507 if (info && (index >= 0 && index < classCount)) 508 { 509 memcpy (info, &classes[index].info16, PClassInfoW.sizeof); 510 return kResultOk; 511 } 512 return kInvalidArgument; 513 } 514 515 extern(Windows) override tresult setHostContext (FUnknown* context) 516 { 517 return kNotImplemented; 518 } 519 520 protected: 521 static struct PClassEntry 522 { 523 PClassInfo2 info8; 524 PClassInfoW info16; 525 526 extern(C) FUnknown function(void*) nothrow @nogc createFunc; 527 void* context; 528 bool isUnicode; 529 } 530 531 PFactoryInfo factoryInfo; 532 PClassEntry* classes = null; 533 int32 classCount = 0; 534 int32 maxClassCount = 0; 535 536 bool growClasses() 537 { 538 static const int32 delta = 10; 539 540 size_t size = (maxClassCount + delta) * PClassEntry.sizeof; 541 void* memory = classes; 542 543 if (!memory) 544 memory = malloc (size); 545 else 546 memory = realloc (memory, size); 547 548 if (!memory) 549 return false; 550 551 classes = cast(PClassEntry*)memory; 552 maxClassCount += delta; 553 return true; 554 } 555 } 556