1 /*
2  * Copyright (c) 2015-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 #define MAX_NPROPS 2
26 
27 typedef struct _targetI_t targetI_t;
28 typedef struct _targetO_t targetO_t;
29 typedef struct _plugstate_t plugstate_t;
30 typedef struct _plughandle_t plughandle_t;
31 
32 struct _targetI_t {
33 	xpress_uuid_t uuid;
34 	bool below;
35 	float x;
36 };
37 
38 struct _targetO_t {
39 	// empty
40 };
41 
42 struct _plugstate_t {
43 	float position_threshold;
44 	float velocity_threshold;
45 };
46 
47 struct _plughandle_t {
48 	LV2_URID_Map *map;
49 	LV2_Atom_Forge forge;
50 	LV2_Atom_Forge_Ref ref;
51 
52 	PROPS_T(props, MAX_NPROPS);
53 	XPRESS_T(xpressI, MAX_NVOICES);
54 	XPRESS_T(xpressO, MAX_NVOICES);
55 	targetI_t targetI [MAX_NVOICES];
56 	targetO_t targetO [MAX_NVOICES];
57 
58 	const LV2_Atom_Sequence *event_in;
59 	LV2_Atom_Sequence *event_out;
60 
61 	plugstate_t state;
62 	plugstate_t stash;
63 };
64 
65 static const props_def_t defs [MAX_NPROPS] = {
66 	{
67 		.property = ESPRESSIVO_URI"#reducto_position_threshold",
68 		.offset = offsetof(plugstate_t, position_threshold),
69 		.type = LV2_ATOM__Float,
70 	},
71 	{
72 		.property = ESPRESSIVO_URI"#reducto_velocity_threshold",
73 		.offset = offsetof(plugstate_t, velocity_threshold),
74 		.type = LV2_ATOM__Float,
75 	}
76 };
77 
78 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)79 _add(void *data, int64_t frames, const xpress_state_t *state,
80 	xpress_uuid_t uuid, void *target)
81 {
82 	plughandle_t *handle = data;
83 	targetI_t *src = target;
84 
85 	LV2_Atom_Forge *forge = &handle->forge;
86 
87 	targetO_t *dst = xpress_create(&handle->xpressO, &src->uuid);
88 	(void)dst;
89 
90 	src->below = true;
91 	src->x = state->pitch * 0x7f;
92 
93 	if(handle->ref)
94 		handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuid, state);
95 }
96 
97 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)98 _set(void *data, int64_t frames, const xpress_state_t *state,
99 	xpress_uuid_t uuid, void *target)
100 {
101 	plughandle_t *handle = data;
102 	targetI_t *src = target;
103 
104 	bool spawn_new = false;
105 	const float vel_x_abs = fabs(state->dPitch);
106 
107 	if(src->below)
108 	{
109 		if(vel_x_abs >= handle->state.velocity_threshold)
110 			src->below = false;
111 	}
112 	else // !src->below
113 	{
114 		if(vel_x_abs < handle->state.velocity_threshold)
115 		{
116 			const float pos_x_diff_abs = fabs(state->pitch * 0x7f - src->x);
117 
118 			if(pos_x_diff_abs >= handle->state.position_threshold)
119 				spawn_new = true;
120 		}
121 	}
122 
123 	if(spawn_new)
124 	{
125 		LV2_Atom_Forge *forge = &handle->forge;
126 
127 		// delete previous event
128 		xpress_free(&handle->xpressO, src->uuid);
129 
130 		if(handle->ref)
131 			handle->ref = xpress_alive(&handle->xpressO, forge, frames);
132 
133 		// create new event
134 		targetO_t *dst = xpress_create(&handle->xpressO, &src->uuid);
135 		(void)dst;
136 
137 		src->below = true;
138 		src->x = state->pitch * 0x7f;
139 
140 		if(handle->ref)
141 			handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuid, state);
142 	}
143 }
144 
145 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)146 _del(void *data, int64_t frames,
147 	xpress_uuid_t uuid, void *target)
148 {
149 	plughandle_t *handle = data;
150 	targetI_t *src = target;
151 
152 	LV2_Atom_Forge *forge = &handle->forge;
153 
154 	xpress_free(&handle->xpressO, src->uuid);
155 
156 	if(handle->ref)
157 		handle->ref = xpress_alive(&handle->xpressO, forge, frames);
158 }
159 
160 static const xpress_iface_t ifaceI = {
161 	.size = sizeof(targetI_t),
162 
163 	.add = _add,
164 	.set = _set,
165 	.del = _del
166 };
167 
168 static const xpress_iface_t ifaceO = {
169 	.size = sizeof(targetO_t)
170 };
171 
172 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)173 instantiate(const LV2_Descriptor* descriptor, double rate,
174 	const char *bundle_path, const LV2_Feature *const *features)
175 {
176 	plughandle_t *handle = calloc(1, sizeof(plughandle_t));
177 	if(!handle)
178 		return NULL;
179 
180 	xpress_map_t *voice_map = NULL;
181 
182 	for(unsigned i=0; features[i]; i++)
183 	{
184 		if(!strcmp(features[i]->URI, LV2_URID__map))
185 			handle->map = features[i]->data;
186 		else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
187 			voice_map = features[i]->data;
188 	}
189 
190 	if(!handle->map)
191 	{
192 		fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
193 		free(handle);
194 		return NULL;
195 	}
196 
197 	lv2_atom_forge_init(&handle->forge, handle->map);
198 
199 	if(  !xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
200 			XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle)
201 		|| !xpress_init(&handle->xpressO, MAX_NVOICES, handle->map, voice_map,
202 			XPRESS_EVENT_NONE, &ifaceO, handle->targetO, handle) )
203 	{
204 		free(handle);
205 		return NULL;
206 	}
207 
208 	if(!props_init(&handle->props, descriptor->URI,
209 		defs, MAX_NPROPS, &handle->state, &handle->stash,
210 		handle->map, handle))
211 	{
212 		fprintf(stderr, "failed to allocate property structure\n");
213 		free(handle);
214 		return NULL;
215 	}
216 
217 	return handle;
218 }
219 
220 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)221 connect_port(LV2_Handle instance, uint32_t port, void *data)
222 {
223 	plughandle_t *handle = instance;
224 
225 	switch(port)
226 	{
227 		case 0:
228 			handle->event_in = (const LV2_Atom_Sequence *)data;
229 			break;
230 		case 1:
231 			handle->event_out = (LV2_Atom_Sequence *)data;
232 			break;
233 		default:
234 			break;
235 	}
236 }
237 
238 static void
run(LV2_Handle instance,uint32_t nsamples)239 run(LV2_Handle instance, uint32_t nsamples)
240 {
241 	plughandle_t *handle = instance;
242 
243 	// prepare midi atom forge
244 	const uint32_t capacity = handle->event_out->atom.size;
245 	LV2_Atom_Forge *forge = &handle->forge;
246 	lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
247 	LV2_Atom_Forge_Frame frame;
248 	handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
249 
250 	props_idle(&handle->props, forge, 0, &handle->ref);
251 	xpress_pre(&handle->xpressI);
252 	xpress_rst(&handle->xpressO);
253 
254 	LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
255 	{
256 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
257 		const int64_t frames = ev->time.frames;
258 
259 		if(!props_advance(&handle->props, forge, frames, obj, &handle->ref))
260 		{
261 			xpress_advance(&handle->xpressI, forge, frames, obj, &handle->ref);
262 		}
263 	}
264 
265 	xpress_post(&handle->xpressI, nsamples-1);
266 	if(handle->ref && !xpress_synced(&handle->xpressO))
267 		handle->ref = xpress_alive(&handle->xpressO, forge, nsamples-1);
268 
269 	if(handle->ref)
270 		lv2_atom_forge_pop(forge, &frame);
271 	else
272 		lv2_atom_sequence_clear(handle->event_out);
273 }
274 
275 static void
cleanup(LV2_Handle instance)276 cleanup(LV2_Handle instance)
277 {
278 	plughandle_t *handle = instance;
279 
280 	if(handle)
281 	{
282 		xpress_deinit(&handle->xpressI);
283 		xpress_deinit(&handle->xpressO);
284 		free(handle);
285 	}
286 }
287 
288 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)289 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
290 	LV2_State_Handle state, uint32_t flags,
291 	const LV2_Feature *const *features)
292 {
293 	plughandle_t *handle = instance;
294 
295 	return props_save(&handle->props, store, state, flags, features);
296 }
297 
298 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)299 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
300 	LV2_State_Handle state, uint32_t flags,
301 	const LV2_Feature *const *features)
302 {
303 	plughandle_t *handle = instance;
304 
305 	return props_restore(&handle->props, retrieve, state, flags, features);
306 }
307 
308 static const LV2_State_Interface state_iface = {
309 	.save = _state_save,
310 	.restore = _state_restore
311 };
312 
313 static const void *
extension_data(const char * uri)314 extension_data(const char *uri)
315 {
316 	if(!strcmp(uri, LV2_STATE__interface))
317 		return &state_iface;
318 	return NULL;
319 }
320 
321 const LV2_Descriptor reducto = {
322 	.URI						= ESPRESSIVO_REDUCTO_URI,
323 	.instantiate		= instantiate,
324 	.connect_port		= connect_port,
325 	.activate				= NULL,
326 	.run						= run,
327 	.deactivate			= NULL,
328 	.cleanup				= cleanup,
329 	.extension_data	= extension_data
330 };
331