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