1 /*
2 * Copyright (c) 2015 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 #include <mpe.h>
26
27 #define MAX_NPROPS (2 + MPE_ZONE_MAX*4)
28
29 typedef struct _targetI_t targetI_t;
30 typedef struct _plugstate_t plugstate_t;
31 typedef struct _plughandle_t plughandle_t;
32
33 struct _targetI_t {
34 uint8_t chan;
35 uint8_t zone;
36 uint8_t key;
37 };
38
39 struct _plugstate_t {
40 int32_t zones;
41 int32_t velocity;
42 int32_t master_range [MPE_ZONE_MAX];
43 int32_t voice_range [MPE_ZONE_MAX];
44 int32_t pressure_controller [MPE_ZONE_MAX];
45 int32_t timbre_controller [MPE_ZONE_MAX];
46 };
47
48 struct _plughandle_t {
49 struct {
50 LV2_URID midi_MidiEvent;
51 } uris;
52
53 LV2_URID_Map *map;
54 LV2_Atom_Forge forge;
55 LV2_Atom_Forge_Ref ref;
56
57 XPRESS_T(xpressI, MAX_NVOICES);
58 targetI_t targetI [MAX_NVOICES];
59
60 const LV2_Atom_Sequence *event_in;
61 LV2_Atom_Sequence *midi_out;
62
63 mpe_t mpe;
64 PROPS_T(props, MAX_NPROPS);
65
66 plugstate_t state;
67 plugstate_t stash;
68 };
69
70 static inline void
mpe_populate(mpe_t * mpe,uint8_t n_zones)71 mpe_populate(mpe_t *mpe, uint8_t n_zones)
72 {
73 plughandle_t *handle = (void *)mpe - offsetof(plughandle_t, mpe);
74
75 assert(n_zones > 0);
76 n_zones %= MPE_ZONE_MAX + 1; // wrap around if n_zones > MPE_ZONE_MAX
77 int8_t rem = MPE_CHAN_MAX % n_zones;
78 const uint8_t span = (MPE_CHAN_MAX - rem) / n_zones - 1;
79 uint8_t ptr = 0;
80
81 mpe->n_zones = n_zones;
82 zone_t *zones = mpe->zones;
83 int8_t *channels = mpe->channels;
84
85 for(uint8_t i=0;
86 i<n_zones;
87 rem--, ptr += 1 + zones[i++].span)
88 {
89 zones[i].base = ptr;
90 zones[i].ref = 0;
91 zones[i].span = span;
92 if(rem > 0)
93 zones[i].span += 1;
94 zones[i].master_range = handle->state.master_range[i];
95 zones[i].voice_range = handle->state.voice_range[i];
96 }
97
98 for(uint8_t i=0; i<MPE_CHAN_MAX; i++)
99 channels[i] = 0;
100 }
101
102 static inline uint8_t
mpe_acquire(mpe_t * mpe,uint8_t zone_idx)103 mpe_acquire(mpe_t *mpe, uint8_t zone_idx)
104 {
105 zone_idx %= mpe->n_zones; // wrap around if zone_idx > n_zones
106 zone_t *zone = &mpe->zones[zone_idx];
107 int8_t *channels = mpe->channels;
108
109 int8_t min = INT8_MAX;
110 uint8_t pos = zone->ref; // start search at current channel
111 const uint8_t base_1 = zone->base + 1;
112 for(uint8_t i = zone->ref; i < zone->ref + zone->span; i++)
113 {
114 const uint8_t ch = base_1 + (i % zone->span); // wrap to [0..span]
115 if(channels[ch] < min) // check for less occupation
116 {
117 min = channels[ch]; // lower minimum
118 pos = i; // set new minimally occupied channel
119 }
120 }
121
122 const uint8_t ch = base_1 + (pos % zone->span); // wrap to [0..span]
123 if(channels[ch] <= 0) // off since long
124 channels[ch] = 1;
125 else
126 channels[ch] += 1; // increase occupation
127 zone->ref = (pos + 1) % zone->span; // start next search from next channel
128
129 return ch;
130 }
131
132 static inline float
mpe_range_1(mpe_t * mpe,uint8_t zone_idx)133 mpe_range_1(mpe_t *mpe, uint8_t zone_idx)
134 {
135 zone_idx %= mpe->n_zones; // wrap around if zone_idx > n_zones
136 zone_t *zone = &mpe->zones[zone_idx];
137 return 1.f / (float)zone->voice_range;
138 }
139
140 static inline void
mpe_release(mpe_t * mpe,uint8_t zone_idx,uint8_t ch)141 mpe_release(mpe_t *mpe, uint8_t zone_idx, uint8_t ch)
142 {
143 zone_idx %= mpe->n_zones; // wrap around if zone_idx > n_zones
144 ch %= MPE_CHAN_MAX; // wrap around if ch > MPE_CHAN_MAX
145 zone_t *zone = &mpe->zones[zone_idx];
146 int8_t *channels = mpe->channels;
147
148 const uint8_t base_1 = zone->base + 1;
149 for(uint8_t i = base_1; i < base_1 + zone->span; i++)
150 {
151 if( (i == ch) || (channels[i] <= 0) )
152 channels[i] -= 1;
153 // do not decrease occupied channels
154 }
155 }
156
157 static inline LV2_Atom_Forge_Ref
_midi_event(plughandle_t * handle,int64_t frames,const uint8_t * m,size_t len)158 _midi_event(plughandle_t *handle, int64_t frames, const uint8_t *m, size_t len)
159 {
160 LV2_Atom_Forge *forge = &handle->forge;
161 LV2_Atom_Forge_Ref ref;
162
163 ref = lv2_atom_forge_frame_time(forge, frames);
164 if(ref)
165 ref = lv2_atom_forge_atom(forge, len, handle->uris.midi_MidiEvent);
166 if(ref)
167 ref = lv2_atom_forge_write(forge, m, len);
168
169 return ref;
170 }
171
172 static inline LV2_Atom_Forge_Ref
_zone_span_update(plughandle_t * handle,int64_t frames,unsigned zone_idx)173 _zone_span_update(plughandle_t *handle, int64_t frames, unsigned zone_idx)
174 {
175 LV2_Atom_Forge_Ref fref;
176 mpe_t *mpe = &handle->mpe;
177 zone_t *zone = &mpe->zones[zone_idx];
178 const uint8_t zone_ch = zone->base;
179
180 const uint8_t lsb [3] = {
181 LV2_MIDI_MSG_CONTROLLER | zone_ch,
182 LV2_MIDI_CTL_RPN_LSB,
183 0x6 // zone
184 };
185
186 const uint8_t msb [3] = {
187 LV2_MIDI_MSG_CONTROLLER | zone_ch,
188 LV2_MIDI_CTL_RPN_MSB,
189 0x0
190 };
191
192 const uint8_t dat [3] = {
193 LV2_MIDI_MSG_CONTROLLER | zone_ch,
194 LV2_MIDI_CTL_MSB_DATA_ENTRY,
195 zone->span
196 };
197
198 fref = _midi_event(handle, frames, lsb, 3);
199 if(fref)
200 fref = _midi_event(handle, frames, msb, 3);
201 if(fref)
202 fref = _midi_event(handle, frames, dat, 3);
203
204 return fref;
205 }
206
207 static inline LV2_Atom_Forge_Ref
_master_range_update(plughandle_t * handle,int64_t frames,unsigned zone_idx)208 _master_range_update(plughandle_t *handle, int64_t frames, unsigned zone_idx)
209 {
210 LV2_Atom_Forge_Ref fref;
211 mpe_t *mpe = &handle->mpe;
212 zone_t *zone = &mpe->zones[zone_idx];
213 const uint8_t zone_ch = zone->base;
214
215 const uint8_t lsb [3] = {
216 LV2_MIDI_MSG_CONTROLLER | zone_ch,
217 LV2_MIDI_CTL_RPN_LSB,
218 0x0, // bend range
219 };
220
221 const uint8_t msb [3] = {
222 LV2_MIDI_MSG_CONTROLLER | zone_ch,
223 LV2_MIDI_CTL_RPN_MSB,
224 0x0,
225 };
226 const uint8_t dat [3] = {
227 LV2_MIDI_MSG_CONTROLLER | zone_ch,
228 LV2_MIDI_CTL_MSB_DATA_ENTRY,
229 zone->master_range
230 };
231
232 fref = _midi_event(handle, frames, lsb, 3);
233 if(fref)
234 fref = _midi_event(handle, frames, msb, 3);
235 if(fref)
236 fref = _midi_event(handle, frames, dat, 3);
237
238 return fref;
239 }
240
241 static inline LV2_Atom_Forge_Ref
_voice_range_update(plughandle_t * handle,int64_t frames,unsigned zone_idx)242 _voice_range_update(plughandle_t *handle, int64_t frames, unsigned zone_idx)
243 {
244 LV2_Atom_Forge_Ref fref;
245 mpe_t *mpe = &handle->mpe;
246 zone_t *zone = &mpe->zones[zone_idx];
247 const uint8_t voice_ch = zone->base + 1;
248
249 const uint8_t lsb [3] = {
250 LV2_MIDI_MSG_CONTROLLER | voice_ch,
251 LV2_MIDI_CTL_RPN_LSB,
252 0x0, // bend range
253 };
254
255 const uint8_t msb [3] = {
256 LV2_MIDI_MSG_CONTROLLER | voice_ch,
257 LV2_MIDI_CTL_RPN_MSB,
258 0x0,
259 };
260 const uint8_t dat [3] = {
261 LV2_MIDI_MSG_CONTROLLER | voice_ch,
262 LV2_MIDI_CTL_MSB_DATA_ENTRY,
263 zone->voice_range
264 };
265
266 fref = _midi_event(handle, frames, lsb, 3);
267 if(fref)
268 fref = _midi_event(handle, frames, msb, 3);
269 if(fref)
270 fref = _midi_event(handle, frames, dat, 3);
271
272 return fref;
273 }
274
275 static inline LV2_Atom_Forge_Ref
_full_update(plughandle_t * handle,int64_t frames)276 _full_update(plughandle_t *handle, int64_t frames)
277 {
278 LV2_Atom_Forge_Ref fref = 1;
279 mpe_t *mpe = &handle->mpe;
280
281 for(unsigned z=0; z<mpe->n_zones; z++)
282 {
283 if(fref)
284 fref = _zone_span_update(handle, frames, z);
285 if(fref)
286 fref = _master_range_update(handle, frames, z);
287 if(fref)
288 fref = _voice_range_update(handle, frames, z);
289 }
290
291 return fref;
292 }
293
294 static void
_intercept_zones(void * data,int64_t frames,props_impl_t * impl)295 _intercept_zones(void *data, int64_t frames, props_impl_t *impl)
296 {
297 plughandle_t *handle = data;
298
299 mpe_populate(&handle->mpe, handle->state.zones);
300 if(handle->ref)
301 handle->ref = _full_update(handle, frames);
302 }
303
304 static void
_intercept_master(void * data,int64_t frames,props_impl_t * impl)305 _intercept_master(void *data, int64_t frames, props_impl_t *impl)
306 {
307 plughandle_t *handle = data;
308
309 const int zone_idx = (int32_t *)impl->value.body - handle->state.master_range; //TODO check
310 handle->mpe.zones[zone_idx].master_range = handle->state.master_range[zone_idx];
311
312 if( (zone_idx < handle->state.zones) && handle->ref) // update active zones only
313 handle->ref = _master_range_update(handle, frames, zone_idx);
314 }
315
316 static void
_intercept_voice(void * data,int64_t frames,props_impl_t * impl)317 _intercept_voice(void *data, int64_t frames, props_impl_t *impl)
318 {
319 plughandle_t *handle = data;
320
321 const int zone_idx = (int32_t *)impl->value.body - handle->state.voice_range; //TODO check
322 handle->mpe.zones[zone_idx].voice_range = handle->state.voice_range[zone_idx];
323
324 if( (zone_idx < handle->state.zones) && handle->ref) // update active zones only
325 handle->ref = _voice_range_update(handle, frames, zone_idx);
326 }
327
328 #define MASTER_RANGE(NUM) \
329 { \
330 .property = ESPRESSIVO_URI"#mpe_master_range_"#NUM, \
331 .offset = offsetof(plugstate_t, master_range) + (NUM-1)*sizeof(int32_t), \
332 .type = LV2_ATOM__Int, \
333 .event_cb = _intercept_master \
334 }
335
336 #define VOICE_RANGE(NUM) \
337 { \
338 .property = ESPRESSIVO_URI"#mpe_voice_range_"#NUM, \
339 .offset = offsetof(plugstate_t, voice_range) + (NUM-1)*sizeof(int32_t), \
340 .type = LV2_ATOM__Int, \
341 .event_cb = _intercept_voice \
342 }
343
344 #define PRESSURE_CONTROLLER(NUM) \
345 { \
346 .property = ESPRESSIVO_URI"#mpe_pressure_controller_"#NUM, \
347 .offset = offsetof(plugstate_t, pressure_controller) + (NUM-1)*sizeof(int32_t), \
348 .type = LV2_ATOM__Int, \
349 }
350
351 #define TIMBRE_CONTROLLER(NUM) \
352 { \
353 .property = ESPRESSIVO_URI"#mpe_timbre_controller_"#NUM, \
354 .offset = offsetof(plugstate_t, timbre_controller) + (NUM-1)*sizeof(int32_t), \
355 .type = LV2_ATOM__Int, \
356 }
357
358 static const props_def_t defs [MAX_NPROPS] = {
359 {
360 .property = ESPRESSIVO_URI"#mpe_zones",
361 .offset = offsetof(plugstate_t, zones),
362 .type = LV2_ATOM__Int,
363 .event_cb = _intercept_zones
364 },
365 {
366 .property = ESPRESSIVO_URI"#mpe_velocity",
367 .offset = offsetof(plugstate_t, velocity),
368 .type = LV2_ATOM__Int
369 },
370
371 MASTER_RANGE(1),
372 MASTER_RANGE(2),
373 MASTER_RANGE(3),
374 MASTER_RANGE(4),
375 MASTER_RANGE(5),
376 MASTER_RANGE(6),
377 MASTER_RANGE(7),
378 MASTER_RANGE(8),
379
380 VOICE_RANGE(1),
381 VOICE_RANGE(2),
382 VOICE_RANGE(3),
383 VOICE_RANGE(4),
384 VOICE_RANGE(5),
385 VOICE_RANGE(6),
386 VOICE_RANGE(7),
387 VOICE_RANGE(8),
388
389 PRESSURE_CONTROLLER(1),
390 PRESSURE_CONTROLLER(2),
391 PRESSURE_CONTROLLER(3),
392 PRESSURE_CONTROLLER(4),
393 PRESSURE_CONTROLLER(5),
394 PRESSURE_CONTROLLER(6),
395 PRESSURE_CONTROLLER(7),
396 PRESSURE_CONTROLLER(8),
397
398 TIMBRE_CONTROLLER(1),
399 TIMBRE_CONTROLLER(2),
400 TIMBRE_CONTROLLER(3),
401 TIMBRE_CONTROLLER(4),
402 TIMBRE_CONTROLLER(5),
403 TIMBRE_CONTROLLER(6),
404 TIMBRE_CONTROLLER(7),
405 TIMBRE_CONTROLLER(8)
406 };
407
408 static inline bool
_notes_enabled(plughandle_t * handle,targetI_t * src)409 _notes_enabled(plughandle_t *handle, targetI_t *src)
410 {
411 if(handle->state.voice_range[src->zone] >= 0)
412 return true;
413
414 return false;
415 }
416
417 static inline void
_upd(plughandle_t * handle,int64_t frames,const xpress_state_t * state,float val,targetI_t * src)418 _upd(plughandle_t *handle, int64_t frames, const xpress_state_t *state,
419 float val, targetI_t *src)
420 {
421 // bender
422 if(_notes_enabled(handle, src))
423 {
424 const uint16_t bnd = (val - src->key) * mpe_range_1(&handle->mpe, state->zone) * 0x2000 + 0x1fff;
425 const uint8_t bnd_msb = bnd >> 7;
426 const uint8_t bnd_lsb = bnd & 0x7f;
427
428 const uint8_t bend [3] = {
429 LV2_MIDI_MSG_BENDER | src->chan,
430 bnd_lsb,
431 bnd_msb
432 };
433
434 if(handle->ref)
435 handle->ref = _midi_event(handle, frames, bend, 3);
436 }
437
438 // pressure
439 if(handle->state.pressure_controller[state->zone] >= 0)
440 {
441 const uint16_t z = state->pressure * 0x3fff;
442 const uint8_t z_msb = z >> 7;
443 const uint8_t z_lsb = z & 0x7f;
444
445 const uint8_t pressure_lsb [3] = {
446 LV2_MIDI_MSG_CONTROLLER | src->chan,
447 handle->state.pressure_controller[state->zone] | 0x20,
448 z_lsb
449 };
450
451 const uint8_t pressure_msb [3] = {
452 LV2_MIDI_MSG_CONTROLLER | src->chan,
453 handle->state.pressure_controller[state->zone],
454 z_msb
455 };
456
457 if(handle->ref)
458 handle->ref = _midi_event(handle, frames, pressure_lsb, 3);
459 if(handle->ref)
460 handle->ref = _midi_event(handle, frames, pressure_msb, 3);
461 }
462
463 // timbre
464 if(handle->state.timbre_controller[state->zone] >= 0)
465 {
466 float pos2 = state->timbre;
467 if(pos2 < -1.f) pos2 = -1.f;
468 else if(pos2 > 1.f) pos2 = 1.f;
469 const uint16_t vx = (pos2 * 0x2000) + 0x1fff;
470 const uint8_t vx_msb = vx >> 7;
471 const uint8_t vx_lsb = vx & 0x7f;
472
473 const uint8_t timbre_lsb [3] = {
474 LV2_MIDI_MSG_CONTROLLER | src->chan,
475 handle->state.timbre_controller[state->zone] | 0x20,
476 vx_lsb
477 };
478
479 const uint8_t timbre_msb [3] = {
480 LV2_MIDI_MSG_CONTROLLER | src->chan,
481 handle->state.timbre_controller[state->zone],
482 vx_msb
483 };
484
485 if(handle->ref)
486 handle->ref = _midi_event(handle, frames, timbre_lsb, 3);
487 if(handle->ref)
488 handle->ref = _midi_event(handle, frames, timbre_msb, 3);
489 }
490
491 // FIXME dPitch, dPressure, dTimbre
492 }
493
494 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)495 _add(void *data, int64_t frames, const xpress_state_t *state,
496 xpress_uuid_t uuid, void *target)
497 {
498 plughandle_t *handle = data;
499 targetI_t *src = target;
500
501 const float val = state->pitch * 0x7f;
502
503 src->chan = mpe_acquire(&handle->mpe, state->zone);
504 src->zone = state->zone;
505 src->key = floor(val);
506
507 if(_notes_enabled(handle, src))
508 {
509 const uint8_t vel = handle->state.velocity;
510
511 const uint8_t note_on [3] = {
512 LV2_MIDI_MSG_NOTE_ON | src->chan,
513 src->key,
514 vel
515 };
516
517 if(handle->ref)
518 handle->ref = _midi_event(handle, frames, note_on, 3);
519 }
520
521 _upd(handle, frames, state, val, src);
522 }
523
524 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)525 _set(void *data, int64_t frames, const xpress_state_t *state,
526 xpress_uuid_t uuid, void *target)
527 {
528 plughandle_t *handle = data;
529 targetI_t *src = target;
530
531 const float val = state->pitch * 0x7f;
532
533 _upd(handle, frames, state, val, src);
534 }
535
536 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)537 _del(void *data, int64_t frames,
538 xpress_uuid_t uuid, void *target)
539 {
540 plughandle_t *handle = data;
541 targetI_t *src = target;
542
543 if(_notes_enabled(handle, src))
544 {
545 const uint8_t vel = 0x0;
546
547 const uint8_t note_off [3] = {
548 LV2_MIDI_MSG_NOTE_OFF | src->chan,
549 src->key,
550 vel
551 };
552
553 if(handle->ref)
554 handle->ref = _midi_event(handle, frames, note_off, 3);
555 }
556
557 mpe_release(&handle->mpe, src->zone, src->chan);
558 }
559
560 static const xpress_iface_t ifaceI = {
561 .size = sizeof(targetI_t),
562
563 .add = _add,
564 .set = _set,
565 .del = _del
566 };
567
568 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)569 instantiate(const LV2_Descriptor* descriptor, double rate,
570 const char *bundle_path, const LV2_Feature *const *features)
571 {
572 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
573 if(!handle)
574 return NULL;
575
576 xpress_map_t *voice_map = NULL;
577
578 for(unsigned i=0; features[i]; i++)
579 {
580 if(!strcmp(features[i]->URI, LV2_URID__map))
581 handle->map = features[i]->data;
582 else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
583 voice_map = features[i]->data;
584 }
585
586 if(!handle->map)
587 {
588 fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
589 free(handle);
590 return NULL;
591 }
592
593 handle->uris.midi_MidiEvent = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
594
595 lv2_atom_forge_init(&handle->forge, handle->map);
596
597 if(!xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
598 XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle))
599 {
600 free(handle);
601 return NULL;
602 }
603
604 if(!props_init(&handle->props, descriptor->URI,
605 defs, MAX_NPROPS, &handle->state, &handle->stash,
606 handle->map, handle))
607 {
608 free(handle);
609 return NULL;
610 }
611
612 return handle;
613 }
614
615 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)616 connect_port(LV2_Handle instance, uint32_t port, void *data)
617 {
618 plughandle_t *handle = instance;
619
620 switch(port)
621 {
622 case 0:
623 handle->event_in = (const LV2_Atom_Sequence *)data;
624 break;
625 case 1:
626 handle->midi_out = (LV2_Atom_Sequence *)data;
627 break;
628 default:
629 break;
630 }
631 }
632
633 static void
activate(LV2_Handle instance)634 activate(LV2_Handle instance)
635 {
636 plughandle_t *handle = instance;
637
638 const uint8_t n_zones = 1;
639 mpe_populate(&handle->mpe, n_zones);
640 }
641
642 static void
run(LV2_Handle instance,uint32_t nsamples)643 run(LV2_Handle instance, uint32_t nsamples)
644 {
645 plughandle_t *handle = instance;
646
647 // prepare midi atom forge
648 const uint32_t capacity = handle->midi_out->atom.size;
649 LV2_Atom_Forge *forge = &handle->forge;
650 lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->midi_out, capacity);
651 LV2_Atom_Forge_Frame frame;
652 handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
653
654 props_idle(&handle->props, forge, 0, &handle->ref);
655 xpress_pre(&handle->xpressI);
656
657 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
658 {
659 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
660 const int64_t frames = ev->time.frames;
661
662 if(!props_advance(&handle->props, forge, frames, obj, &handle->ref))
663 {
664 xpress_advance(&handle->xpressI, forge, frames, obj, &handle->ref);
665 }
666 }
667
668 xpress_post(&handle->xpressI, nsamples-1);
669
670 if(handle->ref)
671 lv2_atom_forge_pop(forge, &frame);
672 else
673 lv2_atom_sequence_clear(handle->midi_out);
674 }
675
676 static void
cleanup(LV2_Handle instance)677 cleanup(LV2_Handle instance)
678 {
679 plughandle_t *handle = instance;
680
681 if(handle)
682 {
683 xpress_deinit(&handle->xpressI);
684 free(handle);
685 }
686 }
687
688 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)689 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
690 LV2_State_Handle state, uint32_t flags,
691 const LV2_Feature *const *features)
692 {
693 plughandle_t *handle = instance;
694
695 return props_save(&handle->props, store, state, flags, features);
696 }
697
698 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)699 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
700 LV2_State_Handle state, uint32_t flags,
701 const LV2_Feature *const *features)
702 {
703 plughandle_t *handle = instance;
704
705 return props_restore(&handle->props, retrieve, state, flags, features);
706 }
707
708 static const LV2_State_Interface state_iface = {
709 .save = _state_save,
710 .restore = _state_restore
711 };
712
713 static const void *
extension_data(const char * uri)714 extension_data(const char *uri)
715 {
716 if(!strcmp(uri, LV2_STATE__interface))
717 return &state_iface;
718 return NULL;
719 }
720
721 const LV2_Descriptor mpe_out = {
722 .URI = ESPRESSIVO_MPE_OUT_URI,
723 .instantiate = instantiate,
724 .connect_port = connect_port,
725 .activate = activate,
726 .run = run,
727 .deactivate = NULL,
728 .cleanup = cleanup,
729 .extension_data = extension_data
730 };
731