lv2 phase 2 fixups
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2client.C
1 #ifdef HAVE_LV2
2
3 /*
4  * CINELERRA
5  * Copyright (C) 2018 GG
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "bchash.h"
24 #include "clip.h"
25 #include "cstrdup.h"
26 #include "file.h"
27 #include "filexml.h"
28 #include "language.h"
29 #include "mainerror.h"
30 #include "mwindow.h"
31 #include "plugin.h"
32 #include "pluginlv2client.h"
33 #include "pluginlv2config.h"
34 #include "pluginlv2ui.inc"
35 #include "pluginserver.h"
36 #include "pluginset.h"
37 #include "samples.h"
38 #include "track.h"
39
40 #include <ctype.h>
41 #include <string.h>
42
43 PluginLV2UIs PluginLV2ParentUI::plugin_lv2;
44
45 PluginLV2UIs::PluginLV2UIs()
46  : Mutex("PluginLV2UIs")
47 {
48 }
49
50 PluginLV2UIs::~PluginLV2UIs()
51 {
52         del_uis();
53 }
54
55 void PluginLV2UIs::del_uis()
56 {
57         while( size() ) remove_object();
58 }
59
60 PluginLV2ParentUI *PluginLV2UIs::del_ui(PluginLV2Client *client)
61 {
62         lock("PluginLV2UIs::del_ui client");
63         int i = size();
64         while( --i >= 0 && get(i)->client != client );
65         PluginLV2ParentUI *ui = 0;
66         if( i >= 0 ) {
67                 if( (ui=get(i))->gui ) ui->client = 0;
68                 else { remove_object_number(i);  ui = 0; }
69         }
70         unlock();
71         return ui;
72 }
73 PluginLV2ParentUI *PluginLV2UIs::del_ui(PluginLV2ClientWindow *gui)
74 {
75         lock("PluginLV2UIs::del_ui gui");
76         int i = size();
77         while( --i >= 0 && get(i)->gui != gui );
78         PluginLV2ParentUI *ui = 0;
79         if( i >= 0 ) {
80                 if( (ui=get(i))->client ) ui->gui = 0;
81                 else { remove_object_number(i);  ui = 0; }
82         }
83         unlock();
84         return ui;
85 }
86
87 PluginLV2ParentUI *PluginLV2UIs::add_ui(PluginLV2ParentUI *ui, PluginLV2Client *client)
88 {
89         ui->start_child();
90         ui->start_parent(client);
91         append(ui);
92         return ui;
93 }
94
95 PluginLV2ParentUI *PluginLV2UIs::search_ui(Plugin *plugin)
96 {
97         int64_t position = plugin->startproject;
98         PluginSet *plugin_set = plugin->plugin_set;
99         int set_no = plugin_set->get_number();
100         int track_no = plugin_set->track->number_of();
101
102         PluginLV2ParentUI *ui = 0;
103         for( int i=size(); !ui && --i>=0; ) {
104                 PluginLV2ParentUI *parent_ui = get(i);
105                 if( parent_ui->position != position ) continue;
106                 if( parent_ui->set_no != set_no ) continue;
107                 if( parent_ui->track_no == track_no ) ui = parent_ui;
108         }
109         return ui;
110 }
111
112 PluginLV2ParentUI *PluginLV2UIs::find_ui(Plugin *plugin)
113 {
114         if( !plugin ) return 0;
115         lock("PluginLV2UIs::find_ui");
116         PluginLV2ParentUI *ui = search_ui(plugin);
117         unlock();
118         return ui;
119 }
120 PluginLV2ParentUI *PluginLV2Client::find_ui()
121 {
122         return PluginLV2ParentUI::plugin_lv2.find_ui(server->plugin);
123 }
124 PluginLV2ParentUI *PluginLV2ClientWindow::find_ui()
125 {
126         return PluginLV2ParentUI::plugin_lv2.find_ui(client->server->plugin);
127 }
128
129 PluginLV2ParentUI *PluginLV2UIs::get_ui(PluginLV2Client *client)
130 {
131         lock("PluginLV2UIs::get_ui");
132         Plugin *plugin = client->server->plugin;
133         PluginLV2ParentUI *ui = search_ui(plugin);
134         if( !ui ) ui = add_ui(new PluginLV2ParentUI(plugin), client);
135         unlock();
136         return ui;
137 }
138 PluginLV2ParentUI *PluginLV2Client::get_ui()
139 {
140         PluginLV2ParentUI *ui = PluginLV2ParentUI::plugin_lv2.get_ui(this);
141         ui->client = this;
142         return ui;
143 }
144 PluginLV2ParentUI *PluginLV2ClientWindow::get_ui()
145 {
146         PluginLV2ParentUI *ui = PluginLV2ParentUI::plugin_lv2.get_ui(client);
147         ui->gui = this;
148         return ui;
149 }
150
151
152 PluginLV2Client::PluginLV2Client(PluginServer *server)
153  : PluginAClient(server), PluginLV2()
154 {
155         title[0] = 0;
156 }
157
158 PluginLV2Client::~PluginLV2Client()
159 {
160         PluginLV2ParentUI::plugin_lv2.del_ui(this);
161 }
162
163 NEW_WINDOW_MACRO(PluginLV2Client, PluginLV2ClientWindow)
164
165 int PluginLV2Client::init_lv2()
166 {
167         int sample_rate = get_project_samplerate();
168         return PluginLV2::init_lv2(config, sample_rate);
169 }
170
171 int PluginLV2Client::load_configuration()
172 {
173         int64_t src_position =  get_source_position();
174         KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
175         PluginLV2ClientConfig curr_config;
176         curr_config.copy_from(config);
177         read_data(prev_keyframe);
178         int ret = !config.equivalent(curr_config) ? 1 : 0;
179         return ret;
180 }
181
182 void PluginLV2Client::update_gui()
183 {
184         PluginClientThread *thread = get_thread();
185         if( !thread ) return;
186         PluginLV2ClientWindow *gui = (PluginLV2ClientWindow*)thread->get_window();
187         gui->lock_window("PluginFClient::update_gui");
188         load_configuration();
189         if( config.update() > 0 ) {
190                 gui->update_selected();
191                 update_lv2();
192         }
193         gui->unlock_window();
194 }
195
196 void PluginLV2Client::update_lv2()
197 {
198         PluginLV2ParentUI *ui = find_ui();
199         if( !ui ) return;
200         ui->send_child(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
201 }
202
203
204 int PluginLV2Client::is_realtime() { return 1; }
205 int PluginLV2Client::is_multichannel() { return nb_inputs > 1 || nb_outputs > 1 ? 1 : 0; }
206 const char* PluginLV2Client::plugin_title() { return title; }
207 int PluginLV2Client::uses_gui() { return 1; }
208 int PluginLV2Client::is_synthesis() { return 1; }
209
210 char* PluginLV2Client::to_string(char *string, const char *input)
211 {
212         char *bp = string;
213         for( const char *cp=input; *cp; ++cp )
214                 *bp++ = isalnum(*cp) ? *cp : '_';
215         *bp = 0;
216         return string;
217 }
218
219 void PluginLV2Client::save_data(KeyFrame *keyframe)
220 {
221         FileXML output;
222         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
223         char name[BCTEXTLEN];  to_string(name, plugin_title());
224         output.tag.set_title(name);
225         for( int i=0; i<config.size(); ++i ) {
226                 PluginLV2Client_Opt *opt = config[i];
227                 output.tag.set_property(opt->get_symbol(), opt->get_value());
228         }
229         output.append_tag();
230         output.terminate_string();
231 }
232
233 void PluginLV2Client::read_data(KeyFrame *keyframe)
234 {
235         FileXML input;
236         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
237         char name[BCTEXTLEN];  to_string(name, plugin_title());
238
239         while( !input.read_tag() ) {
240                 if( !input.tag.title_is(name) ) continue;
241                 for( int i=0; i<config.size(); ++i ) {
242                         PluginLV2Client_Opt *opt = config[i];
243                         float value = input.tag.get_property(opt->get_symbol(), 0.);
244                         opt->set_value(value);
245                 }
246         }
247 }
248
249 void PluginLV2Client::load_buffer(int samples, Samples **input, int ich)
250 {
251         for( int i=0; i<nb_inputs; ++i ) {
252                 int k = i < ich ? i : 0;
253                 double *inp = input[k]->get_data();
254                 float *ip = in_buffers[i];
255                 for( int j=samples; --j>=0; *ip++=*inp++ );
256         }
257         for( int i=0; i<nb_outputs; ++i )
258                 bzero(out_buffers[i], samples*sizeof(float));
259 }
260
261 int PluginLV2Client::unload_buffer(int samples, Samples **output, int och)
262 {
263         if( nb_outputs < och ) och = nb_outputs;
264         for( int i=0; i<och; ++i ) {
265                 double *outp = output[i]->get_data();
266                 float *op = out_buffers[i];
267                 for( int j=samples; --j>=0; *outp++=*op++ );
268         }
269         return samples;
270 }
271
272 void PluginLV2Client::process_buffer(int size)
273 {
274         PluginLV2ParentUI *ui = get_ui();
275         if( !ui ) return;
276         shm_bfr->done = 0;
277         ui->send_child(LV2_SHMID, &shmid, sizeof(shmid));
278 // timeout 10*duration, min 2sec, max 10sec
279         double sample_rate = get_project_samplerate();
280         double t = !sample_rate ? 2. : 10. * size / sample_rate;
281         bclamp(t, 2., 10.);
282         ui->output_bfr->timed_lock(t*1e6, "PluginLV2Client::process_buffer");
283         if( !shm_bfr->done )
284                 eprintf("timeout: %s",server->title);
285 }
286
287 int PluginLV2Client::process_realtime(int64_t size,
288         Samples *input_ptr, Samples *output_ptr)
289 {
290         if( load_configuration() )
291                 update_lv2();
292         init_buffer(size);
293         load_buffer(size, &input_ptr, 1);
294         process_buffer(size);
295         return unload_buffer(size, &output_ptr, 1);
296 }
297
298 int PluginLV2Client::process_realtime(int64_t size,
299         Samples **input_ptr, Samples **output_ptr)
300 {
301         if( load_configuration() )
302                 update_lv2();
303         init_buffer(size);
304         load_buffer(size, input_ptr, PluginClient::total_in_buffers);
305         process_buffer(size);
306         return unload_buffer(size, output_ptr, PluginClient::total_out_buffers);
307 }
308
309
310 PluginLV2BlackList::PluginLV2BlackList(const char *path)
311 {
312         set_array_delete();
313         char lv2_blacklist_path[BCTEXTLEN];
314         sprintf(lv2_blacklist_path, "%s/%s", File::get_cindat_path(), path);
315         FILE *bfp = fopen(lv2_blacklist_path, "r");
316         if( !bfp ) return;
317         while( fgets(lv2_blacklist_path, sizeof(lv2_blacklist_path), bfp) ) {
318                 if( lv2_blacklist_path[0] == '#' ) continue;
319                 int len = strlen(lv2_blacklist_path);
320                 if( len > 0 && lv2_blacklist_path[len-1] == '\n' )
321                         lv2_blacklist_path[len-1] = 0;
322                 if( !lv2_blacklist_path[0] ) continue;
323                 append(cstrdup(lv2_blacklist_path));
324         }
325         fclose(bfp);
326 }
327
328 PluginLV2BlackList::~PluginLV2BlackList()
329 {
330         remove_all_objects();
331 }
332
333 int PluginLV2BlackList::is_badboy(const char *uri)
334 {
335         for( int i=size(); --i>=0; )
336                 if( !strcmp(uri, get(i)) ) return 1;
337         return 0;
338 }
339
340 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
341 {
342         return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
343 }
344
345 PluginClient *PluginServer::new_lv2_plugin()
346 {
347         PluginLV2Client *client = new PluginLV2Client(this);
348         if( client->load_lv2(path, client->title) ) { delete client;  return client = 0; }
349         client->init_lv2();
350         return client;
351 }
352
353 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
354 {
355         printf("init lv2 index:\n");
356         PluginLV2BlackList blacklist("lv2_blacklist.txt");
357
358         LilvWorld *world = lilv_world_new();
359         lilv_world_load_all(world);
360         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
361
362         LILV_FOREACH(plugins, i, all_plugins) {
363                 const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
364                 const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
365                 if( blacklist.is_badboy(uri) ) continue;
366 printf("LOAD: %s\n", uri);
367                 PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
368                 int result = server.open_plugin(1, preferences, 0, 0);
369                 if( !result ) {
370                         server.write_table(fp, uri, PLUGIN_LV2_ID, 0);
371                         server.close_plugin();
372                 }
373         }
374
375         lilv_world_free(world);
376         return 0;
377 }
378
379 PluginLV2ParentUI::PluginLV2ParentUI(Plugin *plugin)
380 {
381         this->position = plugin->startproject;
382         PluginSet *plugin_set = plugin->plugin_set;
383         if( plugin_set ) {
384                 this->set_no = plugin_set->get_number();
385                 this->track_no = plugin_set->track->number_of();
386         }
387         else
388                 this->track_no = this->set_no = -1;
389
390         output_bfr = new Condition(0, "PluginLV2ParentUI::output_bfr", 1);
391         client = 0;
392         gui = 0;
393         hidden = 1;
394 }
395
396 PluginLV2ParentUI::~PluginLV2ParentUI()
397 {
398         stop();
399         delete output_bfr;
400 }
401
402 void PluginLV2ParentUI::start_parent(PluginLV2Client *client)
403 {
404         start();
405         const char *path = client->server->path;
406         int len = sizeof(open_bfr_t) + strlen(path);
407         char bfr[len];  memset(bfr, 0, len);
408         open_bfr_t *open_bfr = (open_bfr_t *)bfr;
409         open_bfr->sample_rate = client->get_project_samplerate();
410         strcpy(open_bfr->path, path);
411         send_child(LV2_OPEN, open_bfr, len);
412         PluginLV2ClientConfig &conf = client->config;
413         send_child(LV2_LOAD, conf.ctls, conf.nb_ports*sizeof(float));
414 }
415
416 int PluginLV2ParentUI::handle_parent()
417 {
418         int result = 1;
419
420         switch( parent_token ) {
421         case LV2_UPDATE: {
422                 if( !gui ) break;
423                 gui->lv2_update((float *)parent_data);
424                 break; }
425         case LV2_HIDE: {
426                 hidden = 1;
427                 break; }
428         case LV2_SHOW: {
429                 hidden = 0;
430                 break; }
431         case LV2_SHMID: {
432                 output_bfr->unlock();
433                 break; }
434         case EXIT_CODE: {
435                 hidden = 1;
436                 output_bfr->unlock();
437                 result = -1;
438                 break; }
439         default:
440                 result = 0;
441                 break;
442         }
443
444         return result;
445 }
446
447 int PluginLV2ParentUI::show()
448 {
449         if( !hidden ) return 1;
450         send_child(LV2_SHOW, 0, 0);
451         return 0;
452 }
453
454 int PluginLV2ParentUI::hide()
455 {
456         if( hidden ) return 1;
457         send_child(LV2_HIDE, 0, 0);
458         return 0;
459 }
460
461 ForkChild *PluginLV2ParentUI::new_fork()
462 {
463         return new PluginLV2ChildUI();
464 }
465
466 // stub in parent
467 int PluginLV2ChildUI::child_iteration(int64_t usec) { return -1; }
468 int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes) { return -1; }
469
470 #else
471 #include "mwindow.h"
472 #include "pluginserver.h"
473
474 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name) { return 0; }
475 PluginClient *PluginServer::new_lv2_plugin() { return 0; }
476 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp) { return 0; }
477
478 #endif /* HAVE_LV2 */