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