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