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