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 }