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