1 /*
2 * Copyright (c) 2017 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <math.h>
21
22 #include <espressivo.h>
23 #include <props.h>
24
25 #include <osc.lv2/osc.h>
26 #include <osc.lv2/util.h>
27 #include <osc.lv2/endian.h>
28
29 #include <canvas.lv2/forge.h>
30 #include <canvas.lv2/forge.h>
31
32 #define MAX_GRAPH 0x20000 //FIXME actually measure this
33 #define MAX_NPROPS 2
34
35 typedef struct _targetI_t targetI_t;
36 typedef struct _plugstate_t plugstate_t;
37 typedef struct _plughandle_t plughandle_t;
38
39 struct _targetI_t {
40 xpress_state_t state;
41 };
42
43 struct _plugstate_t {
44 float aspect_ratio;
45 uint8_t graph [MAX_GRAPH];
46 };
47
48 struct _plughandle_t {
49 LV2_URID_Map *map;
50 LV2_Atom_Forge forge;
51 LV2_Atom_Forge_Ref ref;
52
53 LV2_Log_Log *log;
54 LV2_Log_Logger logger;
55
56 const LV2_Atom_Sequence *event_in;
57 LV2_Atom_Sequence *event_out;
58
59 LV2_OSC_URID osc_urid;
60 LV2_Canvas_URID canvas_urid;
61
62 unsigned n;
63 bool needs_sync;
64
65 plugstate_t state;
66 plugstate_t stash;
67
68 uint32_t overflow;
69 uint32_t overflowsec;
70 uint32_t counter;
71
72 PROPS_T(props, MAX_NPROPS);
73 XPRESS_T(xpressI, MAX_NVOICES);
74 targetI_t targetI [MAX_NVOICES];
75 };
76
77 static const props_def_t defs [MAX_NPROPS] = {
78 {
79 .access = LV2_PATCH__readable,
80 .property = CANVAS__graph,
81 .offset = offsetof(plugstate_t, graph),
82 .type = LV2_ATOM__Tuple,
83 .max_size = MAX_GRAPH
84 },
85 {
86 .property = CANVAS__aspectRatio,
87 .offset = offsetof(plugstate_t, aspect_ratio),
88 .type = LV2_ATOM__Float
89 }
90 };
91
92 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)93 _add(void *data, int64_t frames, const xpress_state_t *state,
94 xpress_uuid_t uuid, void *target)
95 {
96 plughandle_t *handle = data;
97 targetI_t *src = target;
98
99 src->state = *state;
100
101 handle->needs_sync = true;
102 }
103
104 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)105 _set(void *data, int64_t frames, const xpress_state_t *state,
106 xpress_uuid_t uuid, void *target)
107 {
108 plughandle_t *handle = data;
109 targetI_t *src = target;
110
111 src->state = *state;
112
113 handle->needs_sync = true;
114 }
115
116 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)117 _del(void *data, int64_t frames,
118 xpress_uuid_t uuid, void *target)
119 {
120 plughandle_t *handle = data;
121
122 handle->needs_sync = true;
123 }
124
125 static const xpress_iface_t ifaceI = {
126 .size = sizeof(targetI_t),
127
128 .add = _add,
129 .set = _set,
130 .del = _del
131 };
132
133 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)134 instantiate(const LV2_Descriptor* descriptor, double rate,
135 const char *bundle_path, const LV2_Feature *const *features)
136 {
137 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
138 if(!handle)
139 return NULL;
140
141 xpress_map_t *voice_map = NULL;
142
143 for(unsigned i=0; features[i]; i++)
144 {
145 if(!strcmp(features[i]->URI, LV2_URID__map))
146 handle->map = features[i]->data;
147 else if(!strcmp(features[i]->URI, LV2_LOG__log))
148 handle->log = features[i]->data;
149 }
150
151 if(!handle->map)
152 {
153 fprintf(stderr,
154 "%s: Host does not support urid:map\n", descriptor->URI);
155 free(handle);
156 return NULL;
157 }
158
159 if(handle->log)
160 lv2_log_logger_init(&handle->logger, handle->map, handle->log);
161
162 const float update_rate = 120.f; //FIXME read from options
163 handle->overflow = rate / update_rate;
164 handle->overflowsec = rate;
165
166 lv2_atom_forge_init(&handle->forge, handle->map);
167 lv2_osc_urid_init(&handle->osc_urid, handle->map);
168 lv2_canvas_urid_init(&handle->canvas_urid, handle->map);
169
170 if( !xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
171 XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle) )
172 {
173 free(handle);
174 return NULL;
175 }
176
177 if(!props_init(&handle->props, descriptor->URI,
178 defs, MAX_NPROPS, &handle->state, &handle->stash,
179 handle->map, handle))
180 {
181 fprintf(stderr, "failed to initialize property structure\n");
182 free(handle);
183 return NULL;
184 }
185
186 return handle;
187 }
188
189 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)190 connect_port(LV2_Handle instance, uint32_t port, void *data)
191 {
192 plughandle_t *handle = (plughandle_t *)instance;
193
194 switch(port)
195 {
196 case 0:
197 handle->event_in = (const void *)data;
198 break;
199 case 1:
200 handle->event_out = data;
201 break;
202 default:
203 break;
204 }
205 }
206
207 #define NUM_COLS 6
208
209 static const uint32_t cols [NUM_COLS] = {
210 [0] = 0xff0000ff,
211 [1] = 0x00ff00ff,
212 [2] = 0x0000ffff,
213 [3] = 0xffff00ff,
214 [4] = 0xff00ffff,
215 [5] = 0x00ffffff
216 };
217
218 static LV2_Atom_Forge_Ref
_render(plughandle_t * handle,uint32_t frames)219 _render(plughandle_t *handle, uint32_t frames)
220 {
221 LV2_Atom_Forge *forge = &handle->forge;
222 LV2_Canvas_URID *canvas_urid = &handle->canvas_urid;
223 LV2_Atom_Forge_Frame frame [2];
224
225 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);;
226 if(ref)
227 ref = lv2_atom_forge_object(forge, &frame[0], 0, handle->props.urid.patch_set);
228 {
229 if(ref)
230 ref = lv2_atom_forge_key(forge, handle->props.urid.patch_property);
231 if(ref)
232 ref = lv2_atom_forge_urid(forge, canvas_urid->Canvas_graph);
233
234 if(ref)
235 ref = lv2_atom_forge_key(forge, handle->props.urid.patch_value);
236 if(ref)
237 ref = lv2_atom_forge_tuple(forge, &frame[1]);
238 {
239 // draw background
240 if(ref)
241 ref = lv2_canvas_forge_style(forge, canvas_urid, 0x0000003f);
242 if(ref)
243 ref = lv2_canvas_forge_rectangle(forge, canvas_urid, 0.f, 0.f, 1.f, 1.f);
244 if(ref)
245 ref = lv2_canvas_forge_fill(forge, canvas_urid);
246
247 if(ref)
248 ref = lv2_canvas_forge_lineWidth(forge, canvas_urid, 0.005f);
249
250 XPRESS_VOICE_FOREACH(&handle->xpressI, voice)
251 {
252 targetI_t *src = voice->target;
253
254 //FIXME
255 const float x = src->state.pitch;
256 const float vx = src->state.dPitch * 0.5f;
257 const float y = 0.5f;
258 const float r = src->state.pressure * 0.1f;
259 const float vr = -src->state.dPressure * 0.08f;
260 const float a1 = 0.f;
261 const float a2 = 2.f * M_PI;
262 const uint32_t col = cols[voice->uuid % NUM_COLS];
263
264 if(ref)
265 ref = lv2_canvas_forge_style(forge, canvas_urid, col);
266
267 if(ref)
268 ref = lv2_canvas_forge_arc(forge, canvas_urid, x, y, r, a1, a2);
269 if(ref)
270 ref = lv2_canvas_forge_stroke(forge, canvas_urid);
271
272 if(ref)
273 ref = lv2_canvas_forge_moveTo(forge, canvas_urid, x, y);
274 if(ref)
275 ref = lv2_canvas_forge_lineTo(forge, canvas_urid, x + vx, y);
276 if(ref)
277 ref = lv2_canvas_forge_stroke(forge, canvas_urid);
278
279 if(ref)
280 ref = lv2_canvas_forge_moveTo(forge, canvas_urid, x, y);
281 if(ref)
282 ref = lv2_canvas_forge_lineTo(forge, canvas_urid, x, y + vr);
283 if(ref)
284 ref = lv2_canvas_forge_stroke(forge, canvas_urid);
285 }
286
287 }
288 if(ref)
289 lv2_atom_forge_pop(forge, &frame[1]);
290 }
291 if(ref)
292 lv2_atom_forge_pop(forge, &frame[0]);
293
294 return ref;
295 }
296
297 static void
run(LV2_Handle instance,uint32_t nsamples)298 run(LV2_Handle instance, uint32_t nsamples)
299 {
300 plughandle_t *handle = instance;
301
302 const uint32_t capacity = handle->event_out->atom.size;
303 lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
304 LV2_Atom_Forge_Frame frame;
305 handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
306
307 props_idle(&handle->props, &handle->forge, 0, &handle->ref);
308 xpress_pre(&handle->xpressI);
309
310 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
311 {
312 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
313 const int64_t frames = ev->time.frames;
314
315 if(!props_advance(&handle->props, &handle->forge, frames, obj, &handle->ref))
316 {
317 xpress_advance(&handle->xpressI, &handle->forge, frames, obj, &handle->ref);
318 }
319 }
320
321 xpress_post(&handle->xpressI, nsamples-1);
322
323 handle->counter += nsamples;
324
325 if(handle->counter >= handle->overflowsec) // update every sec
326 {
327 if(handle->ref)
328 handle->ref = _render(handle, nsamples-1);
329
330 handle->counter -= handle->overflowsec;
331 }
332 else if(handle->needs_sync && (handle->counter >= handle->overflow) )
333 {
334 if(handle->ref)
335 handle->ref = _render(handle, nsamples-1);
336
337 handle->counter -= handle->overflow;
338 handle->needs_sync = false;
339 }
340
341 if(handle->ref)
342 lv2_atom_forge_pop(&handle->forge, &frame);
343 else
344 {
345 if(handle->log)
346 lv2_log_trace(&handle->logger, "%s: output buffer overflow\n", __func__);
347 lv2_atom_sequence_clear(handle->event_out);
348 }
349 }
350
351 static void
cleanup(LV2_Handle instance)352 cleanup(LV2_Handle instance)
353 {
354 plughandle_t *handle = instance;
355
356 if(handle)
357 {
358 xpress_deinit(&handle->xpressI);
359 free(handle);
360 }
361 }
362
363 static LV2_State_Status
_state_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)364 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
365 LV2_State_Handle state, uint32_t flags,
366 const LV2_Feature *const *features)
367 {
368 plughandle_t *handle = instance;
369
370 return props_save(&handle->props, store, state, flags, features);
371 }
372
373 static LV2_State_Status
_state_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)374 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
375 LV2_State_Handle state, uint32_t flags,
376 const LV2_Feature *const *features)
377 {
378 plughandle_t *handle = instance;
379
380 return props_restore(&handle->props, retrieve, state, flags, features);
381 }
382
383 static const LV2_State_Interface state_iface = {
384 .save = _state_save,
385 .restore = _state_restore
386 };
387
388 static const void*
extension_data(const char * uri)389 extension_data(const char* uri)
390 {
391 if(!strcmp(uri, LV2_STATE__interface))
392 return &state_iface;
393
394 return NULL;
395 }
396
397 const LV2_Descriptor monitor_out = {
398 .URI = ESPRESSIVO_MONITOR_OUT_URI,
399 .instantiate = instantiate,
400 .connect_port = connect_port,
401 .activate = NULL,
402 .run = run,
403 .deactivate = NULL,
404 .cleanup = cleanup,
405 .extension_data = extension_data
406 };
407