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 <inttypes.h>
21 #include <math.h>
22
23 #include <espressivo.h>
24 #include <props.h>
25
26 #include <mpe.h>
27
28 #define MAX_NPROPS (1 + MPE_ZONE_MAX*2)
29 #define MAX_ZONES 8
30 #define MAX_CHANNELS 16
31
32 typedef struct _slot_t slot_t;
33 typedef struct _targetO_t targetO_t;
34 typedef struct _plugstate_t plugstate_t;
35 typedef struct _plughandle_t plughandle_t;
36
37 struct _slot_t {
38 uint8_t zone;
39 uint8_t master_channel;
40 uint8_t num_voices;
41
42 uint8_t master_bend_range;
43 uint8_t voice_bend_range;
44
45 int16_t master_bender;
46
47 int16_t voice_bender [MAX_CHANNELS - 1];
48 int16_t voice_pressure [MAX_CHANNELS - 1];
49 int16_t voice_timbre [ MAX_CHANNELS - 1];
50 };
51
52 struct _targetO_t {
53 uint8_t key;
54 unsigned voice;
55 xpress_uuid_t uuid;
56 xpress_state_t state;
57 };
58
59 struct _plugstate_t {
60 int32_t num_zones;
61 int32_t master_range [MPE_ZONE_MAX];
62 int32_t voice_range [MPE_ZONE_MAX];
63 };
64
65 struct _plughandle_t {
66 LV2_URID_Map *map;
67 struct {
68 LV2_URID midi_MidiEvent;
69 } uris;
70 LV2_Atom_Forge forge;
71 LV2_Atom_Forge_Ref ref;
72
73 const LV2_Atom_Sequence *midi_in;
74 LV2_Atom_Sequence *event_out;
75
76 PROPS_T(props, MAX_NPROPS);
77
78 XPRESS_T(xpressO, MAX_NVOICES);
79 targetO_t targetO [MAX_NVOICES];
80
81 uint16_t data;
82 uint16_t rpn;
83
84 slot_t slots [MAX_ZONES];
85 slot_t *index [MAX_CHANNELS];
86
87 plugstate_t state;
88 plugstate_t stash;
89 struct {
90 LV2_URID num_zones;
91 LV2_URID master_range [MPE_ZONE_MAX];
92 LV2_URID voice_range [MPE_ZONE_MAX];
93 } urid;
94 };
95
96 static const targetO_t targetO_vanilla;
97
98 #define MASTER_RANGE(NUM) \
99 { \
100 .property = ESPRESSIVO_URI"#mpe_master_range_"#NUM, \
101 .offset = offsetof(plugstate_t, master_range) + (NUM-1)*sizeof(int32_t), \
102 .access = LV2_PATCH__readable, \
103 .type = LV2_ATOM__Int, \
104 }
105
106 #define VOICE_RANGE(NUM) \
107 { \
108 .property = ESPRESSIVO_URI"#mpe_voice_range_"#NUM, \
109 .offset = offsetof(plugstate_t, voice_range) + (NUM-1)*sizeof(int32_t), \
110 .access = LV2_PATCH__readable, \
111 .type = LV2_ATOM__Int, \
112 }
113
114 static const props_def_t defs [MAX_NPROPS] = {
115 {
116 .property = ESPRESSIVO_URI"#mpe_zones",
117 .offset = offsetof(plugstate_t, num_zones),
118 .access = LV2_PATCH__readable,
119 .type = LV2_ATOM__Int,
120 },
121
122 MASTER_RANGE(1),
123 MASTER_RANGE(2),
124 MASTER_RANGE(3),
125 MASTER_RANGE(4),
126 MASTER_RANGE(5),
127 MASTER_RANGE(6),
128 MASTER_RANGE(7),
129 MASTER_RANGE(8),
130
131 VOICE_RANGE(1),
132 VOICE_RANGE(2),
133 VOICE_RANGE(3),
134 VOICE_RANGE(4),
135 VOICE_RANGE(5),
136 VOICE_RANGE(6),
137 VOICE_RANGE(7),
138 VOICE_RANGE(8)
139 };
140
141
142 static inline void
_slot_init(slot_t * slot,uint8_t zone,uint8_t master_channel,uint8_t num_voices)143 _slot_init(slot_t *slot, uint8_t zone, uint8_t master_channel, uint8_t num_voices)
144 {
145 slot->zone = zone;
146 slot->master_channel = master_channel;
147 slot->num_voices = num_voices;
148
149 slot->master_bend_range = 2;
150 slot->voice_bend_range = 48;
151
152 slot->master_bender = 0;
153
154 const int16_t empty [MAX_CHANNELS - 1] = {
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
156 };
157 memcpy(slot->voice_bender, empty, sizeof(empty));
158 memcpy(slot->voice_pressure, empty, sizeof(empty));
159 memcpy(slot->voice_timbre, empty, sizeof(empty));
160 }
161
162 static inline void
_slots_init(plughandle_t * handle)163 _slots_init(plughandle_t *handle)
164 {
165 _slot_init(&handle->slots[0], 0, 0, 15);
166 for(unsigned i=1; i<MAX_ZONES; i++)
167 _slot_init(&handle->slots[i], i, UINT8_MAX, 0);
168 }
169
170 static inline void
_index_update(plughandle_t * handle)171 _index_update(plughandle_t *handle)
172 {
173 memset(handle->index, 0x0, sizeof(handle->index));
174
175 for(unsigned i=0; i<MAX_ZONES; i++)
176 {
177 slot_t *slot = &handle->slots[i];
178
179 if(slot->num_voices == 0)
180 continue; // invalid
181
182 for(unsigned j = slot->master_channel;
183 j <= slot->master_channel + slot->num_voices;
184 j++)
185 {
186 handle->index[j] = slot;
187 }
188 }
189 }
190
191 static inline bool
_slot_is_master(slot_t * slot,uint8_t chan)192 _slot_is_master(slot_t *slot, uint8_t chan)
193 {
194 return slot->master_channel == chan;
195 }
196
197 static inline bool
_slot_is_first(slot_t * slot,uint8_t chan)198 _slot_is_first(slot_t *slot, uint8_t chan)
199 {
200 return slot->master_channel + 1 == chan;
201 }
202
203 static inline bool
_slot_is_voice(slot_t * slot,uint8_t chan)204 _slot_is_voice(slot_t *slot, uint8_t chan)
205 {
206 return (chan >= slot->master_channel + 1)
207 && (chan <= slot->master_channel + slot->num_voices);
208 }
209
210 static inline unsigned
_slot_voice(slot_t * slot,uint8_t chan)211 _slot_voice(slot_t *slot, uint8_t chan)
212 {
213 return chan - slot->master_channel - 1;
214 }
215
216 #if 0
217 static inline void
218 _zone_dump(plughandle_t *handle)
219 {
220 for(unsigned i=0; i<MAX_ZONES; i++)
221 {
222 slot_t *slot = &handle->slots[i];
223
224 if(slot->num_voices == 0)
225 continue;
226
227 printf("%u: %"PRIu8" %"PRIu8" %"PRIu8"\n", i, slot->zone, slot->master_channel, slot->num_voices);
228 }
229 printf("\n");
230 }
231 #endif
232
233 static inline void
_zone_notify(plughandle_t * handle,int64_t frames)234 _zone_notify(plughandle_t *handle, int64_t frames)
235 {
236 handle->state.num_zones = 0;
237
238 for(unsigned i=0; i<MAX_ZONES; i++)
239 {
240 slot_t *slot = &handle->slots[i];
241
242 //printf("%u: %u %i %i\n", i, slot->num_voices, slot->master_bend_range, slot->voice_bend_range);
243
244 handle->state.master_range[i] = slot->master_bend_range;
245 props_set(&handle->props, &handle->forge, frames, handle->urid.master_range[i], &handle->ref);
246
247 handle->state.voice_range[i] = slot->voice_bend_range;
248 props_set(&handle->props, &handle->forge, frames, handle->urid.voice_range[i], &handle->ref);
249
250 if(slot->num_voices == 0)
251 continue; // invalid
252
253 handle->state.num_zones += 1;
254 }
255
256 props_set(&handle->props, &handle->forge, frames, handle->urid.num_zones, &handle->ref);
257 }
258
259 static inline void
_zone_register(plughandle_t * handle,int64_t frames,uint8_t master_channel,uint8_t num_voices)260 _zone_register(plughandle_t *handle, int64_t frames, uint8_t master_channel, uint8_t num_voices)
261 {
262 if( (num_voices < 1) || (master_channel + num_voices > MAX_CHANNELS) )
263 return; // invalid
264
265 // make copy of current zone layout
266 slot_t stash [MAX_ZONES];
267 memcpy(stash, handle->slots, sizeof(stash));
268
269 const uint8_t master_end = master_channel + num_voices;
270
271 // pass 1
272 for(unsigned i=0; i<MAX_ZONES; i++)
273 {
274 slot_t *slot = &stash[i];
275
276 // invalid?
277 if(slot->num_voices == 0)
278 continue;
279
280 // shorten zone?
281 if( (slot->master_channel < master_channel) && (slot->master_channel + slot->num_voices >= master_channel) )
282 {
283 slot->num_voices = master_channel - 1 - slot->master_channel;
284 continue;
285 }
286
287 // overwrite master?
288 if( (slot->master_channel >= master_channel) && (slot->master_channel <= master_end) )
289 {
290 slot->num_voices = 0; // invalidate
291 continue;
292 }
293
294 // overwrite first voice?
295 if( (slot->master_channel + 1 >= master_channel) && (slot->master_channel + 1 <= master_end) )
296 {
297 slot->num_voices = 0; // invalidate
298 continue;
299 }
300 }
301
302 // clear slots
303 _slots_init(handle);
304
305 // pass 2
306 bool inserted = false;
307 slot_t *dst = handle->slots;
308 unsigned I = 0;
309 for(unsigned i=0; i<MAX_ZONES; i++)
310 {
311 slot_t *slot = &stash[i];
312
313 if(slot->num_voices == 0)
314 {
315 continue;
316 }
317
318 if(!inserted && (slot->master_channel > master_end) )
319 {
320 // insert new zone
321 _slot_init(dst, i, master_channel, num_voices);
322 dst++;
323
324 inserted = true;
325 }
326
327 // clone zone
328 memcpy(dst, slot, sizeof(slot_t));
329 dst->zone = i;
330 dst++;
331
332 I = i + 1;
333 }
334
335 if(!inserted)
336 {
337 // insert new zone at end
338 _slot_init(dst, I++, master_channel, num_voices);
339 dst++;
340 }
341
342 _index_update(handle);
343 }
344
345 static const xpress_iface_t ifaceO = {
346 .size = sizeof(targetO_t)
347 };
348
349 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)350 instantiate(const LV2_Descriptor* descriptor, double rate,
351 const char *bundle_path, const LV2_Feature *const *features)
352 {
353 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
354 if(!handle)
355 return NULL;
356
357 xpress_map_t *voice_map = NULL;
358
359 for(unsigned i=0; features[i]; i++)
360 {
361 if(!strcmp(features[i]->URI, LV2_URID__map))
362 handle->map = features[i]->data;
363 else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
364 voice_map = features[i]->data;
365 }
366
367 if(!handle->map)
368 {
369 fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
370 free(handle);
371 return NULL;
372 }
373
374 handle->uris.midi_MidiEvent = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
375
376 lv2_atom_forge_init(&handle->forge, handle->map);
377
378 if( !xpress_init(&handle->xpressO, MAX_NVOICES, handle->map, voice_map,
379 XPRESS_EVENT_NONE, &ifaceO, handle->targetO, handle) )
380 {
381 free(handle);
382 return NULL;
383 }
384
385 if(!props_init(&handle->props, descriptor->URI,
386 defs, MAX_NPROPS, &handle->state, &handle->stash,
387 handle->map, handle))
388 {
389 fprintf(stderr, "failed to allocate property structure\n");
390 free(handle);
391 return NULL;
392 }
393
394 unsigned p = 0;
395 handle->urid.num_zones = props_map(&handle->props, defs[p++].property);
396 handle->state.num_zones = 1;
397
398 for(unsigned z=0; z<MPE_ZONE_MAX; z++)
399 {
400 handle->urid.master_range[z] = props_map(&handle->props, defs[p++].property);
401 handle->state.master_range[z] = 2;
402 }
403 for(unsigned z=0; z<MPE_ZONE_MAX; z++)
404 {
405 handle->urid.voice_range[z] = props_map(&handle->props, defs[p++].property);
406 handle->state.voice_range[z] = 48;
407 }
408
409 _slots_init(handle);
410 _index_update(handle);
411
412 return handle;
413 }
414
415 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)416 connect_port(LV2_Handle instance, uint32_t port, void *data)
417 {
418 plughandle_t *handle = instance;
419
420 switch(port)
421 {
422 case 0:
423 handle->midi_in = (const LV2_Atom_Sequence *)data;
424 break;
425 case 1:
426 handle->event_out = (LV2_Atom_Sequence *)data;
427 break;
428 default:
429 break;
430 }
431 }
432
433 static inline xpress_uuid_t
_uuid_create(slot_t * slot,uint8_t chan)434 _uuid_create(slot_t *slot, uint8_t chan)
435 {
436 return ((int32_t)slot->zone << 8) | chan;
437 }
438
439 static inline bool
_mpe_in(plughandle_t * handle,int64_t frames,const LV2_Atom * atom)440 _mpe_in(plughandle_t *handle, int64_t frames, const LV2_Atom *atom)
441 {
442 LV2_Atom_Forge *forge = &handle->forge;
443 const uint8_t *m = LV2_ATOM_BODY_CONST(atom);
444 const uint8_t comm = m[0] & 0xf0;
445 const uint8_t chan = m[0] & 0x0f;
446
447 bool zone_notify = false;
448
449 switch(comm)
450 {
451 case LV2_MIDI_MSG_NOTE_ON:
452 {
453 slot_t *slot = handle->index[chan];
454
455 if(slot && _slot_is_voice(slot, chan))
456 {
457 const unsigned voice = _slot_voice(slot, chan);
458 const uint8_t key = m[1];
459 const xpress_uuid_t uuid = _uuid_create(slot, chan);
460
461 targetO_t *target = xpress_add(&handle->xpressO, uuid);
462 if(target)
463 {
464 *target = targetO_vanilla;
465 target->key = key;
466 target->voice = voice;
467 target->uuid = xpress_map(&handle->xpressO);
468
469 const float offset_master = slot->master_bender * 0x1p-13 * slot->master_bend_range;
470 const float offset_voice = slot->voice_bender[voice] * 0x1p-13 * slot->voice_bend_range;
471 target->state.zone = slot->zone;
472 target->state.pitch = ((float)target->key + offset_master + offset_voice) / 0x7f;
473
474 if(handle->ref)
475 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
476 }
477 }
478
479 break;
480 }
481 case LV2_MIDI_MSG_NOTE_OFF:
482 {
483 slot_t *slot = handle->index[chan];
484
485 if(slot && _slot_is_voice(slot, chan))
486 {
487 const xpress_uuid_t uuid = _uuid_create(slot, chan);
488
489 targetO_t *target = xpress_get(&handle->xpressO, uuid);
490 if(target)
491 {
492 #if 0
493 if(handle->ref)
494 handle->ref = xpress_del(&handle->xpressO, forge, frames, target->uuid);
495 #endif
496 }
497
498 xpress_free(&handle->xpressO, uuid);
499 }
500
501 break;
502 }
503 case LV2_MIDI_MSG_CHANNEL_PRESSURE:
504 {
505 slot_t *slot = handle->index[chan];
506
507 if(slot && _slot_is_voice(slot, chan))
508 {
509 const xpress_uuid_t uuid = _uuid_create(slot, chan);
510 const unsigned voice = _slot_voice(slot, chan);
511
512 slot->voice_pressure[voice] = m[1] << 7;
513
514 targetO_t *target = xpress_get(&handle->xpressO, uuid);
515 if(target)
516 {
517 const float pressure = slot->voice_pressure[voice] / 0x3fff;
518 target->state.pressure = pressure;
519
520 if(handle->ref)
521 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
522 }
523 }
524
525 break;
526 }
527 case LV2_MIDI_MSG_BENDER:
528 {
529 slot_t *slot = handle->index[chan];
530
531 if(slot)
532 {
533 const int16_t bender = (((int16_t)m[2] << 7) | m[1]) - 0x2000;
534
535 if(_slot_is_master(slot, chan))
536 {
537 slot->master_bender = bender;
538
539 XPRESS_VOICE_FOREACH(&handle->xpressO, voice)
540 {
541 targetO_t *target = voice->target;
542
543 if(target->state.zone != slot->zone)
544 continue;
545
546 const float offset_master = slot->master_bender * 0x1p-13 * slot->master_bend_range;
547 const float offset_voice = slot->voice_bender[target->voice] * 0x1p-13 * slot->voice_bend_range;
548 target->state.pitch = ((float)target->key + offset_master + offset_voice) / 0x7f;
549
550 if(handle->ref)
551 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
552 }
553 }
554 else if(_slot_is_voice(slot, chan))
555 {
556 const xpress_uuid_t uuid = _uuid_create(slot, chan);
557 const unsigned voice = _slot_voice(slot, chan);
558
559 slot->voice_bender[voice] = bender;
560
561 targetO_t *target = xpress_get(&handle->xpressO, uuid);
562 if(target)
563 {
564 const float offset_master = slot->master_bender * 0x1p-13 * slot->master_bend_range;
565 const float offset_voice = slot->voice_bender[voice] * 0x1p-13 * slot->voice_bend_range;
566 target->state.pitch = ((float)target->key + offset_master + offset_voice) / 0x7f;
567
568 if(handle->ref)
569 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
570 }
571 }
572 }
573
574 break;
575 }
576 case LV2_MIDI_MSG_CONTROLLER:
577 {
578 const uint8_t controller = m[1];
579 const uint8_t value = m[2];
580
581 switch(controller)
582 {
583 case LV2_MIDI_CTL_LSB_DATA_ENTRY:
584 {
585 handle->data = (handle->data & 0x3f80) | value;
586
587 break;
588 }
589 case LV2_MIDI_CTL_MSB_DATA_ENTRY:
590 {
591 handle->data = (handle->data & 0x7f) | (value << 7);
592
593 if(handle->rpn == 6) // new MPE zone registered
594 {
595 const uint8_t zone_width = handle->data >> 7;
596
597 _zone_register(handle, frames, chan, zone_width);
598 zone_notify = true;
599 }
600 else if(handle->rpn == 0) // pitch-bend range registered
601 {
602 const uint8_t bend_range = handle->data >> 7;
603 slot_t *slot = handle->index[chan];
604
605 if(slot)
606 {
607 if(_slot_is_master(slot, chan))
608 {
609 slot->master_bend_range = bend_range;
610
611 handle->state.master_range[slot->zone] = slot->master_bend_range;
612 props_set(&handle->props, &handle->forge, frames, handle->urid.master_range[slot->zone], &handle->ref);
613 }
614 else if(_slot_is_first(slot, chan))
615 {
616 slot->voice_bend_range = bend_range;
617
618 handle->state.voice_range[slot->zone] = slot->voice_bend_range;
619 props_set(&handle->props, &handle->forge, frames, handle->urid.voice_range[slot->zone], &handle->ref);
620 }
621 }
622 }
623
624 break;
625 }
626
627 case LV2_MIDI_CTL_RPN_LSB:
628 {
629 handle->rpn = (handle->rpn & 0x3f80) | value;
630
631 break;
632 }
633 case LV2_MIDI_CTL_RPN_MSB:
634 {
635 handle->rpn = (handle->rpn & 0x7f) | ((uint16_t)value << 7);
636
637 break;
638 }
639
640 case LV2_MIDI_CTL_SC1_SOUND_VARIATION | 0x20:
641 {
642 slot_t *slot = handle->index[chan];
643
644 if(slot && _slot_is_voice(slot, chan))
645 {
646 const unsigned voice = _slot_voice(slot, chan);
647 slot->voice_pressure[voice] = (slot->voice_pressure[voice] & 0x3f80) | value;
648 }
649
650 break;
651 }
652 case LV2_MIDI_CTL_SC1_SOUND_VARIATION:
653 {
654 slot_t *slot = handle->index[chan];
655
656 if(slot && _slot_is_voice(slot, chan))
657 {
658 const unsigned voice = _slot_voice(slot, chan);
659 const xpress_uuid_t uuid = _uuid_create(slot, chan);
660
661 slot->voice_pressure[voice] = (slot->voice_pressure[voice] & 0x7f) | ((uint16_t)value << 7);
662
663 targetO_t *target = xpress_get(&handle->xpressO, uuid);
664 if(target)
665 {
666 const float pressure = slot->voice_pressure[voice] / 0x3fff;
667 target->state.pressure = pressure;
668
669 if(handle->ref)
670 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
671 }
672 }
673
674 break;
675 }
676
677 case LV2_MIDI_CTL_SC5_BRIGHTNESS | 0x20:
678 {
679 slot_t *slot = handle->index[chan];
680
681 if(slot && _slot_is_voice(slot, chan))
682 {
683 const unsigned voice = _slot_voice(slot, chan);
684 slot->voice_timbre[voice] = (slot->voice_timbre[voice] & 0x3f80) | value;
685 }
686
687 break;
688 }
689 case LV2_MIDI_CTL_SC5_BRIGHTNESS:
690 {
691 slot_t *slot = handle->index[chan];
692
693 if(slot && _slot_is_voice(slot, chan))
694 {
695 const unsigned voice = _slot_voice(slot, chan);
696 const xpress_uuid_t uuid = _uuid_create(slot, chan);
697
698 slot->voice_timbre[voice] = (slot->voice_timbre[voice] & 0x7f) | ((uint16_t)value << 7);
699
700 targetO_t *target = xpress_get(&handle->xpressO, uuid);
701 if(target)
702 {
703 const float timbre = slot->voice_timbre[voice] / 0x3fff;
704 target->state.timbre = timbre;
705
706 if(handle->ref)
707 handle->ref = xpress_token(&handle->xpressO, forge, frames, target->uuid, &target->state);
708 }
709 }
710
711 break;
712 }
713 }
714
715 break;
716 }
717 }
718
719 return zone_notify;
720 }
721
722 static void
run(LV2_Handle instance,uint32_t nsamples)723 run(LV2_Handle instance, uint32_t nsamples)
724 {
725 plughandle_t *handle = instance;
726
727 // prepare midi atom forge
728 const uint32_t capacity = handle->event_out->atom.size;
729 LV2_Atom_Forge *forge = &handle->forge;
730 lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
731 LV2_Atom_Forge_Frame frame;
732 handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
733
734 props_idle(&handle->props, forge, 0, &handle->ref);
735 xpress_rst(&handle->xpressO);
736
737 bool zone_notify = false;
738
739 LV2_ATOM_SEQUENCE_FOREACH(handle->midi_in, ev)
740 {
741 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
742 const int64_t frames = ev->time.frames;
743
744 if(obj->atom.type == handle->uris.midi_MidiEvent)
745 {
746 if(_mpe_in(handle, frames, &obj->atom))
747 zone_notify = true;
748 }
749 else
750 {
751 props_advance(&handle->props, forge, frames, obj, &handle->ref);
752 }
753 }
754
755 if(zone_notify)
756 _zone_notify(handle, nsamples - 1);
757
758 if(handle->ref && !xpress_synced(&handle->xpressO))
759 handle->ref = xpress_alive(&handle->xpressO, forge, nsamples-1);
760
761 if(handle->ref)
762 lv2_atom_forge_pop(forge, &frame);
763 else
764 lv2_atom_sequence_clear(handle->event_out);
765 }
766
767 static void
cleanup(LV2_Handle instance)768 cleanup(LV2_Handle instance)
769 {
770 plughandle_t *handle = instance;
771
772 if(handle)
773 {
774 xpress_deinit(&handle->xpressO);
775 free(handle);
776 }
777 }
778
779 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)780 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
781 LV2_State_Handle state, uint32_t flags,
782 const LV2_Feature *const *features)
783 {
784 plughandle_t *handle = instance;
785
786 return props_save(&handle->props, store, state, flags, features);
787 }
788
789 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)790 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
791 LV2_State_Handle state, uint32_t flags,
792 const LV2_Feature *const *features)
793 {
794 plughandle_t *handle = instance;
795
796 return props_restore(&handle->props, retrieve, state, flags, features);
797 }
798
799 static const LV2_State_Interface state_iface = {
800 .save = _state_save,
801 .restore = _state_restore
802 };
803
804 static const void *
extension_data(const char * uri)805 extension_data(const char *uri)
806 {
807 if(!strcmp(uri, LV2_STATE__interface))
808 return &state_iface;
809 return NULL;
810 }
811
812 const LV2_Descriptor mpe_in = {
813 .URI = ESPRESSIVO_MPE_IN_URI,
814 .instantiate = instantiate,
815 .connect_port = connect_port,
816 .activate = NULL,
817 .run = run,
818 .deactivate = NULL,
819 .cleanup = cleanup,
820 .extension_data = extension_data
821 };
822