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