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