1 /*
2 * Copyright (c) 2015 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 #include <osc.lv2/forge.h>
25
26 #define MAX_NPROPS 6
27 #define MAX_STRLEN 128
28
29 typedef struct _targetI_t targetI_t;
30 typedef struct _plugstate_t plugstate_t;
31 typedef struct _plughandle_t plughandle_t;
32
33 struct _targetI_t {
34 xpress_uuid_t uuid;
35 xpress_state_t state;
36 bool dirty;
37 int64_t last;
38 };
39
40 struct _plugstate_t {
41 int32_t device_width;
42 int32_t device_height;
43 char device_name [MAX_STRLEN];
44 int32_t octave;
45 int32_t sensors_per_semitone;
46 float timestamp_offset;
47 };
48
49 struct _plughandle_t {
50 LV2_URID_Map *map;
51 LV2_Atom_Forge forge;
52 LV2_Atom_Forge_Ref ref;
53 LV2_OSC_URID osc_urid;
54 LV2_OSC_Schedule *osc_sched;
55
56 PROPS_T(props, MAX_NPROPS);
57 XPRESS_T(xpressI, MAX_NVOICES);
58 targetI_t targetI [MAX_NVOICES];
59
60 const LV2_Atom_Sequence *event_in;
61 LV2_Atom_Sequence *event_out;
62
63 int32_t dim;
64 int32_t fid;
65 bool dirty;
66 int64_t last;
67 float bot;
68 float ran_1;
69
70 plugstate_t state;
71 plugstate_t stash;
72 };
73
74 static void
_intercept(void * data,int64_t frames,props_impl_t * impl)75 _intercept(void *data, int64_t frames, props_impl_t *impl)
76 {
77 plughandle_t *handle = data;
78
79 int32_t w = handle->state.device_width;
80 const int32_t h = handle->state.device_height;
81 const float oct = handle->state.octave;
82 int32_t sps = handle->state.sensors_per_semitone;
83
84 if(w <= 0)
85 w = 1;
86 if(sps <= 0)
87 sps = 1;
88
89 handle->ran_1 = (float)sps / w;
90 handle->bot = oct*12.f - 0.5 - (w % (6*sps) / (2.f*sps));
91
92 handle->dim = (w << 16) | h;
93 }
94
95 static const props_def_t defs [MAX_NPROPS] = {
96 {
97 .property = ESPRESSIVO_URI"#tuio2_deviceWidth",
98 .offset = offsetof(plugstate_t, device_width),
99 .type = LV2_ATOM__Int,
100 .event_cb = _intercept
101 },
102 {
103 .property = ESPRESSIVO_URI"#tuio2_deviceHeight",
104 .offset = offsetof(plugstate_t, device_height),
105 .type = LV2_ATOM__Int,
106 .event_cb = _intercept
107 },
108 {
109 .property = ESPRESSIVO_URI"#tuio2_octave",
110 .offset = offsetof(plugstate_t, octave),
111 .type = LV2_ATOM__Int,
112 .event_cb = _intercept
113 },
114 {
115 .property = ESPRESSIVO_URI"#tuio2_sensorsPerSemitone",
116 .offset = offsetof(plugstate_t, sensors_per_semitone),
117 .type = LV2_ATOM__Int,
118 .event_cb = _intercept
119 },
120 {
121 .property = ESPRESSIVO_URI"#tuio2_deviceName",
122 .offset = offsetof(plugstate_t, device_name),
123 .type = LV2_ATOM__String,
124 .max_size = MAX_STRLEN
125 },
126 {
127 .property = ESPRESSIVO_URI"#tuio2_timestampOffset",
128 .offset = offsetof(plugstate_t, timestamp_offset),
129 .type = LV2_ATOM__Float,
130 }
131 };
132
133 static inline LV2_Atom_Forge_Ref
_frm(plughandle_t * handle,uint64_t ttag)134 _frm(plughandle_t *handle, uint64_t ttag)
135 {
136 /*
137 /tuio2/frm f_id time dim source
138 /tuio2/frm int32 ttag int32 string
139 */
140
141 return lv2_osc_forge_message_vararg(&handle->forge, &handle->osc_urid,
142 "/tuio2/frm", "itis",
143 ++handle->fid, ttag, handle->dim, handle->state.device_name);
144 }
145
146 static inline LV2_Atom_Forge_Ref
_tok_2d(plughandle_t * handle,xpress_uuid_t uuid,const xpress_state_t * state)147 _tok_2d(plughandle_t *handle, xpress_uuid_t uuid, const xpress_state_t *state)
148 {
149 /*
150 /tuio2/tok s_id tu_id c_id x_pos y_pos angle [x_vel y_vel a_vel m_acc r_acc]
151 /tuio2/tok int32 int32 int32 float float float [float float float float float]
152 */
153
154 const int32_t sid = uuid;
155 const int32_t tuid = 0;
156 const int32_t gid = state->zone;
157 /*FIXME
158 const float macc = sqrtf(state->acceleration[0]*state->acceleration[0]
159 + state->acceleration[1]*state->acceleration[1]);
160 */
161 const float macc = 0.f;
162
163 return lv2_osc_forge_message_vararg(&handle->forge, &handle->osc_urid,
164 "/tuio2/tok", "iiiffffffff",
165 sid, tuid, gid,
166 state->pitch, state->pressure, 0.f,
167 state->dPitch, state->dPressure, 0.f,
168 macc, 0.f);
169 }
170
171 static inline LV2_Atom_Forge_Ref
_alv(plughandle_t * handle)172 _alv(plughandle_t *handle)
173 {
174 /*
175 /tuio2/alv s_id0 ... s_idN
176 /tuio2/alv int32... int32
177 */
178
179 LV2_Atom_Forge_Ref ref;
180 LV2_Atom_Forge_Frame msg_frame [2];
181
182 ref = lv2_osc_forge_message_head(&handle->forge, &handle->osc_urid, msg_frame,
183 "/tuio2/alv");
184
185 XPRESS_VOICE_FOREACH(&handle->xpressI, voice)
186 {
187 targetI_t *src = voice->target;
188
189 if(ref)
190 ref = lv2_osc_forge_int(&handle->forge, &handle->osc_urid, src->uuid);
191 }
192
193 if(ref)
194 lv2_osc_forge_pop(&handle->forge, msg_frame);
195
196 return ref;
197 }
198
199 static inline LV2_Atom_Forge_Ref
_tuio2_2d(plughandle_t * handle,int64_t from,int64_t to)200 _tuio2_2d(plughandle_t *handle, int64_t from, int64_t to)
201 {
202 LV2_Atom_Forge_Frame bndl_frame [2];
203 LV2_Atom_Forge_Ref ref;
204
205 uint64_t ttag0 = 1ULL; // immediate
206 LV2_OSC_Timetag ttag1 = {.integral = 0, .fraction = 1};
207 if(handle->osc_sched)
208 {
209 // get timetag corresponding to frame time
210 ttag0 = handle->osc_sched->frames2osc(handle->osc_sched->handle, from);
211
212 // calculate bundle timetag
213 if(handle->state.timestamp_offset == 0.f)
214 {
215 lv2_osc_timetag_create(&ttag1, ttag0);
216 }
217 else
218 {
219 uint64_t sec = ttag0 >> 32;
220 double frac = (ttag0 & 0xffffffff) * 0x1p-32;
221 frac += handle->state.timestamp_offset * 1e-3;
222 while(frac >= 1.0)
223 {
224 sec += 1;
225 frac -= 1.0;
226 }
227 ttag1.integral = sec;
228 ttag1.fraction = frac * 0x1p32;
229 }
230 }
231
232 ref = lv2_atom_forge_frame_time(&handle->forge, from);
233 if(ref)
234 ref = lv2_osc_forge_bundle_head(&handle->forge, &handle->osc_urid, bndl_frame, &ttag1);
235 if(ref)
236 ref = _frm(handle, ttag0);
237
238 XPRESS_VOICE_FOREACH(&handle->xpressI, voice)
239 {
240 targetI_t *src = voice->target;
241
242 if(src->dirty && (src->last < to) )
243 {
244 if(ref)
245 ref = _tok_2d(handle, src->uuid, &src->state);
246
247 src->dirty= false;
248 }
249 }
250
251 if(ref)
252 ref = _alv(handle);
253 if(ref)
254 lv2_osc_forge_pop(&handle->forge, bndl_frame);
255
256 return ref;
257 }
258
259 static inline void
_upd(plughandle_t * handle,int64_t frames)260 _upd(plughandle_t *handle, int64_t frames)
261 {
262 if(handle->dirty && (frames > handle->last) )
263 {
264 if(handle->ref)
265 handle->ref = _tuio2_2d(handle, handle->last, frames);
266
267 handle->last = frames;
268 handle->dirty = false;
269 }
270 }
271
272 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)273 _add(void *data, int64_t frames, const xpress_state_t *state,
274 xpress_uuid_t uuid, void *target)
275 {
276 plughandle_t *handle = data;
277 targetI_t *src = target;
278
279 _upd(handle, frames);
280
281 src->uuid = xpress_map(&handle->xpressI);
282 src->state = *state;
283 src->state.pitch = (state->pitch*0x7f - handle->bot) * handle->ran_1;
284 src->dirty = true;
285 src->last = frames;
286
287 handle->dirty = true;
288 }
289
290 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)291 _set(void *data, int64_t frames, const xpress_state_t *state,
292 xpress_uuid_t uuid, void *target)
293 {
294 plughandle_t *handle = data;
295 targetI_t *src = target;
296
297 _upd(handle, frames);
298
299 src->state = *state;
300 src->state.pitch = (state->pitch*0x7f - handle->bot) * handle->ran_1;
301 src->dirty = true;
302 src->last = frames;
303
304 handle->dirty = true;
305 }
306
307 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)308 _del(void *data, int64_t frames,
309 xpress_uuid_t uuid, void *target)
310 {
311 plughandle_t *handle = data;
312
313 _upd(handle, frames);
314
315 handle->dirty = true;
316 }
317
318 static const xpress_iface_t ifaceI = {
319 .size = sizeof(targetI_t),
320
321 .add = _add,
322 .set = _set,
323 .del = _del
324 };
325
326 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)327 instantiate(const LV2_Descriptor* descriptor, double rate,
328 const char *bundle_path, const LV2_Feature *const *features)
329 {
330 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
331 if(!handle)
332 return NULL;
333
334 xpress_map_t *voice_map = NULL;
335
336 for(unsigned i=0; features[i]; i++)
337 {
338 if(!strcmp(features[i]->URI, LV2_URID__map))
339 handle->map = features[i]->data;
340 else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
341 voice_map = features[i]->data;
342 else if(!strcmp(features[i]->URI, LV2_OSC__schedule))
343 handle->osc_sched = features[i]->data;
344 }
345
346 if(!handle->map)
347 {
348 fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
349 free(handle);
350 return NULL;
351 }
352
353 lv2_atom_forge_init(&handle->forge, handle->map);
354 lv2_osc_urid_init(&handle->osc_urid, handle->map);
355
356 if(!xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
357 XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle))
358 {
359 free(handle);
360 return NULL;
361 }
362
363 if(!props_init(&handle->props, descriptor->URI,
364 defs, MAX_NPROPS, &handle->state, &handle->stash,
365 handle->map, handle))
366 {
367 fprintf(stderr, "failed to allocate property structure\n");
368 free(handle);
369 return NULL;
370 }
371
372 return handle;
373 }
374
375 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)376 connect_port(LV2_Handle instance, uint32_t port, void *data)
377 {
378 plughandle_t *handle = instance;
379
380 switch(port)
381 {
382 case 0:
383 handle->event_in = (const LV2_Atom_Sequence *)data;
384 break;
385 case 1:
386 handle->event_out = (LV2_Atom_Sequence *)data;
387 break;
388 default:
389 break;
390 }
391 }
392
393 static void
run(LV2_Handle instance,uint32_t nsamples)394 run(LV2_Handle instance, uint32_t nsamples)
395 {
396 plughandle_t *handle = instance;
397
398 // prepare midi atom forge
399 const uint32_t capacity = handle->event_out->atom.size;
400 LV2_Atom_Forge *forge = &handle->forge;
401 lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
402 LV2_Atom_Forge_Frame frame;
403 handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
404
405 props_idle(&handle->props, forge, 0, &handle->ref);
406 xpress_pre(&handle->xpressI);
407
408 handle->last = -1;
409 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
410 {
411 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
412 const int64_t frames = ev->time.frames;
413
414 if(handle->last == -1)
415 handle->last = frames;
416
417 if(!props_advance(&handle->props, forge, handle->last, obj, &handle->ref)) //XXX frame time
418 {
419 xpress_advance(&handle->xpressI, forge, frames, obj, &handle->ref);
420 }
421 }
422 _upd(handle, nsamples - 1);
423
424 xpress_post(&handle->xpressI, nsamples-1);
425
426 if(handle->ref)
427 lv2_atom_forge_pop(forge, &frame);
428 else
429 lv2_atom_sequence_clear(handle->event_out);
430 }
431
432 static void
cleanup(LV2_Handle instance)433 cleanup(LV2_Handle instance)
434 {
435 plughandle_t *handle = instance;
436
437 if(handle)
438 {
439 xpress_deinit(&handle->xpressI);
440 free(handle);
441 }
442 }
443
444 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)445 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
446 LV2_State_Handle state, uint32_t flags,
447 const LV2_Feature *const *features)
448 {
449 plughandle_t *handle = instance;
450
451 return props_save(&handle->props, store, state, flags, features);
452 }
453
454 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)455 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
456 LV2_State_Handle state, uint32_t flags,
457 const LV2_Feature *const *features)
458 {
459 plughandle_t *handle = instance;
460
461 return props_restore(&handle->props, retrieve, state, flags, features);
462 }
463
464 static const LV2_State_Interface state_iface = {
465 .save = _state_save,
466 .restore = _state_restore
467 };
468
469 static const void *
extension_data(const char * uri)470 extension_data(const char *uri)
471 {
472 if(!strcmp(uri, LV2_STATE__interface))
473 return &state_iface;
474 return NULL;
475 }
476
477 const LV2_Descriptor tuio2_out= {
478 .URI = ESPRESSIVO_TUIO2_OUT_URI,
479 .instantiate = instantiate,
480 .connect_port = connect_port,
481 .activate = NULL,
482 .run = run,
483 .deactivate = NULL,
484 .cleanup = cleanup,
485 .extension_data = extension_data
486 };
487