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