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