1 /// This test shows how to use the runtime in a plug-in which
2 /// would have an otherwise disabled runtime
3 ///
4 /// ============================================================================
5 ///
6 ///         VERY IMPORTANT MESSAGE
7 ///     This is a bit of an experimental feature!
8 ///
9 ///     1. You can't use it with DMD + macOS. Because DMD doesn't support 
10 ///        shared library initialization on macOS.
11 ///
12 ///     2. It is yet unknown how far Mac compatibility is broken by using 
13 ///        the runtime. It is known to work since macOS 10.12 with LDC >= 1.3
14 ///        but we don't have the data for previous macOS versions.
15 ///
16 ///     3. The behaviour on POSIX in presence of multiple instances is yet unknown.
17 ///        Please let us know.
18 ///
19 /// ============================================================================
20 module main;
21 
22 import core.memory;
23 import std.stdio;
24 import dplug.core, dplug.client, dplug.vst;
25 
26 // This create the DLL entry point
27 mixin(DLLEntryPoint!());
28 
29 // This create the VST entry point
30 mixin(VSTEntryPoint!RuntimeTestPlugin);
31 
32 final class RuntimeTestPlugin : dplug.client.Client
33 {
34 
35 public:
36 nothrow:
37 @nogc:
38 
39     // <needed for runtime> This is required so that the rest of the plug-in can make runtime calls.
40     ScopedRuntime _runtime;
41     this()
42     {
43         _runtime.initialize();
44     }
45     // </needed for runtime>
46 
47     // Needed for the removal of _heapObject which was added as root.
48     // So this has to be mirrored.
49     //
50     // Note: You are subjected to the classical D limitation with regards
51     //       to the order of finalization of GC objects.
52     ~this()
53     {       
54         runtimeSection(&cleanupObject)(_heapObject);
55     }
56 
57     override PluginInfo buildPluginInfo()
58     {
59         static immutable PluginInfo pluginInfo = parsePluginInfo(import("plugin.json"));
60         return pluginInfo;
61     }
62 
63     override LegalIO[] buildLegalIO()
64     {
65         auto io = makeVec!LegalIO();
66         io.pushBack(LegalIO(2, 2));
67         return io.releaseData();
68     }
69 
70     override void reset(double sampleRate, int maxFrames, int numInputs, int numOutputs) 
71     {
72         // Note: this doesn't need to be `@nogc`.
73         // This can be a delegate, or a raw function pointer,.
74         int functionThatUseRuntime(ref HeapObject obj) nothrow 
75         {
76             // Note: here you can call runtime functions and stuff, however
77             // keep in mind nothing GC-allocated must escape that function.
78             // That limits applicability 
79 
80             // Here you can create garbage
81             float[] invalidMemory = new float[13];
82             assert(invalidMemory.capacity != 0);
83             
84             if (obj is null) 
85             {
86                 // Allocate an object on the GC heap
87                 obj = new HeapObject;
88 
89                 // Make it survive the end of the Runtime Section by being referenced elsewhere
90                 // (than the stack of this thread)
91                 GC.addRoot(cast(void*)obj);
92             }
93 
94             return 1984;
95         }
96 
97         // Note: this returns a callable Voldemort from a GC-using delegate
98         // However this delegate should not have a GC closure, so you can't refer to _members.
99         // This is a stark limitation, sorry.
100         auto runtimeUsingFunction = runtimeSection(&functionThatUseRuntime);
101         int result = runtimeUsingFunction(_heapObject);
102         assert(result == 1984);
103     
104         // You can call stand-alone functions or methods
105         // but they have to be `nothrow`.
106         size_t len = runtimeSection(&myCarelessFunction)();
107         assert(len == 4000);
108     }
109 
110     override void processAudio(const(float*)[] inputs, float*[]outputs, int frames, TimeInfo info)
111     {    
112         outputs[0][0..frames] = 0;
113         outputs[1][0..frames] = 0;
114     }
115 
116     HeapObject _heapObject;
117 }
118 
119 class HeapObject
120 {
121     this() nothrow
122     {
123         try
124         {
125             writeln("Creating a GC object");
126         }
127         catch(Exception e)
128         {
129         }
130     }
131 
132     ~this() nothrow
133     {
134         try
135         {
136             writeln("Destroying a GC object");
137         }
138         catch(Exception e)
139         {
140         }
141     }
142 }
143 
144 static void cleanupObject(ref HeapObject obj) nothrow
145 {
146     if (obj !is null) 
147     {
148         GC.removeRoot(cast(void*)obj);
149         obj = null;
150     }
151 }
152 
153 size_t myCarelessFunction() nothrow
154 {
155     auto A = new ubyte[4000];
156     return A.length;
157 }
158