1 /*
2  * Copyright (C) 2020 Hermann meyer
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  * --------------------------------------------------------------------------
18  */
19 
20 
21 #include <stdio.h>
22 #include <libgen.h>
23 
24 /*---------------------------------------------------------------------
25 -----------------------------------------------------------------------
26                 define PortIndex and plugin uri
27 -----------------------------------------------------------------------
28 ----------------------------------------------------------------------*/
29 
30 #include "fluida.h"
31 
32 /*---------------------------------------------------------------------
33 -----------------------------------------------------------------------
34                 define controller numbers
35 -----------------------------------------------------------------------
36 ----------------------------------------------------------------------*/
37 
38 #define CONTROLS 12
39 
40 /*---------------------------------------------------------------------
41 -----------------------------------------------------------------------
42                 include the LV2 interface
43 -----------------------------------------------------------------------
44 ----------------------------------------------------------------------*/
45 
46 #include "lv2_plugin.cc"
47 #include "xfile-dialog.h"
48 #include "xmessage-dialog.h"
49 
50 /*---------------------------------------------------------------------
51 -----------------------------------------------------------------------
52                 define the plugin settings
53 -----------------------------------------------------------------------
54 ----------------------------------------------------------------------*/
55 
56 #define OBJ_BUF_SIZE 1024
57 #define _(S) S
58 
59 
60 typedef struct {
61     LV2_Atom_Forge forge;
62 
63     FluidaLV2URIs   uris;
64 
65     Widget_t *dia;
66     Widget_t *combo;
67     Widget_t *control[CONTROLS];
68     char *filename;
69     char *dir_name;
70     char **instruments;
71     size_t n_elem;
72     uint8_t obj_buf[OBJ_BUF_SIZE];
73 
74 } X11_UI_Private_t;
75 
76 
77 //static
draw_ui(void * w_,void * user_data)78 void draw_ui(void *w_, void* user_data) {
79     Widget_t *w = (Widget_t*)w_;
80     X11_UI *ui = (X11_UI*) w->parent_struct;
81     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
82     set_pattern(w,&w->app->color_scheme->selected,&w->app->color_scheme->normal,BACKGROUND_);
83     cairo_paint (w->crb);
84 
85     use_text_color_scheme(w, NORMAL_);
86     cairo_set_font_size (w->crb, w->app->big_font/w->scale.ascale);
87 
88     widget_set_scale(w);
89     cairo_move_to (w->crb, 70, 50 );
90     cairo_show_text(w->crb, ps->filename);
91     widget_reset_scale(w);
92     cairo_new_path (w->crb);
93 }
94 
plugin_value_changed(X11_UI * ui,Widget_t * w,PortIndex index)95 void plugin_value_changed(X11_UI *ui, Widget_t *w, PortIndex index) {
96     // do special stuff when needed
97 }
98 
plugin_set_window_size(int * w,int * h,const char * plugin_uri)99 void plugin_set_window_size(int *w,int *h,const char * plugin_uri) {
100     (*w) = 570; //set initial widht of main window
101     (*h) = 280; //set initial heigth of main window
102 }
103 
plugin_set_name()104 const char* plugin_set_name() {
105     return "Fluida"; //set plugin name to display on UI
106 }
107 
synth_load_response(void * w_,void * user_data)108 static void synth_load_response(void *w_, void* user_data) {
109     Widget_t *w = (Widget_t*)w_;
110     Widget_t *p = (Widget_t*)w->parent;
111     X11_UI *ui = (X11_UI*) p->parent_struct;
112     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
113     if(user_data !=NULL) {
114 
115         if( access(*(const char**)user_data, F_OK ) == -1 ) {
116             Widget_t *dia = open_message_dialog(ui->win, ERROR_BOX, *(const char**)user_data,
117                                                 _("Couldn't access file, sorry"),NULL);
118             XSetTransientForHint(ui->win->app->dpy, dia->widget, ui->win->widget);
119             return;
120         }
121         if (strstr(*(const char**)user_data, ".sfz")) {
122             Widget_t *dia = open_message_dialog(ui->win, ERROR_BOX, *(const char**)user_data,
123             _("Couldn't load file in sfz format, sorry"),NULL);
124             XSetTransientForHint(ui->win->app->dpy, dia->widget, ui->win->widget);
125             return;
126         }
127         free(ps->filename);
128         ps->filename = NULL;
129         ps->filename = strdup(*(const char**)user_data);
130         free(ps->dir_name);
131         ps->dir_name = NULL;
132         ps->dir_name = strdup(dirname(*(char**)user_data));
133         FileButton *filebutton = (FileButton*)ps->dia->parent_struct;
134         filebutton->path = ps->dir_name;
135         lv2_atom_forge_set_buffer(&ps->forge, ps->obj_buf, sizeof(ps->obj_buf));
136 
137         LV2_Atom* msg = (LV2_Atom*)write_set_file(&ps->forge, &ps->uris, ps->filename);
138 
139         ui->write_function(ui->controller, MIDI_IN, lv2_atom_total_size(msg),
140                            ps->uris.atom_eventTransfer, msg);
141         free(ps->filename);
142         ps->filename = NULL;
143         ps->filename = strdup("None");;
144     }
145 }
146 
rebuild_instrument_list(X11_UI * ui)147 void rebuild_instrument_list(X11_UI *ui) {
148     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
149     if(ps->combo) {
150         combobox_delete_entrys(ps->combo);
151     }
152     for (int i = 0; i < (int)ps->n_elem; i++) {
153         combobox_add_entry(ps->combo, ps->instruments[i]);
154     }
155     if (!(int)ps->n_elem) {
156         combobox_add_entry(ps->combo,"None");
157     }
158 
159     combobox_set_active_entry(ps->combo, 0);
160     expose_widget(ps->combo);
161 }
162 
instrument_callback(void * w_,void * user_data)163 static void instrument_callback(void *w_, void* user_data) {
164     Widget_t *w = (Widget_t*)w_;
165     Widget_t *p = (Widget_t*)w->parent;
166     X11_UI *ui = (X11_UI*) p->parent_struct;
167     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
168     const int i = (const int)adj_get_value(w->adj);
169     lv2_atom_forge_set_buffer(&ps->forge, ps->obj_buf, sizeof(ps->obj_buf));
170 
171     LV2_Atom* msg = write_set_instrument(&ps->forge, &ps->uris, i);
172 
173     ui->write_function(ui->controller, MIDI_IN, lv2_atom_total_size(msg),
174                        ps->uris.atom_eventTransfer, msg);
175 
176 }
177 
dummy_callback(void * w_,void * user_data)178 static void dummy_callback(void *w_, void* user_data) {
179 
180 }
181 
fetch_next_sflist(X11_UI * ui)182 void fetch_next_sflist(X11_UI *ui) {
183     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
184     const int i = 1;
185     lv2_atom_forge_set_buffer(&ps->forge, ps->obj_buf, sizeof(ps->obj_buf));
186     LV2_Atom* msg = write_get_sflist_next(&ps->forge, &ps->uris, i);
187 
188     ui->write_function(ui->controller, MIDI_IN, lv2_atom_total_size(msg),
189                        ps->uris.atom_eventTransfer, msg);
190 }
191 
notify_dsp(X11_UI * ui)192 void notify_dsp(X11_UI *ui) {
193     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
194     const int i = 1;
195     lv2_atom_forge_set_buffer(&ps->forge, ps->obj_buf, sizeof(ps->obj_buf));
196     LV2_Atom* msg = write_set_gui(&ps->forge, &ps->uris, i);
197 
198     ui->write_function(ui->controller, MIDI_IN, lv2_atom_total_size(msg),
199                        ps->uris.atom_eventTransfer, msg);
200 }
201 
first_loop(X11_UI * ui)202 void first_loop(X11_UI *ui) {
203     notify_dsp(ui);
204 }
205 
set_active_instrument(X11_UI * ui,int a)206 void set_active_instrument(X11_UI *ui, int a) {
207     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
208     ps->combo->func.value_changed_callback = dummy_callback;
209     combobox_set_active_entry(ps->combo, a);
210     ps->combo->func.value_changed_callback = instrument_callback;
211 }
212 
dnd_load_response(void * w_,void * user_data)213 static void dnd_load_response(void *w_, void* user_data) {
214     if(user_data !=NULL) {
215         Widget_t *w = (Widget_t*)w_;
216         Widget_t *c = w->childlist->childs[0];
217         char* dndfile = NULL;
218         bool sf2_done = false;
219         dndfile = strtok(*(char**)user_data, "\r\n");
220         while (dndfile != NULL) {
221             if (strstr(dndfile, ".sf") && !sf2_done) {
222                 synth_load_response((void*)c, (void*)&dndfile);
223                 sf2_done = true;
224             }
225             dndfile = strtok(NULL, "\r\n");
226         }
227     }
228 }
229 
send_controller_message(Widget_t * w,const LV2_URID control)230 void send_controller_message(Widget_t *w, const LV2_URID control) {
231     Widget_t *p = (Widget_t*)w->parent;
232     X11_UI *ui = (X11_UI*) p->parent_struct;
233     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
234     const FluidaLV2URIs* uris = &ps->uris;
235     const float value = adj_get_value(w->adj);
236     uint8_t obj_buf[OBJ_BUF_SIZE];
237     lv2_atom_forge_set_buffer(&ps->forge, obj_buf, OBJ_BUF_SIZE);
238 
239     LV2_Atom_Forge_Frame frame;
240     LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&ps->forge, &frame, 0, uris->patch_Set);
241     lv2_atom_forge_key(&ps->forge, uris->patch_property);
242     lv2_atom_forge_urid(&ps->forge, control);
243     lv2_atom_forge_key(&ps->forge, uris->patch_value);
244     switch(w->data) {
245         case 2:
246             lv2_atom_forge_int(&ps->forge, (int)value);
247         break;
248         case 3:
249             lv2_atom_forge_bool(&ps->forge, (int)value);
250         break;
251         default:
252             lv2_atom_forge_float(&ps->forge, value);
253         break;
254     }
255     lv2_atom_forge_pop(&ps->forge, &frame);
256 
257     ui->write_function(ui->controller, MIDI_IN, lv2_atom_total_size(msg),
258                        ps->uris.atom_eventTransfer, msg);
259 }
260 
261 // static
controller_callback(void * w_,void * user_data)262 void controller_callback(void *w_, void* user_data) {
263     Widget_t *w = (Widget_t*)w_;
264     const LV2_URID urid = *(const LV2_URID*)w->parent_struct;
265     send_controller_message(w, urid);
266 }
267 
268 // static
set_on_off_label(void * w_,void * user_data)269 void set_on_off_label(void *w_, void* user_data) {
270     Widget_t *w = (Widget_t*)w_;
271     const int value = (const int)adj_get_value(w->adj);
272     if (value) {
273         w->label = _("Off");
274     } else {
275         w->label = _("On");
276     }
277     expose_widget(w);
278 }
279 
set_ctl_val_from_host(Widget_t * w,float value)280 void set_ctl_val_from_host(Widget_t *w, float value) {
281     xevfunc store = w->func.value_changed_callback;
282     w->func.value_changed_callback = dummy_callback;
283     adj_set_value(w->adj, value);
284     w->func.value_changed_callback = *(*store);
285 }
286 
plugin_create_controller_widgets(X11_UI * ui,const char * plugin_uri)287 void plugin_create_controller_widgets(X11_UI *ui, const char * plugin_uri) {
288     X11_UI_Private_t *ps =(X11_UI_Private_t*)malloc(sizeof(X11_UI_Private_t));;
289     ui->private_ptr = (void*)ps;
290     ps->filename = strdup("None");
291     ps->dir_name = NULL;
292     ps->instruments = NULL;
293     ps->n_elem = 0;
294 
295     map_fluidalv2_uris(ui->map, &ps->uris);
296     lv2_atom_forge_init(&ps->forge, ui->map);
297     widget_set_dnd_aware(ui->win);
298 
299     ui->win->func.expose_callback = draw_ui;
300     ui->win->func.dnd_notify_callback = dnd_load_response;
301     const FluidaLV2URIs* uris = &ps->uris;
302 
303     ps->dia = add_file_button(ui->win, 20, 20, 40, 40, ps->dir_name, ".sf");
304     ps->dia->func.user_callback = synth_load_response;
305 
306     ps->combo = add_combobox(ui->win, _("Instruments"), 20, 70, 260, 30);
307     ps->combo->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
308     ps->combo->parent_struct = (void*)uris;
309     combobox_add_entry(ps->combo,"None");
310     ps->combo->childlist->childs[0]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
311     ps->combo->func.value_changed_callback = instrument_callback;
312 
313     // reverb
314     ps->control[0] = add_toggle_button(ui->win, _("On"), 20,  230, 60, 30);
315     ps->control[0]->parent_struct = (void*)&uris->fluida_rev_on;
316     ps->control[0]->data = 3;
317     ps->control[0]->func.adj_callback = set_on_off_label;
318     ps->control[0]->func.value_changed_callback = controller_callback;
319 
320     Widget_t *tmp = add_label(ui->win,_("Reverb"),15,110,80,20);
321     tmp->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
322 
323     ps->control[1] = add_knob(ui->win, _("Roomsize"), 20, 140, 65, 85);
324     ps->control[1]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
325     ps->control[1]->parent_struct = (void*)&uris->fluida_rev_size;
326     ps->control[1]->data = 1;
327     set_adjustment(ps->control[1]->adj, 0.6, 0.6, 0.0, 1.2, 0.01, CL_CONTINUOS);
328     ps->control[1]->func.value_changed_callback = controller_callback;
329 
330     ps->control[2] = add_knob(ui->win, _("Damp"), 80, 140, 65, 85);
331     ps->control[2]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
332     ps->control[2]->parent_struct = (void*)&uris->fluida_rev_damp;
333     ps->control[2]->data = 1;
334     set_adjustment(ps->control[2]->adj, 0.4, 0.4, 0.0, 1.0, 0.01, CL_CONTINUOS);
335     ps->control[2]->func.value_changed_callback = controller_callback;
336 
337     ps->control[3] = add_knob(ui->win, _("Width"), 145, 140, 65, 85);
338     ps->control[3]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
339     ps->control[3]->parent_struct = (void*)&uris->fluida_rev_width;
340     ps->control[3]->data = 1;
341     set_adjustment(ps->control[3]->adj, 10.0, 10.0, 0.0, 100.0, 0.5, CL_CONTINUOS);
342     ps->control[3]->func.value_changed_callback = controller_callback;
343 
344     ps->control[4] = add_knob(ui->win, _("Level"), 210, 140, 65, 85);
345     ps->control[4]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
346     ps->control[4]->parent_struct = (void*)&uris->fluida_rev_lev;
347     ps->control[4]->data = 1;
348     set_adjustment(ps->control[4]->adj, 0.7, 0.7, 0.0, 1.0, 0.01, CL_CONTINUOS);
349     ps->control[4]->func.value_changed_callback = controller_callback;
350 
351     // chorus
352     tmp = add_label(ui->win,_("Chorus"),290,110,80,20);
353     tmp->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
354 
355     ps->control[5] = add_toggle_button(ui->win, _("On"), 290,  230, 60, 30);
356     ps->control[5]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
357     ps->control[5]->parent_struct = (void*)&uris->fluida_chorus_on;
358     ps->control[5]->data = 3;
359     ps->control[5]->func.adj_callback = set_on_off_label;
360     ps->control[5]->func.value_changed_callback = controller_callback;
361 
362     ps->control[6] = add_knob(ui->win, _("voices"), 290, 140, 65, 85);
363     ps->control[6]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
364     ps->control[6]->parent_struct = (void*)&uris->fluida_chorus_voices;
365     ps->control[6]->data = 2;
366     set_adjustment(ps->control[6]->adj, 3.0, 3.0, 0.0, 99.0, 1.0, CL_CONTINUOS);
367     ps->control[6]->func.value_changed_callback = controller_callback;
368 
369     ps->control[7] = add_knob(ui->win, _("Level"), 355, 140, 65, 85);
370     ps->control[7]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
371     ps->control[7]->parent_struct = (void*)&uris->fluida_chorus_lev;
372     ps->control[7]->data = 1;
373     set_adjustment(ps->control[7]->adj, 3.0, 3.0, 0.0, 10.0, 0.1, CL_CONTINUOS);
374     ps->control[7]->func.value_changed_callback = controller_callback;
375 
376     ps->control[8] = add_knob(ui->win, _("Speed"), 420, 140, 65, 85);
377     ps->control[8]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
378     ps->control[8]->parent_struct = (void*)&uris->fluida_chorus_speed;
379     ps->control[8]->data = 1;
380     set_adjustment(ps->control[8]->adj, 0.3, 0.3, 0.1, 5.0, 0.05, CL_CONTINUOS);
381     ps->control[8]->func.value_changed_callback = controller_callback;
382 
383     ps->control[9] = add_knob(ui->win, _("Depth"), 485, 140, 65, 85);
384     ps->control[9]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
385     ps->control[9]->parent_struct = (void*)&uris->fluida_chorus_depth;
386     ps->control[9]->data = 1;
387     set_adjustment(ps->control[9]->adj, 3.0, 3.0, 0.0, 21.0, 0.1, CL_CONTINUOS);
388     ps->control[9]->func.value_changed_callback = controller_callback;
389 
390     ps->control[10] = add_combobox(ui->win, _("MODE"), 420, 230, 100, 30);
391     ps->control[10]->parent_struct = (void*)&uris->fluida_chorus_type;
392     ps->control[9]->data = 2;
393     combobox_add_entry(ps->control[10], _("SINE"));
394     combobox_add_entry(ps->control[10], _("TRIANGLE"));
395     combobox_set_active_entry(ps->control[10], 0);
396     ps->control[10]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
397     ps->control[10]->func.value_changed_callback = controller_callback;
398 
399     ps->control[11] = add_hslider(ui->win, _("Channel Pressure"), 300, 70, 260, 30);
400     set_adjustment(ps->control[11]->adj, 0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS);
401     ps->control[11]->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
402     ps->control[11]->parent_struct = (void*)&uris->fluida_channel_pressure;
403     ps->control[11]->data = 2;
404     ps->control[11]->func.value_changed_callback = controller_callback;
405 
406 }
407 
plugin_cleanup(X11_UI * ui)408 void plugin_cleanup(X11_UI *ui) {
409     // clean up used sources when needed
410     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
411     free(ps->filename);
412     free(ps->dir_name);
413     unsigned int j = 0;
414     for(; j<ps->n_elem;j++) {
415         free(ps->instruments[j]);
416         ps->instruments[j] = NULL;
417     }
418     free(ps->instruments);
419     free(ps);
420     ui->private_ptr = NULL;
421 }
422 
get_widget_from_urid(X11_UI_Private_t * ps,const LV2_URID urid)423 Widget_t *get_widget_from_urid(X11_UI_Private_t *ps, const LV2_URID urid) {
424     int i = 0;
425     for(; i<CONTROLS; i++) {
426         if (*(const LV2_URID*)ps->control[i]->parent_struct == urid) {
427             return ps->control[i];
428         }
429     }
430     return NULL;
431 }
432 
plugin_port_event(LV2UI_Handle handle,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)433 void plugin_port_event(LV2UI_Handle handle, uint32_t port_index,
434                        uint32_t buffer_size, uint32_t format,
435                        const void * buffer) {
436     X11_UI* ui = (X11_UI*)handle;
437     X11_UI_Private_t *ps = (X11_UI_Private_t*)ui->private_ptr;
438     const FluidaLV2URIs* uris = &ps->uris;
439 
440     if (format == ps->uris.atom_eventTransfer) {
441         const LV2_Atom* atom = (LV2_Atom*)buffer;
442         if (atom->type == ps->uris.atom_Object) {
443             const LV2_Atom_Object* obj      = (LV2_Atom_Object*)atom;
444             if (obj->body.otype == uris->patch_Set) {
445                 const LV2_Atom*  file_uri = read_set_file(uris, obj);
446                 if (file_uri) {
447                     const char* uri = (const char*)LV2_ATOM_BODY(file_uri);
448                     if (strlen(uri)) {
449                         if (strcmp(uri, (const char*)ps->filename) !=0) {
450                             free(ps->filename);
451                             ps->filename = NULL;
452                             ps->filename = strdup(uri);
453                             free(ps->dir_name);
454                             ps->dir_name = NULL;
455                             ps->dir_name = strdup(dirname((char*)uri));
456                             FileButton *filebutton = (FileButton*)ps->dia->parent_struct;
457                             filebutton->path = ps->dir_name;
458                             expose_widget(ui->win);
459                         }
460                     }
461                 } else {
462                     const LV2_Atom* value = NULL;
463                     const LV2_Atom* property = NULL;
464                     lv2_atom_object_get(obj, uris->patch_value, &value,
465                                     uris->patch_property, &property, 0);
466                     if (value == NULL) return;
467                     if (property == NULL) return;
468                     Widget_t *w = get_widget_from_urid(ps,((LV2_Atom_URID*)property)->body);
469                     if (w) {
470                         if (value->type == uris->atom_Float ) {
471                             float* val = (float*)LV2_ATOM_BODY(value);
472                             set_ctl_val_from_host(w, (*val));
473                         } else if (value->type == uris->atom_Int ) {
474                             int* val = (int*)LV2_ATOM_BODY(value);
475                             set_ctl_val_from_host(w, (float)(*val));
476                         }else if (value->type == uris->atom_Bool ) {
477                             int* val = (int*)LV2_ATOM_BODY(value);
478                             set_ctl_val_from_host(w, (float)(*val));
479                         }
480                     }
481                 }
482             } else if (obj->body.otype == uris->fluida_sflist_start) {
483                 int i = 0;
484                 unsigned int j = 0;
485                 for(; j<ps->n_elem;j++) {
486                     free(ps->instruments[j]);
487                     ps->instruments[j] = NULL;
488                 }
489                 free(ps->instruments);
490                 ps->instruments = NULL;
491                 LV2_ATOM_OBJECT_FOREACH(obj, ob) {
492                     if (ob->key == uris->atom_String) {
493                         ps->instruments = (char **)realloc(ps->instruments, (i+1) * sizeof(char *));
494                         if (asprintf(&ps->instruments[i],(char*)LV2_ATOM_BODY(&ob->value))) {
495                             i++;
496                         }
497                     }
498 
499                 }
500                 ps->n_elem = i;
501                 fetch_next_sflist(ui);
502             } else if (obj->body.otype == uris->fluida_sflist_next) {
503                 int i = (int)ps->n_elem;
504                 LV2_ATOM_OBJECT_FOREACH(obj, ob) {
505                     if (ob->key == uris->atom_String) {
506                         ps->instruments = (char **)realloc(ps->instruments, (i+1) * sizeof(char *));
507                         if (asprintf(&ps->instruments[i],(char*)LV2_ATOM_BODY(&ob->value))) {
508                             i++;
509                         }
510                     }
511 
512                 }
513                 ps->n_elem = i;
514                 fetch_next_sflist(ui);
515             } else if (obj->body.otype == uris->fluida_sflist_end) {
516                 LV2_Atom* data = NULL;
517                 lv2_atom_object_get(obj,uris->atom_Int, &data, NULL);
518                 const int value = ((LV2_Atom_Int*)data)->body;
519                 if (value) ps->n_elem = value;
520                 rebuild_instrument_list(ui);
521             } else if (obj->body.otype == uris->fluida_instrument) {
522                 const LV2_Atom*  value = read_set_instrument(uris, obj);
523                 if (value) {
524                     const int* uri = (int*)LV2_ATOM_BODY(value);
525                     set_active_instrument(ui, (*uri)) ;
526                 }
527             }
528         }
529     }
530 }
531 
532