1 /**
2 * LV2 Client implementation
3 *
4 * Copyright: Ethan Reker 2018-2019.
5 *            Guillaume Piolat 2019.
6 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 */
8 /*
9  * DISTRHO Plugin Framework (DPF)
10  * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
11  *
12  * Permission to use, copy, modify, and/or distribute this software for any purpose with
13  * or without fee is hereby granted, provided that the above copyright notice and this
14  * permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
17  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
18  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
19  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 module dplug.lv2.lv2_init;
24 
25 version(LV2):
26 
27 import core.stdc.stdint;
28 import core.stdc.string;
29 
30 import dplug.core.nogc;
31 import dplug.core.runtime;
32 
33 import dplug.client.client;
34 
35 import dplug.lv2.lv2;
36 import dplug.lv2.ui;
37 import dplug.lv2.lv2client;
38 import dplug.lv2.ttl;
39 
40 //debug = debugLV2Client;
41 
42 
43 
44 /**
45  * Main entry point for LV2 plugins.
46  */
47 template LV2EntryPoint(alias ClientClass)
48 {
49     static immutable enum lv2_descriptor =
50         "export extern(C) const(void)* lv2_descriptor(uint index) nothrow @nogc" ~
51         "{" ~
52         "    return lv2_descriptor_templated!" ~ ClientClass.stringof ~ "(index);" ~
53         "}\n";
54 
55     static immutable enum lv2_ui_descriptor =
56         "export extern(C) const(void)* lv2ui_descriptor(uint index)nothrow @nogc" ~
57         "{" ~
58         "    return lv2ui_descriptor_templated!" ~ ClientClass.stringof ~ "(index);" ~
59         "}\n";
60 
61     static immutable enum generate_manifest_from_client =
62         "export extern(C) int GenerateManifestFromClient(char* manifestBuf, int manifestBufLen, const(char)* binaryFileName, int binaryFileNameLen)"  ~
63         "{" ~
64         "    return GenerateManifestFromClient_templated!" ~ ClientClass.stringof ~ "(manifestBuf[0..manifestBufLen], binaryFileName[0..binaryFileNameLen]);" ~
65         "}\n";
66 
67     const char[] LV2EntryPoint = lv2_descriptor ~ lv2_ui_descriptor ~ generate_manifest_from_client;
68 }
69 
70 const(LV2_Descriptor)* lv2_descriptor_templated(ClientClass)(uint index) nothrow @nogc
71 {
72     debug(debugLV2Client) debugLog(">lv2_descriptor_templated");
73     ScopedForeignCallback!(false, true) scopedCallback;
74     scopedCallback.enter();
75     
76     build_all_lv2_descriptors!ClientClass();
77     if(index >= cast(int)(lv2Descriptors.length))
78         return null;
79 
80     debug(debugLV2Client) debugLog("<lv2_descriptor_templated");
81     return &lv2Descriptors[index];
82 }
83 
84 const (LV2UI_Descriptor)* lv2ui_descriptor_templated(ClientClass)(uint index) nothrow @nogc
85 {
86     debug(debugLV2Client) debugLog(">lv2ui_descriptor_templated");
87     ScopedForeignCallback!(false, true) scopedCallback;
88     scopedCallback.enter();
89 
90     
91     build_all_lv2_descriptors!ClientClass();
92     if (hasUI && index == 0)
93     {
94         debug(debugLV2Client) debugLog("<lv2ui_descriptor_templated");
95         return &lv2UIDescriptor;
96     }
97     else
98         return null;
99 }
100 
101 extern(C) static LV2_Handle instantiate(ClientClass)(const(LV2_Descriptor)* descriptor,
102                                                      double rate,
103                                                      const(char)* bundle_path,
104                                                      const(LV2_Feature*)* features)
105 {
106     debug(debugLV2Client) debugLog(">instantiate");
107     ScopedForeignCallback!(false, true) scopedCallback;
108     scopedCallback.enter();
109     
110     LV2_Handle handle = cast(LV2_Handle)myLV2EntryPoint!ClientClass(descriptor, rate, bundle_path, features);
111     debug(debugLV2Client) debugLog("<instantiate");
112     return handle;
113 }
114 
115 
116 private:
117 
118 // These are initialized lazily by `build_all_lv2_descriptors`
119 __gshared bool descriptorsAreInitialized = false;
120 __gshared LV2_Descriptor[] lv2Descriptors;
121 __gshared bool hasUI;
122 __gshared LV2UI_Descriptor lv2UIDescriptor;
123 
124 // build all needed LV2_Descriptors and LV2UI_Descriptor lazily
125 void build_all_lv2_descriptors(ClientClass)() nothrow @nogc
126 {
127     if (descriptorsAreInitialized)
128         return;
129 
130     debug(debugLV2Client) debugLog(">build_all_lv2_descriptors");
131 
132     // Build a client
133     auto client = mallocNew!ClientClass();
134     scope(exit) client.destroyFree();
135 
136     LegalIO[] legalIOs = client.legalIOs();
137 
138     lv2Descriptors = mallocSlice!LV2_Descriptor(legalIOs.length); // Note: leaked
139 
140     char[256] uriBuf;
141 
142     for(int io = 0; io < cast(int)(legalIOs.length); io++)
143     {
144         // Make an URI for this I/O configuration
145         sprintPluginURI_IO(uriBuf.ptr, 256, client.pluginHomepage(), client.getPluginUniqueID(), legalIOs[io]);
146 
147         lv2Descriptors[io] = LV2_Descriptor.init;
148 
149         lv2Descriptors[io].URI = stringDup(uriBuf.ptr).ptr;
150         lv2Descriptors[io].instantiate = &instantiate!ClientClass;
151         lv2Descriptors[io].connect_port = &connect_port;
152         lv2Descriptors[io].activate = &activate;
153         lv2Descriptors[io].run = &run;
154         lv2Descriptors[io].deactivate = &deactivate;
155         lv2Descriptors[io].cleanup = &cleanup;
156         lv2Descriptors[io].extension_data = null;//extension_data; support it for real
157     }
158 
159 
160     if (client.hasGUI())
161     {
162         hasUI = true;
163 
164         // Make an URI for this the UI
165         sprintPluginURI_UI(uriBuf.ptr, 256, client.pluginHomepage(), client.getPluginUniqueID());
166 
167         LV2UI_Descriptor descriptor =
168         {
169             URI:            stringDup(uriBuf.ptr).ptr,
170             instantiate:    &instantiateUI,
171             cleanup:        &cleanupUI,
172             port_event:     &port_eventUI,
173             extension_data: &extensionDataUI
174         };
175         lv2UIDescriptor = descriptor;
176     }
177     else
178     {
179         hasUI = false;
180     }
181     descriptorsAreInitialized = true;
182     debug(debugLV2Client) debugLog("<build_all_lv2_descriptors");
183 }
184 
185 
186 
187 LV2Client myLV2EntryPoint(alias ClientClass)(const LV2_Descriptor* descriptor,
188                                              double rate,
189                                              const char* bundle_path,
190                                              const(LV2_Feature*)* features) nothrow @nogc
191 {
192     debug(debugLV2Client) debugLog(">myLV2EntryPoint");
193     auto client = mallocNew!ClientClass();
194 
195     // Find which decsriptor was used using pointer offset
196     int legalIOIndex = cast(int)(descriptor - lv2Descriptors.ptr);
197     auto lv2client = mallocNew!LV2Client(client, legalIOIndex);
198 
199     lv2client.instantiate(descriptor, rate, bundle_path, features);
200     debug(debugLV2Client) debugLog("<myLV2EntryPoint");
201     return lv2client;
202 }
203 
204 /*
205     LV2 Callback funtion implementations
206 */
207 extern(C) nothrow @nogc
208 {
209     void connect_port(LV2_Handle instance, uint32_t   port, void* data)
210     {
211         debug(debugLV2Client) debugLog(">connect_port");
212         ScopedForeignCallback!(false, true) scopedCallback;
213         scopedCallback.enter();
214         LV2Client lv2client = cast(LV2Client)instance;
215         lv2client.connect_port(port, data);
216         debug(debugLV2Client) debugLog("<connect_port");
217     }
218 
219     void activate(LV2_Handle instance)
220     {
221         debug(debugLV2Client) debugLog(">activate");
222         ScopedForeignCallback!(false, true) scopedCallback;
223         scopedCallback.enter();
224         LV2Client lv2client = cast(LV2Client)instance;
225         lv2client.activate();
226         debug(debugLV2Client) debugLog("<activate");
227     }
228 
229     void run(LV2_Handle instance, uint32_t n_samples)
230     {
231         ScopedForeignCallback!(false, true) scopedCallback;
232         scopedCallback.enter();
233         LV2Client lv2client = cast(LV2Client)instance;
234         lv2client.run(n_samples);
235     }
236 
237     void deactivate(LV2_Handle instance)
238     {
239         debug(debugLV2Client) debugLog(">deactivate");
240         debug(debugLV2Client) debugLog("<deactivate");
241     }
242 
243     void cleanup(LV2_Handle instance)
244     {
245         debug(debugLV2Client) debugLog(">cleanup");
246         ScopedForeignCallback!(false, true) scopedCallback;
247         scopedCallback.enter();
248         LV2Client lv2client = cast(LV2Client)instance;
249         lv2client.destroyFree();
250         debug(debugLV2Client) debugLog("<cleanup");
251     }
252 
253     const (void)* extensionDataUI(const char* uri)
254     {
255         void* feature = null;
256         debug(debugLV2Client) debugLog(">extension_dataUI");
257         static const LV2UI_Resize lv2UIResize = LV2UI_Resize(cast(void*)null, &uiResize);
258         if (!strcmp(uri, LV2_UI__resize)) {
259             feature = cast(void*)&lv2UIResize;
260         }
261         debug(debugLV2Client) debugLog("<extension_dataUI");
262         return feature;
263     }
264     
265     /// This is currently not fully implemented
266     /// According to the LV2 IRC channel, this extension is planned to be
267     /// phased out.  The only known host that uses this extension is
268     /// synthpod.  LV2 plug-ins should respond directly to resize
269     /// events from the window.
270     /// Note: is it used at all?
271     int uiResize(LV2UI_Feature_Handle handle, int width, int height)
272     {
273         ScopedForeignCallback!(false, true) scopedCallback;
274         scopedCallback.enter();
275         LV2Client lv2client = cast(LV2Client)handle;
276         return 0;
277     }
278 
279     LV2UI_Handle instantiateUI(const LV2UI_Descriptor* descriptor,
280                                const char*             plugin_uri,
281                                const char*             bundle_path,
282                                LV2UI_Write_Function    write_function,
283                                LV2UI_Controller        controller,
284                                LV2UI_Widget*           widget,
285                                const (LV2_Feature*)*   features)
286     {
287         debug(debugLV2Client) debugLog(">instantiateUI");
288         ScopedForeignCallback!(false, true) scopedCallback;
289         scopedCallback.enter();
290         void* instance_access = lv2_features_data(features, "http://lv2plug.in/ns/ext/instance-access");
291         if (instance_access)
292         {
293             LV2Client lv2client = cast(LV2Client)instance_access;
294             lv2client.instantiateUI(descriptor, plugin_uri, bundle_path, write_function, controller, widget, features);
295             debug(debugLV2Client) debugLog("<instantiateUI");
296             return cast(LV2UI_Handle)instance_access;
297         }
298         else
299         {
300             debug(debugLV2Client) debugLog("Error: Instance access is not available\n");
301             return null;
302         }
303     }
304 
305     void write_function(LV2UI_Controller controller,
306                               uint32_t         port_index,
307                               uint32_t         buffer_size,
308                               uint32_t         port_protocol,
309                               const void*      buffer)
310     {
311         debug(debugLV2Client) debugLog(">write_function");
312         debug(debugLV2Client) debugLog("<write_function");
313     }
314 
315     void cleanupUI(LV2UI_Handle ui)
316     {
317         debug(debugLV2Client) debugLog(">cleanupUI");
318         ScopedForeignCallback!(false, true) scopedCallback;
319         scopedCallback.enter();
320         LV2Client lv2client = cast(LV2Client)ui;
321         lv2client.cleanupUI();
322         debug(debugLV2Client) debugLog("<cleanupUI");
323     }
324 
325     void port_eventUI(LV2UI_Handle ui,
326                       uint32_t     port_index,
327                       uint32_t     buffer_size,
328                       uint32_t     format,
329                       const void*  buffer)
330     {
331         debug(debugLV2Client) debugLog(">port_event");
332         ScopedForeignCallback!(false, true) scopedCallback;
333         scopedCallback.enter();
334         LV2Client lv2client = cast(LV2Client)ui;
335         lv2client.portEventUI(port_index, buffer_size, format, buffer);
336         debug(debugLV2Client) debugLog("<port_event");
337     }
338 
339     const (void)* extension_dataUI(const char* uri)
340     {
341         debug(debugLV2Client) debugLog(">extension_dataUI");
342         debug(debugLV2Client) debugLog("<extension_dataUI");
343         return null;
344     }
345 }