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