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