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 9
26
27 typedef enum _enum_t enum_t;
28 typedef enum _op_t op_t;
29 typedef struct _targetI_t targetI_t;
30 typedef struct _targetO_t targetO_t;
31 typedef struct _plugstate_t plugstate_t;
32 typedef struct _plughandle_t plughandle_t;
33
34 enum _enum_t {
35 ENUM_PITCH = 0,
36 ENUM_PRESSURE,
37 ENUM_TIMBRE,
38 ENUM_DPITCH,
39 ENUM_DPRESSURE,
40 ENUM_DTIMBRE
41 };
42
43 enum _op_t {
44 OP_ADD = 0,
45 OP_SUB,
46 OP_MUL,
47 OP_DIV,
48 OP_POW,
49 OP_SET
50 };
51
52 struct _targetI_t {
53 xpress_uuid_t uuid;
54 int32_t zone_mask;
55 };
56
57 struct _targetO_t {
58 xpress_state_t state;
59 };
60
61 struct _plugstate_t {
62 int32_t zone_mask_src;
63 int32_t zone_mask_mod;;
64 int32_t zone_offset;;
65 int32_t enum_src;
66 int32_t enum_mod;
67 float multiplier;
68 float adder;
69 int32_t op;
70 int32_t reset;
71 };
72
73 struct _plughandle_t {
74 LV2_URID_Map *map;
75 LV2_Atom_Forge forge;
76 LV2_Atom_Forge_Ref ref;
77
78 PROPS_T(props, MAX_NPROPS);
79 XPRESS_T(xpressI, MAX_NVOICES);
80 XPRESS_T(xpressO, MAX_NVOICES);
81 targetI_t targetI [MAX_NVOICES];
82 targetO_t targetO [MAX_NVOICES];
83
84 const LV2_Atom_Sequence *event_in;
85 LV2_Atom_Sequence *event_out;
86
87 plugstate_t state;
88 plugstate_t stash;
89
90 xpress_uuid_t uuid;
91 xpress_state_t modu;
92 };
93
94 static const xpress_state_t empty_state = {
95 .zone = 0,
96
97 .pitch = 0.f,
98 .pressure = 0.f,
99 .timbre = 0.f,
100
101 .dPitch = 0.f,
102 .dPressure = 0.f,
103 .dTimbre = 0.f
104 };
105
106 static inline float
_clip(float min,float val,float max)107 _clip(float min, float val, float max)
108 {
109 if(val < min)
110 return min;
111 else if(val > max)
112 return max;
113 return val;
114 }
115
116 static inline float
_op(plughandle_t * handle,float dst,float val)117 _op(plughandle_t *handle, float dst, float val)
118 {
119 switch((op_t)handle->state.op)
120 {
121 case OP_ADD:
122 return dst + val;
123 case OP_SUB:
124 return dst - val;
125 case OP_MUL:
126 return dst * val;
127 case OP_DIV:
128 return (val == 0.f) ? 0.f : dst / val;
129 case OP_POW:
130 return powf(dst, val);
131 case OP_SET:
132 return val;
133 }
134
135 return 0.f;
136 }
137
138 static inline void
_modulate(plughandle_t * handle,xpress_state_t * state)139 _modulate(plughandle_t *handle, xpress_state_t *state)
140 {
141 float val = 0.f;
142
143 switch((enum_t)handle->state.enum_mod)
144 {
145 case ENUM_PITCH:
146 {
147 val = handle->modu.pitch;
148 } break;
149 case ENUM_PRESSURE:
150 {
151 val = handle->modu.pressure;
152 } break;
153 case ENUM_TIMBRE:
154 {
155 val = handle->modu.timbre;
156 } break;
157 case ENUM_DPITCH:
158 {
159 val = handle->modu.dPitch;
160 } break;
161 case ENUM_DPRESSURE:
162 {
163 val = handle->modu.dPressure;
164 } break;
165 case ENUM_DTIMBRE:
166 {
167 val = handle->modu.dTimbre;
168 } break;
169 }
170
171 val *= handle->state.multiplier;
172 val += handle->state.adder;
173
174 switch((enum_t)handle->state.enum_src)
175 {
176 case ENUM_PITCH:
177 {
178 state->pitch = _op(handle, state->pitch, val);
179 state->pitch = _clip(0.f, state->pitch, 1.f);
180 } break;
181 case ENUM_PRESSURE:
182 {
183 state->pressure = _op(handle, state->pressure, val);
184 state->pressure = _clip(0.f, state->pressure, 1.f);
185 } break;
186 case ENUM_TIMBRE:
187 {
188 state->timbre = _op(handle, state->timbre, val);
189 state->timbre = _clip(0.f, state->timbre, 1.f);
190 } break;
191 case ENUM_DPITCH:
192 {
193 state->dPitch = _op(handle, state->dPitch, val);
194 } break;
195 case ENUM_DPRESSURE:
196 {
197 state->dPressure = _op(handle, state->dPressure, val);
198 } break;
199 case ENUM_DTIMBRE:
200 {
201 state->dTimbre = _op(handle, state->dTimbre, val);
202 } break;
203 }
204 }
205
206 static inline void
_upd(plughandle_t * handle,int64_t frames)207 _upd(plughandle_t *handle, int64_t frames)
208 {
209 XPRESS_VOICE_FOREACH(&handle->xpressO, voice)
210 {
211 LV2_Atom_Forge *forge = &handle->forge;
212
213 targetO_t *dst = voice->target;
214
215 xpress_state_t new_state = dst->state;
216 new_state.zone += handle->state.zone_offset;
217 _modulate(handle, &new_state);
218
219 if(handle->ref)
220 handle->ref = xpress_token(&handle->xpressO, forge, frames, voice->uuid, &new_state);
221 }
222 }
223
224 static void
_intercept(void * data,int64_t frames,props_impl_t * impl)225 _intercept(void *data, int64_t frames, props_impl_t *impl)
226 {
227 plughandle_t *handle = data;
228
229 _upd(handle, frames);
230 }
231
232 static const props_def_t defs [MAX_NPROPS] = {
233 {
234 .property = ESPRESSIVO_URI"#modulator_zone_mask_src",
235 .offset = offsetof(plugstate_t, zone_mask_src),
236 .type = LV2_ATOM__Int
237 },
238 {
239 .property = ESPRESSIVO_URI"#modulator_zone_mask_mod",
240 .offset = offsetof(plugstate_t, zone_mask_mod),
241 .type = LV2_ATOM__Int
242 },
243 {
244 .property = ESPRESSIVO_URI"#modulator_enum_src",
245 .offset = offsetof(plugstate_t, enum_src),
246 .type = LV2_ATOM__Int,
247 .event_cb = _intercept
248 },
249 {
250 .property = ESPRESSIVO_URI"#modulator_enum_mod",
251 .offset = offsetof(plugstate_t, enum_mod),
252 .type = LV2_ATOM__Int,
253 .event_cb = _intercept
254 },
255 {
256 .property = ESPRESSIVO_URI"#modulator_zone_offset",
257 .offset = offsetof(plugstate_t, zone_offset),
258 .type = LV2_ATOM__Int
259 },
260 {
261 .property = ESPRESSIVO_URI"#modulator_multiplier",
262 .offset = offsetof(plugstate_t, multiplier),
263 .type = LV2_ATOM__Float,
264 .event_cb = _intercept
265 },
266 {
267 .property = ESPRESSIVO_URI"#modulator_adder",
268 .offset = offsetof(plugstate_t, adder),
269 .type = LV2_ATOM__Float,
270 .event_cb = _intercept
271 },
272 {
273 .property = ESPRESSIVO_URI"#modulator_op",
274 .offset = offsetof(plugstate_t, op),
275 .type = LV2_ATOM__Int,
276 .event_cb = _intercept
277 },
278 {
279 .property = ESPRESSIVO_URI"#modulator_reset",
280 .offset = offsetof(plugstate_t, reset),
281 .type = LV2_ATOM__Bool
282 }
283 };
284
285 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)286 _add(void *data, int64_t frames, const xpress_state_t *state,
287 xpress_uuid_t uuid, void *target)
288 {
289 plughandle_t *handle = data;
290 targetI_t *src = target;
291 src->zone_mask = 1 << state->zone;
292
293 if(src->zone_mask & handle->state.zone_mask_src)
294 {
295 LV2_Atom_Forge *forge = &handle->forge;
296
297 targetO_t *dst = xpress_create(&handle->xpressO, &src->uuid);
298 dst->state = *state;
299
300 xpress_state_t new_state = dst->state;
301 new_state.zone += handle->state.zone_offset;
302 _modulate(handle, &new_state);
303
304 if(handle->ref)
305 handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuid, &new_state);
306 }
307
308 if(src->zone_mask & handle->state.zone_mask_mod)
309 {
310 if(handle->uuid == 0) // no modulator registered, yet
311 {
312 handle->uuid = uuid;
313 handle->modu = *state;
314
315 _upd(handle, frames);
316 }
317 }
318 }
319
320 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)321 _set(void *data, int64_t frames, const xpress_state_t *state,
322 xpress_uuid_t uuid, void *target)
323 {
324 plughandle_t *handle = data;
325 targetI_t *src = target;
326
327 if(src->zone_mask & handle->state.zone_mask_src)
328 {
329 LV2_Atom_Forge *forge = &handle->forge;
330
331 targetO_t *dst = xpress_get(&handle->xpressO, src->uuid);
332 dst->state = *state;
333
334 xpress_state_t new_state = dst->state;
335 new_state.zone += handle->state.zone_offset;
336 _modulate(handle, &new_state);
337
338 if(handle->ref)
339 handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuid, &new_state);
340 }
341
342 if(src->zone_mask & handle->state.zone_mask_mod)
343 {
344 if(handle->uuid == uuid) // this is our modulator
345 {
346 handle->modu = *state;
347
348 _upd(handle, frames);
349 }
350 }
351 }
352
353 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)354 _del(void *data, int64_t frames,
355 xpress_uuid_t uuid, void *target)
356 {
357 plughandle_t *handle = data;
358 targetI_t *src = target;
359
360 if(src->zone_mask & handle->state.zone_mask_src)
361 {
362 LV2_Atom_Forge *forge = &handle->forge;
363
364 xpress_free(&handle->xpressO, src->uuid);
365
366 if(handle->ref)
367 handle->ref = xpress_alive(&handle->xpressO, forge, frames);
368 }
369
370 if(src->zone_mask & handle->state.zone_mask_mod)
371 {
372 if(handle->uuid == uuid) // this is our modulator
373 {
374 handle->uuid = 0;
375 if(handle->state.reset)
376 handle->modu = empty_state;
377
378 _upd(handle, frames);
379 }
380 }
381 }
382
383 static const xpress_iface_t ifaceI = {
384 .size = sizeof(targetI_t),
385
386 .add = _add,
387 .set = _set,
388 .del = _del
389 };
390
391 static const xpress_iface_t ifaceO = {
392 .size = sizeof(targetO_t)
393 };
394
395 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)396 instantiate(const LV2_Descriptor* descriptor, double rate,
397 const char *bundle_path, const LV2_Feature *const *features)
398 {
399 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
400 if(!handle)
401 return NULL;
402
403 xpress_map_t *voice_map = NULL;
404
405 for(unsigned i=0; features[i]; i++)
406 {
407 if(!strcmp(features[i]->URI, LV2_URID__map))
408 handle->map = features[i]->data;
409 else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
410 voice_map = features[i]->data;
411 }
412
413 if(!handle->map)
414 {
415 fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
416 free(handle);
417 return NULL;
418 }
419
420 lv2_atom_forge_init(&handle->forge, handle->map);
421
422 if( !xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
423 XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle)
424 || !xpress_init(&handle->xpressO, MAX_NVOICES, handle->map, voice_map,
425 XPRESS_EVENT_NONE, &ifaceO, handle->targetO, handle) )
426 {
427 free(handle);
428 return NULL;
429 }
430
431 if(!props_init(&handle->props, descriptor->URI,
432 defs, MAX_NPROPS, &handle->state, &handle->stash,
433 handle->map, handle))
434 {
435 fprintf(stderr, "failed to allocate property structure\n");
436 free(handle);
437 return NULL;
438 }
439
440 return handle;
441 }
442
443 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)444 connect_port(LV2_Handle instance, uint32_t port, void *data)
445 {
446 plughandle_t *handle = instance;
447
448 switch(port)
449 {
450 case 0:
451 handle->event_in = (const LV2_Atom_Sequence *)data;
452 break;
453 case 1:
454 handle->event_out = (LV2_Atom_Sequence *)data;
455 break;
456 default:
457 break;
458 }
459 }
460
461 static void
run(LV2_Handle instance,uint32_t nsamples)462 run(LV2_Handle instance, uint32_t nsamples)
463 {
464 plughandle_t *handle = instance;
465
466 // prepare midi atom forge
467 const uint32_t capacity = handle->event_out->atom.size;
468 LV2_Atom_Forge *forge = &handle->forge;
469 lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
470 LV2_Atom_Forge_Frame frame;
471 handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
472
473 props_idle(&handle->props, forge, 0, &handle->ref);
474 xpress_pre(&handle->xpressI);
475 xpress_rst(&handle->xpressO);
476
477 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
478 {
479 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
480 const int64_t frames = ev->time.frames;
481
482 if(!props_advance(&handle->props, forge, frames, obj, &handle->ref))
483 {
484 xpress_advance(&handle->xpressI, forge, frames, obj, &handle->ref);
485 }
486 }
487
488 xpress_post(&handle->xpressI, nsamples-1);
489 if(handle->ref && !xpress_synced(&handle->xpressO))
490 handle->ref = xpress_alive(&handle->xpressO, forge, nsamples-1);
491
492 if(handle->ref)
493 lv2_atom_forge_pop(forge, &frame);
494 else
495 lv2_atom_sequence_clear(handle->event_out);
496 }
497
498 static void
cleanup(LV2_Handle instance)499 cleanup(LV2_Handle instance)
500 {
501 plughandle_t *handle = instance;
502
503 if(handle)
504 {
505 xpress_deinit(&handle->xpressI);
506 xpress_deinit(&handle->xpressO);
507 free(handle);
508 }
509 }
510
511 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)512 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
513 LV2_State_Handle state, uint32_t flags,
514 const LV2_Feature *const *features)
515 {
516 plughandle_t *handle = instance;
517
518 return props_save(&handle->props, store, state, flags, features);
519 }
520
521 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)522 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
523 LV2_State_Handle state, uint32_t flags,
524 const LV2_Feature *const *features)
525 {
526 plughandle_t *handle = instance;
527
528 return props_restore(&handle->props, retrieve, state, flags, features);
529 }
530
531 static const LV2_State_Interface state_iface = {
532 .save = _state_save,
533 .restore = _state_restore
534 };
535
536 static const void *
extension_data(const char * uri)537 extension_data(const char *uri)
538 {
539 if(!strcmp(uri, LV2_STATE__interface))
540 return &state_iface;
541 return NULL;
542 }
543
544 const LV2_Descriptor modulator = {
545 .URI = ESPRESSIVO_MODULATOR_URI,
546 .instantiate = instantiate,
547 .connect_port = connect_port,
548 .activate = NULL,
549 .run = run,
550 .deactivate = NULL,
551 .cleanup = cleanup,
552 .extension_data = extension_data
553 };
554