port 7.2 mods: align_edits foreground plugin refresh_frame tweak, rework soundlevel...
[goodguy/cinelerra.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 "edl.h"
27 #include "edlsession.h"
28 #include "file.h"
29 #include "filesystem.h"
30 #include "filexml.h"
31 #include "language.h"
32 #include "mainerror.h"
33 #include "mwindow.h"
34 #include "plugin.h"
35 #include "pluginlv2client.h"
36 #include "pluginlv2config.h"
37 #include "pluginlv2ui.inc"
38 #include "pluginserver.h"
39 #include "pluginset.h"
40 #include "samples.h"
41 #include "track.h"
42
43 #include <ctype.h>
44 #include <string.h>
45
46 PluginLV2UIs PluginLV2ParentUI::plugin_lv2;
47
48 PluginLV2UIs::PluginLV2UIs()
49  : Mutex("PluginLV2UIs")
50 {
51 }
52
53 PluginLV2UIs::~PluginLV2UIs()
54 {
55         del_uis();
56 }
57
58 void PluginLV2UIs::del_uis()
59 {
60         while( size() ) remove_object();
61 }
62
63 PluginLV2ParentUI *PluginLV2UIs::del_ui(PluginLV2Client *client)
64 {
65         lock("PluginLV2UIs::del_ui client");
66         int i = size();
67         while( --i >= 0 && get(i)->client != client );
68         PluginLV2ParentUI *ui = 0;
69         if( i >= 0 ) {
70                 if( (ui=get(i))->gui ) ui->client = 0;
71                 else { remove_object_number(i);  ui = 0; }
72         }
73         unlock();
74         return ui;
75 }
76 PluginLV2ParentUI *PluginLV2UIs::del_ui(PluginLV2ClientWindow *gui)
77 {
78         lock("PluginLV2UIs::del_ui gui");
79         int i = size();
80         while( --i >= 0 && get(i)->gui != gui );
81         PluginLV2ParentUI *ui = 0;
82         if( i >= 0 ) {
83                 if( (ui=get(i))->client ) ui->gui = 0;
84                 else { remove_object_number(i);  ui = 0; }
85         }
86         unlock();
87         return ui;
88 }
89
90 PluginLV2ParentUI *PluginLV2UIs::add_ui(PluginLV2ParentUI *ui, PluginLV2Client *client)
91 {
92         ui->start_child();
93         ui->start_parent(client);
94         append(ui);
95         return ui;
96 }
97
98 PluginLV2ParentUI *PluginLV2UIs::search_ui(Plugin *plugin)
99 {
100         int64_t position = plugin->startproject;
101         PluginSet *plugin_set = plugin->plugin_set;
102         int set_no = plugin_set->get_number();
103         int track_no = plugin_set->track->number_of();
104
105         PluginLV2ParentUI *ui = 0;
106         for( int i=size(); !ui && --i>=0; ) {
107                 PluginLV2ParentUI *parent_ui = get(i);
108                 if( parent_ui->position != position ) continue;
109                 if( parent_ui->set_no != set_no ) continue;
110                 if( parent_ui->track_no == track_no ) ui = parent_ui;
111         }
112         return ui;
113 }
114
115 PluginLV2ParentUI *PluginLV2UIs::find_ui(Plugin *plugin)
116 {
117         if( !plugin ) return 0;
118         lock("PluginLV2UIs::find_ui");
119         PluginLV2ParentUI *ui = search_ui(plugin);
120         unlock();
121         return ui;
122 }
123 PluginLV2ParentUI *PluginLV2Client::find_ui()
124 {
125         return PluginLV2ParentUI::plugin_lv2.find_ui(server->plugin);
126 }
127 PluginLV2ParentUI *PluginLV2ClientWindow::find_ui()
128 {
129         return PluginLV2ParentUI::plugin_lv2.find_ui(client->server->plugin);
130 }
131
132 PluginLV2ParentUI *PluginLV2UIs::get_ui(PluginLV2Client *client)
133 {
134         lock("PluginLV2UIs::get_ui");
135         Plugin *plugin = client->server->plugin;
136         PluginLV2ParentUI *ui = search_ui(plugin);
137         if( !ui ) ui = add_ui(new PluginLV2ParentUI(plugin), client);
138         unlock();
139         return ui;
140 }
141 PluginLV2ParentUI *PluginLV2Client::get_ui()
142 {
143         PluginLV2ParentUI *ui = PluginLV2ParentUI::plugin_lv2.get_ui(this);
144         ui->client = this;
145         return ui;
146 }
147 PluginLV2ParentUI *PluginLV2ClientWindow::get_ui()
148 {
149         PluginLV2ParentUI *ui = PluginLV2ParentUI::plugin_lv2.get_ui(client);
150         ui->gui = this;
151         return ui;
152 }
153
154
155 PluginLV2Client::PluginLV2Client(PluginServer *server)
156  : PluginAClient(server), PluginLV2()
157 {
158         title[0] = 0;
159 }
160
161 PluginLV2Client::~PluginLV2Client()
162 {
163         PluginLV2ParentUI::plugin_lv2.del_ui(this);
164 }
165
166 NEW_WINDOW_MACRO(PluginLV2Client, PluginLV2ClientWindow)
167
168 int PluginLV2Client::init_lv2()
169 {
170         int bfrsz = block_length;
171         EDL *edl = server->edl;
172         if( edl ) {
173                 PlaybackConfig *playback_config = edl->session->playback_config;
174                 bfrsz = playback_config->aconfig->fragment_size;
175         }
176         int sample_rate = get_project_samplerate();
177         if( sample_rate < 64 ) sample_rate = samplerate;
178         return PluginLV2::init_lv2(config, sample_rate, bfrsz);
179 }
180
181 int PluginLV2Client::load_configuration()
182 {
183         int64_t src_position =  get_source_position();
184         KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
185         if( prev_keyframe->is_default ) return 0;
186         PluginLV2ClientConfig curr_config;
187         curr_config.copy_from(config);
188         read_data(prev_keyframe);
189         int ret = !config.equivalent(curr_config) ? 1 : 0;
190         return ret;
191 }
192
193 void PluginLV2Client::update_gui()
194 {
195         PluginClientThread *thread = get_thread();
196         if( !thread ) return;
197         PluginLV2ClientWindow *gui = (PluginLV2ClientWindow*)thread->get_window();
198         gui->lock_window("PluginFClient::update_gui");
199         load_configuration();
200         if( config.update() > 0 ) {
201                 gui->update_selected();
202                 update_lv2(LV2_UPDATE);
203         }
204         gui->unlock_window();
205 }
206
207 void PluginLV2Client::update_lv2(int token)
208 {
209         PluginLV2ParentUI *ui = find_ui();
210         if( !ui ) return;
211         ui->send_child(token, config.ctls, sizeof(float)*config.nb_ports);
212 }
213
214
215 int PluginLV2Client::is_realtime() { return 1; }
216 int PluginLV2Client::is_multichannel() { return nb_inputs > 1 || nb_outputs > 1 ? 1 : 0; }
217 const char* PluginLV2Client::plugin_title() { return title; }
218 int PluginLV2Client::uses_gui() { return 1; }
219 int PluginLV2Client::is_synthesis() { return 1; }
220
221 char* PluginLV2Client::to_string(char *string, const char *input)
222 {
223         char *bp = string;
224         for( const char *cp=input; *cp; ++cp )
225                 *bp++ = isalnum(*cp) ? *cp : '_';
226         *bp = 0;
227         return string;
228 }
229
230 void PluginLV2Client::save_data(KeyFrame *keyframe)
231 {
232         FileXML output;
233         output.set_shared_output(keyframe->xbuf);
234         char name[BCTEXTLEN];  to_string(name, plugin_title());
235         output.tag.set_title(name);
236         for( int i=0; i<config.size(); ++i ) {
237                 PluginLV2Client_Opt *opt = config[i];
238                 output.tag.set_property(opt->get_symbol(), opt->get_value());
239         }
240         output.append_tag();
241         output.terminate_string();
242 }
243
244 void PluginLV2Client::read_data(KeyFrame *keyframe)
245 {
246         FileXML input;
247         input.set_shared_input(keyframe->xbuf);
248         char name[BCTEXTLEN];  to_string(name, plugin_title());
249
250         while( !input.read_tag() ) {
251                 if( !input.tag.title_is(name) ) continue;
252                 for( int i=0; i<config.size(); ++i ) {
253                         PluginLV2Client_Opt *opt = config[i];
254                         float value = input.tag.get_property(opt->get_symbol(), 0.);
255                         opt->set_value(value);
256                 }
257         }
258 }
259
260 void PluginLV2Client::load_buffer(int samples, Samples **input, int ich)
261 {
262         for( int i=0; i<nb_inputs; ++i ) {
263                 int k = i < ich ? i : 0;
264                 double *inp = input[k]->get_data();
265                 float *ip = in_buffers[i];
266                 for( int j=samples; --j>=0; *ip++=*inp++ );
267         }
268         for( int i=0; i<nb_outputs; ++i )
269                 bzero(out_buffers[i], samples*sizeof(float));
270 }
271
272 int PluginLV2Client::unload_buffer(int samples, Samples **output, int och)
273 {
274         if( nb_outputs < och ) och = nb_outputs;
275         for( int i=0; i<och; ++i ) {
276                 double *outp = output[i]->get_data();
277                 float *op = out_buffers[i];
278                 for( int j=samples; --j>=0; *outp++=*op++ );
279         }
280         return samples;
281 }
282
283 void PluginLV2Client::process_buffer(int size)
284 {
285         PluginLV2ParentUI *ui = get_ui();
286         if( !ui ) return;
287         shm_bfr->done = 0;
288         ui->send_child(LV2_SHMID, &shmid, sizeof(shmid));
289 // timeout 10*duration, min 2sec, max 10sec
290         double sample_rate = get_project_samplerate();
291         double t = !sample_rate ? 2. : 10. * size / sample_rate;
292         bclamp(t, 2., 10.);
293         ui->output_bfr->timed_lock(t*1e6, "PluginLV2Client::process_buffer");
294         if( !shm_bfr->done )
295                 eprintf("timeout: %s",server->title);
296 }
297
298 int PluginLV2Client::process_realtime(int64_t size,
299         Samples **input_ptr, Samples **output_ptr, int chs)
300 {
301         int64_t pos = get_source_position();
302         int64_t end = pos + size;
303         int64_t samples = 0;
304         while( pos < end ) {
305                 KeyFrame *prev_keyframe = get_prev_keyframe(pos);
306                 if( !prev_keyframe->is_default ) {
307                         read_data(prev_keyframe);
308                         update_lv2(LV2_CONFIG);
309                 }
310                 KeyFrame *next_keyframe = get_next_keyframe(pos);
311                 int64_t next_pos = next_keyframe->position;
312                 if( pos >= next_pos || next_pos > end )
313                         next_pos = end;
314                 int64_t len = next_pos - pos;
315                 if( len > block_length )
316                         len = block_length;
317                 if( pos + len > end )
318                         len = end - pos;
319                 init_buffer(len);
320                 for( int i=chs; --i>=0; ) {
321                         input_ptr[i]->set_offset(samples);
322                         output_ptr[i]->set_offset(samples);
323                 }
324                 load_buffer(len, input_ptr, chs);
325                 process_buffer(len);
326                 unload_buffer(len, output_ptr, chs);
327                 samples += len;
328                 pos += len;
329         }
330         for( int i=chs; --i>=0; )
331                 output_ptr[i]->set_offset(0);
332         return samples;
333 }
334
335 int PluginLV2Client::process_realtime(int64_t size,
336         Samples **input_ptr, Samples **output_ptr)
337 {
338         return process_realtime(size, input_ptr, output_ptr,
339                 PluginClient::total_out_buffers);
340 }
341
342 int PluginLV2Client::process_realtime(int64_t size,
343         Samples *input_ptr, Samples *output_ptr)
344 {
345         return process_realtime(size, &input_ptr, &output_ptr, 1);
346 }
347
348 PluginLV2BlackList::PluginLV2BlackList(const char *path)
349 {
350         set_array_delete();
351         char lv2_blacklist_path[BCTEXTLEN];
352         sprintf(lv2_blacklist_path, "%s/%s", File::get_cindat_path(), path);
353         FILE *bfp = fopen(lv2_blacklist_path, "r");
354         if( !bfp ) return;
355         while( fgets(lv2_blacklist_path, sizeof(lv2_blacklist_path), bfp) ) {
356                 if( lv2_blacklist_path[0] == '#' ) continue;
357                 int len = strlen(lv2_blacklist_path);
358                 if( len > 0 && lv2_blacklist_path[len-1] == '\n' )
359                         lv2_blacklist_path[len-1] = 0;
360                 if( !lv2_blacklist_path[0] ) continue;
361                 append(cstrdup(lv2_blacklist_path));
362         }
363         fclose(bfp);
364 }
365
366 PluginLV2BlackList::~PluginLV2BlackList()
367 {
368         remove_all_objects();
369 }
370
371 int PluginLV2BlackList::is_badboy(const char *uri)
372 {
373         FileSystem fs;
374         for( int i=size(); --i>=0; )
375                 if( !fs.test_filter(uri, get(i)) ) return 1;
376         return 0;
377 }
378
379 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
380 {
381         return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
382 }
383
384 PluginClient *PluginServer::new_lv2_plugin()
385 {
386         PluginLV2Client *client = new PluginLV2Client(this);
387         if( client->load_lv2(path, client->title) ) { delete client;  return client = 0; }
388         client->init_lv2();
389         return client;
390 }
391
392 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
393 {
394         printf("init lv2 index:\n");
395         PluginLV2BlackList blacklist("lv2_blacklist.txt");
396
397         LilvWorld *world = lilv_world_new();
398         lilv_world_load_all(world);
399         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
400
401         LILV_FOREACH(plugins, i, all_plugins) {
402                 const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
403                 const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
404                 if( blacklist.is_badboy(uri) ) continue;
405 printf("LOAD: %s\n", uri);
406                 PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
407                 int result = server.open_plugin(1, preferences, 0, 0);
408                 if( !result ) {
409                         server.write_table(fp, uri, PLUGIN_LV2_ID, 0);
410                         server.close_plugin();
411                 }
412         }
413
414         lilv_world_free(world);
415         return 0;
416 }
417
418 PluginLV2ParentUI::PluginLV2ParentUI(Plugin *plugin)
419 {
420         this->position = plugin->startproject;
421         PluginSet *plugin_set = plugin->plugin_set;
422         if( plugin_set ) {
423                 this->set_no = plugin_set->get_number();
424                 this->track_no = plugin_set->track->number_of();
425         }
426         else
427                 this->track_no = this->set_no = -1;
428
429         output_bfr = new Condition(0, "PluginLV2ParentUI::output_bfr", 1);
430         client = 0;
431         gui = 0;
432         hidden = -1;
433 }
434
435 PluginLV2ParentUI::~PluginLV2ParentUI()
436 {
437         stop();
438         delete output_bfr;
439 }
440
441 void PluginLV2ParentUI::start_parent(PluginLV2Client *client)
442 {
443         start();
444         const char *path = client->server->path;
445         int len = sizeof(open_bfr_t) + strlen(path);
446         char bfr[len];  memset(bfr, 0, len);
447         open_bfr_t *open_bfr = (open_bfr_t *)bfr;
448         open_bfr->sample_rate = client->get_project_samplerate();
449         PlaybackConfig *playback_config = client->server->edl->session->playback_config;
450         open_bfr->bfrsz = playback_config->aconfig->fragment_size;
451         strcpy(open_bfr->path, path);
452         send_child(LV2_OPEN, open_bfr, len);
453         PluginLV2ClientConfig &conf = client->config;
454         send_child(LV2_LOAD, conf.ctls, conf.nb_ports*sizeof(float));
455 }
456
457 int PluginLV2ParentUI::handle_parent()
458 {
459         int result = 1;
460
461         switch( parent_token ) {
462         case LV2_UPDATE: {
463                 if( !gui ) break;
464                 gui->lv2_update((float *)parent_data);
465                 break; }
466         case LV2_HIDE: {
467                 hidden = 1;
468                 break; }
469         case LV2_SHOW: {
470                 if( hidden < 0 )
471                         gui->lv2_ui_enable();
472                 hidden = 0;
473                 break; }
474         case LV2_SHMID: {
475                 output_bfr->unlock();
476                 break; }
477         case EXIT_CODE: {
478                 hidden = 1;
479                 output_bfr->unlock();
480                 result = -1;
481                 break; }
482         default:
483                 result = 0;
484                 break;
485         }
486
487         return result;
488 }
489
490 int PluginLV2ParentUI::show()
491 {
492         if( !hidden ) return 1;
493         send_child(LV2_SHOW, 0, 0);
494         return 0;
495 }
496
497 int PluginLV2ParentUI::hide()
498 {
499         if( hidden ) return 1;
500         send_child(LV2_HIDE, 0, 0);
501         return 0;
502 }
503
504 ForkChild *PluginLV2ParentUI::new_fork()
505 {
506         return new PluginLV2ChildUI();
507 }
508
509 // stub in parent
510 int PluginLV2ChildUI::child_iteration(int64_t usec) { return -1; }
511 int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes) { return -1; }
512
513 #else
514 #include "mwindow.h"
515 #include "pluginserver.h"
516
517 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name) { return 0; }
518 PluginClient *PluginServer::new_lv2_plugin() { return 0; }
519 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp) { return 0; }
520
521 #endif /* HAVE_LV2 */