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