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