1 /* avldrums -- simple & robust x-platform fluidsynth LV2
2 *
3 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
31 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
32 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
33
34 #include "midnam_lv2.h"
35
36 #include "fluidsynth.h"
37 #include "avldrums.h"
38
39 #ifdef _WIN32
40 #define PATH_SEP "\\"
41 #else
42 #define PATH_SEP "/"
43 #endif
44
45 enum {
46 CMD_APPLY = 0,
47 CMD_FREE = 1,
48 };
49
50 typedef struct {
51 /* ports */
52 const LV2_Atom_Sequence* control;
53 LV2_Atom_Sequence* notify;
54
55 float* p_ports[AVL_PORT_LAST];
56
57 /* fluid synth */
58 fluid_settings_t* settings;
59 fluid_synth_t* synth;
60 int synthId;
61
62 /* lv2 URIDs */
63 AVLLV2URIs uris;
64
65 /* lv2 extensions */
66 LV2_Log_Log* log;
67 LV2_Log_Logger logger;
68 LV2_Worker_Schedule* schedule;
69 LV2_Midnam* midnam;
70 LV2_Atom_Forge forge;
71 LV2_Atom_Forge_Frame frame;
72
73 /* state */
74 bool panic;
75 bool initialized;
76 bool multi_out;
77 bool inform_ui;
78 bool ui_active;
79
80 char current_sf2_file_path[1024];
81 char queue_sf2_file_path[1024];
82 bool reinit_in_progress; // set in run, cleared in work_response
83 bool queue_reinit; // set in restore, cleared in work_response
84
85 fluid_midi_event_t* fmidi_event;
86
87 } AVLSynth;
88
89 /* *****************************************************************************
90 * helpers
91 */
92
93 static bool
load_sf2(AVLSynth * self,const char * fn)94 load_sf2 (AVLSynth* self, const char* fn)
95 {
96 const int synth_id = fluid_synth_sfload (self->synth, fn, 1);
97
98 if (synth_id == FLUID_FAILED) {
99 return false;
100 }
101
102 fluid_sfont_t* const sfont = fluid_synth_get_sfont_by_id (self->synth, synth_id);
103 if (!sfont) {
104 return false;
105 }
106
107 int chn = 0;
108 fluid_preset_t* preset;
109 fluid_sfont_iteration_start (sfont);
110 if ((preset = fluid_sfont_iteration_next (sfont))) {
111 for (chn = 0; chn < 16; ++chn) {
112 fluid_synth_program_select (self->synth, chn, synth_id,
113 fluid_preset_get_banknum (preset), fluid_preset_get_num (preset));
114 }
115 }
116
117 if (chn == 0) {
118 return false;
119 }
120
121 if (self->multi_out) {
122 /* pan mono outs hard left */
123 fluid_midi_event_set_value (self->fmidi_event, 0);
124
125 for (uint8_t chn = 0; chn < 5; ++chn) {
126 fluid_midi_event_set_type (self->fmidi_event, 0xb0 | chn);
127 fluid_midi_event_set_key (self->fmidi_event, 0x0a);
128 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
129
130 fluid_midi_event_set_type (self->fmidi_event, 0xb0 | chn);
131 fluid_midi_event_set_key (self->fmidi_event, 0x2a);
132 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
133 }
134 }
135
136 return true;
137 }
138
139 static void
synth_sound(AVLSynth * self,uint32_t n_samples,uint32_t offset)140 synth_sound (AVLSynth* self, uint32_t n_samples, uint32_t offset)
141 {
142 if (self->multi_out) {
143 while (n_samples > 0) {
144 uint32_t n = n_samples > 8192 ? 8192 : n_samples;
145 float* out[14];
146
147 out[0] = &self->p_ports[AVL_PORT_OUT_Kick][offset];
148 out[1] = NULL;
149 out[2] = &self->p_ports[AVL_PORT_OUT_Snare][offset];
150 out[3] = NULL;
151 out[4] = &self->p_ports[AVL_PORT_OUT_HiHat][offset];
152 out[5] = NULL;
153 out[6] = &self->p_ports[AVL_PORT_OUT_Tom][offset];
154 out[7] = NULL;
155 out[8] = &self->p_ports[AVL_PORT_OUT_FloorTom][offset];
156 out[9] = NULL;
157 out[10] = &self->p_ports[AVL_PORT_OUT_Overheads_L][offset];
158 out[11] = &self->p_ports[AVL_PORT_OUT_Overheads_R][offset];
159 out[12] = &self->p_ports[AVL_PORT_OUT_Percussion_L][offset];
160 out[13] = &self->p_ports[AVL_PORT_OUT_Percussion_R][offset];
161
162 fluid_synth_process (self->synth, n, 0, NULL, 14, out);
163 n_samples -= n;
164 offset += n;
165 }
166 } else {
167 fluid_synth_write_float (
168 self->synth,
169 n_samples,
170 &self->p_ports[AVL_PORT_OUT_L][offset], 0, 1,
171 &self->p_ports[AVL_PORT_OUT_R][offset], 0, 1);
172 }
173 }
174
175 static uint8_t
assign_channel(uint8_t note)176 assign_channel (uint8_t note)
177 {
178 switch (note) {
179 case 36:
180 return 0; // Kick
181 case 37:
182 case 38:
183 case 40:
184 return 1; // Snare
185 case 42:
186 case 44:
187 case 46:
188 case 48:
189 return 2; // HiHat
190 case 45:
191 case 47:
192 return 3; // Tom
193 case 41:
194 case 43:
195 return 4; // Floor Tom
196 case 39: // Hand Clap
197 case 54: // Tambourine
198 case 56: // Cowbell
199 case 61: // Maracas
200 return 6; // Percussions etc
201 default:
202 break;
203 }
204 return 5; // Cymbals
205 }
206
207 static void
inform_ui(AVLSynth * self)208 inform_ui (AVLSynth* self)
209 {
210 LV2_Atom_Forge_Frame frame;
211 lv2_atom_forge_frame_time (&self->forge, 0);
212 x_forge_object (&self->forge, &frame, 1, self->uris.drumkit);
213 lv2_atom_forge_property_head (&self->forge, self->uris.loaded, 0);
214 lv2_atom_forge_bool (&self->forge, strlen (self->current_sf2_file_path) > 0);
215 lv2_atom_forge_pop (&self->forge, &frame);
216 }
217
218 static void
kick_ui(AVLSynth * self,int * n)219 kick_ui (AVLSynth* self, int* n)
220 {
221 LV2_Atom_Forge_Frame frame;
222 lv2_atom_forge_frame_time (&self->forge, 0);
223 x_forge_object (&self->forge, &frame, 1, self->uris.drumhit);
224 lv2_atom_forge_property_head (&self->forge, self->uris.drumhits, 0);
225 lv2_atom_forge_vector (&self->forge, sizeof(int32_t), self->uris.atom_Int, DRUM_PCS, n);
226 lv2_atom_forge_pop (&self->forge, &frame);
227 }
228
file_exists(const char * filename)229 static int file_exists (const char *filename) {
230 struct stat s;
231 if (!filename || strlen(filename) < 1) return 0;
232 int result= stat (filename, &s);
233 if (result != 0) return 0; /* stat() failed */
234 if (S_ISREG(s.st_mode)) return 1; /* is a regular file - ok */
235 return 0;
236 }
237
238 /* *****************************************************************************
239 * LV2 Plugin
240 */
241
242 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)243 instantiate (const LV2_Descriptor* descriptor,
244 double rate,
245 const char* bundle_path,
246 const LV2_Feature* const* features)
247 {
248 static const char* kits [] = {
249 "Black_Pearl_4_LV2.sf2",
250 "Red_Zeppelin_4_LV2.sf2"
251 };
252
253 int kit = -1;
254 bool multi_out = false;
255
256 if (!strcmp (descriptor->URI, AVL_URI "BlackPearl")) {
257 kit = 0;
258 } else if (!strcmp (descriptor->URI, AVL_URI "BlackPearlMulti")) {
259 kit = 0; multi_out = true;
260 } else if (!strcmp (descriptor->URI, AVL_URI "RedZeppelin")) {
261 kit = 1;
262 } else if (!strcmp (descriptor->URI, AVL_URI "RedZeppelinMulti")) {
263 kit = 1; multi_out = true;
264 }
265
266 AVLSynth* self = (AVLSynth*)calloc (1, sizeof (AVLSynth));
267
268 if (!self || !bundle_path || kit < 0) {
269 return NULL;
270 }
271
272 LV2_URID_Map* map = NULL;
273
274 for (int i=0; features[i] != NULL; ++i) {
275 if (!strcmp (features[i]->URI, LV2_URID__map)) {
276 map = (LV2_URID_Map*)features[i]->data;
277 } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
278 self->log = (LV2_Log_Log*)features[i]->data;
279 } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) {
280 self->schedule = (LV2_Worker_Schedule*)features[i]->data;
281 } else if (!strcmp (features[i]->URI, LV2_MIDNAM__update)) {
282 self->midnam = (LV2_Midnam*)features[i]->data;
283 }
284 }
285
286 lv2_log_logger_init (&self->logger, map, self->log);
287
288 if (!map) {
289 lv2_log_error (&self->logger, "avldrums.lv2: Host does not support urid:map\n");
290 free (self);
291 return NULL;
292 }
293
294 if (!self->schedule) {
295 lv2_log_error (&self->logger, "avldrums.lv2: Host does not support worker:schedule\n");
296 free (self);
297 return NULL;
298 }
299
300 snprintf (self->queue_sf2_file_path, sizeof (self->queue_sf2_file_path), "%s" PATH_SEP "%s",
301 bundle_path, kits[kit]);
302 self->queue_sf2_file_path[sizeof(self->queue_sf2_file_path) - 1] = '\0';
303
304 if (!file_exists (self->queue_sf2_file_path)) {
305 lv2_log_error (&self->logger, "avldrums.lv2: Cannot find drumkit soundfont: '%s'\n", self->queue_sf2_file_path);
306 free (self);
307 return NULL;
308 }
309
310 /* initialize fluid synth */
311 self->settings = new_fluid_settings ();
312
313 if (!self->settings) {
314 lv2_log_error (&self->logger, "avldrums.lv2: cannot allocate Fluid Settings\n");
315 free (self);
316 return NULL;
317 }
318
319 fluid_settings_setnum (self->settings, "synth.sample-rate", rate);
320 fluid_settings_setint (self->settings, "synth.threadsafe-api", 0);
321
322 if (multi_out) {
323 self->multi_out = true;
324 fluid_settings_setint (self->settings, "synth.audio-channels", 7);
325 fluid_settings_setint (self->settings, "synth.audio-groups", 7);
326 } else {
327 self->multi_out = false;
328 fluid_settings_setint (self->settings, "synth.audio-channels", 1); // stereo pairs
329 }
330
331 self->synth = new_fluid_synth (self->settings);
332
333 if (!self->synth) {
334 lv2_log_error (&self->logger, "avldrums.lv2: cannot allocate Fluid Synth\n");
335 delete_fluid_settings (self->settings);
336 free (self);
337 return NULL;
338 }
339
340 fluid_synth_set_gain (self->synth, 1.0f);
341 fluid_synth_set_polyphony (self->synth, DRUM_PCS);
342 fluid_synth_set_sample_rate (self->synth, (float)rate);
343
344 self->fmidi_event = new_fluid_midi_event ();
345
346 if (!self->fmidi_event) {
347 lv2_log_error (&self->logger, "avldrums.lv2: cannot allocate Fluid Event\n");
348 delete_fluid_synth (self->synth);
349 delete_fluid_settings (self->settings);
350 free (self);
351 return NULL;
352 }
353
354 /* initialize plugin state */
355
356 self->panic = false;
357 self->inform_ui = false;
358 self->ui_active = false;
359 self->initialized = false;
360 self->reinit_in_progress = false;
361 self->queue_reinit = true;
362
363 lv2_atom_forge_init (&self->forge, map);
364 map_avldrums_uris (map, &self->uris);
365
366 return (LV2_Handle)self;
367 }
368
369 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)370 connect_port (LV2_Handle instance,
371 uint32_t port,
372 void* data)
373 {
374 AVLSynth* self = (AVLSynth*)instance;
375
376 switch (port) {
377 case AVL_PORT_CONTROL:
378 self->control = (const LV2_Atom_Sequence*)data;
379 break;
380 case AVL_PORT_NOTIFY:
381 self->notify = (LV2_Atom_Sequence*)data;
382 break;
383 default:
384 if (port < AVL_PORT_LAST) {
385 self->p_ports[port] = (float*)data;
386 }
387 break;
388 }
389 }
390
391 static void
deactivate(LV2_Handle instance)392 deactivate (LV2_Handle instance)
393 {
394 AVLSynth* self = (AVLSynth*)instance;
395 self->panic = true;
396 }
397
398 static void
run(LV2_Handle instance,uint32_t n_samples)399 run (LV2_Handle instance, uint32_t n_samples)
400 {
401 AVLSynth* self = (AVLSynth*)instance;
402
403 if (!self->control || !self->notify) {
404 return;
405 }
406
407 const uint32_t capacity = self->notify->atom.size;
408 lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->notify, capacity);
409 lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
410
411 if (self->multi_out || !self->initialized || self->reinit_in_progress) {
412 memset (self->p_ports[AVL_PORT_OUT_L], 0, n_samples * sizeof (float));
413 memset (self->p_ports[AVL_PORT_OUT_R], 0, n_samples * sizeof (float));
414 if (self->multi_out) {
415 for (uint32_t cc = AVL_PORT_OUT_HiHat; cc < AVL_PORT_LAST; ++cc) {
416 memset (self->p_ports[cc], 0, n_samples * sizeof (float));
417 }
418 }
419 } else if (self->panic) {
420 fluid_synth_all_notes_off (self->synth, -1);
421 fluid_synth_all_sounds_off (self->synth, -1);
422 self->panic = false;
423 }
424
425 uint32_t offset = 0;
426
427 bool hit_event = false;
428 int drum_hits[DRUM_PCS];
429 memset (drum_hits, 0, sizeof (drum_hits));
430
431 LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
432 if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) {
433 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
434 if (obj->body.otype == self->uris.ui_off) {
435 self->ui_active = false;
436 } else if (obj->body.otype == self->uris.ui_on) {
437 self->ui_active = true;
438 self->inform_ui = true;
439 }
440 } else if (ev->body.type == self->uris.midi_MidiEvent &&
441 self->initialized && !self->reinit_in_progress) {
442 if (ev->body.size != 3 || ev->time.frames > n_samples) { // XXX should be >= n_samples
443 continue;
444 }
445 // work-around jalv sending GUI -> DSP messages @ n_samples
446 if (ev->time.frames == n_samples) {
447 ev->time.frames = n_samples -1;
448 }
449
450 if (ev->time.frames > offset) {
451 synth_sound (self, ev->time.frames - offset, offset);
452 }
453
454 offset = ev->time.frames;
455
456 const uint8_t* const data = (const uint8_t*)(ev + 1);
457 fluid_midi_event_set_type (self->fmidi_event, data[0] & 0xf0);
458
459 if (fluid_midi_event_get_type(self->fmidi_event) == 0xc0 /*PROGRAM_CHANGE*/) {
460 continue;
461 }
462 if (fluid_midi_event_get_type(self->fmidi_event) == 0xa0 /*KEY_PRESSURE*/) {
463 continue;
464 }
465 if (fluid_midi_event_get_type(self->fmidi_event) == 0xd0 /*CHANNEL_PRESSURE*/) {
466 continue;
467 }
468 if (fluid_midi_event_get_type(self->fmidi_event) == 0xe0 /*PITCH_BEND*/) {
469 continue;
470 }
471 if (fluid_midi_event_get_type(self->fmidi_event) == 0xf0 /*MIDI_SYSEX*/) {
472 continue;
473 }
474
475 if (fluid_midi_event_get_type(self->fmidi_event) == 0xb0 /*CC*/) {
476 switch (data[1]) {
477 case 0x0a: // pan
478 case 0x2a: // pan
479 continue;
480 default:
481 break;
482 }
483 }
484
485 if ((fluid_midi_event_get_type(self->fmidi_event) & 0xe0) == 0x80 /*NOTE*/) {
486 fluid_midi_event_set_channel (self->fmidi_event, assign_channel (data[1]));
487 }
488
489 fluid_midi_event_set_key (self->fmidi_event, data[1]);
490 fluid_midi_event_set_value (self->fmidi_event, data[2]);
491 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
492
493 if (fluid_midi_event_get_type(self->fmidi_event) == 0x90 /*NOTE_ON*/) {
494 int dp = data[1] - 36; // base-note
495 if (dp >= 0 && dp < DRUM_PCS) {
496 drum_hits[dp] = data[2];
497 hit_event = true;
498 }
499 }
500 }
501 }
502
503 if (self->queue_reinit && !self->reinit_in_progress) {
504 self->reinit_in_progress = true;
505 int magic = 0x47110815;
506 self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
507 }
508
509 /* inform the GUI */
510 if (self->ui_active) {
511 if (self->inform_ui) {
512 self->inform_ui = false;
513 inform_ui (self);
514 }
515 if (hit_event) {
516 kick_ui (self, drum_hits);
517 }
518 }
519
520 if (n_samples > offset && self->initialized && !self->reinit_in_progress) {
521 synth_sound (self, n_samples - offset, offset);
522 }
523 }
524
cleanup(LV2_Handle instance)525 static void cleanup (LV2_Handle instance)
526 {
527 AVLSynth* self = (AVLSynth*)instance;
528 delete_fluid_synth (self->synth);
529 delete_fluid_settings (self->settings);
530 delete_fluid_midi_event (self->fmidi_event);
531 free (self);
532 }
533
534 /* *****************************************************************************
535 * LV2 Extensions
536 */
537
538 static LV2_Worker_Status
work(LV2_Handle instance,LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle handle,uint32_t size,const void * data)539 work (LV2_Handle instance,
540 LV2_Worker_Respond_Function respond,
541 LV2_Worker_Respond_Handle handle,
542 uint32_t size,
543 const void* data)
544 {
545 AVLSynth* self = (AVLSynth*)instance;
546
547 if (size != sizeof (int)) {
548 return LV2_WORKER_ERR_UNKNOWN;
549 }
550 int magic = *((const int*)data);
551 if (magic != 0x47110815) {
552 return LV2_WORKER_ERR_UNKNOWN;
553 }
554
555 self->initialized = load_sf2 (self, self->queue_sf2_file_path);
556
557 if (self->initialized) {
558 fluid_synth_all_notes_off (self->synth, -1);
559 fluid_synth_all_sounds_off (self->synth, -1);
560 self->panic = false;
561 // boostrap synth engine.
562 float b[1024];
563 fluid_synth_write_float (self->synth, 1024, b, 0, 1, b, 0, 1);
564 }
565
566 respond (handle, 1, "");
567 return LV2_WORKER_SUCCESS;
568 }
569
570 static LV2_Worker_Status
work_response(LV2_Handle instance,uint32_t size,const void * data)571 work_response (LV2_Handle instance,
572 uint32_t size,
573 const void* data)
574 {
575 AVLSynth* self = (AVLSynth*)instance;
576
577 if (self->initialized) {
578 strcpy (self->current_sf2_file_path, self->queue_sf2_file_path);
579 } else {
580 self->current_sf2_file_path[0] = 0;
581 }
582
583 self->reinit_in_progress = false;
584 self->inform_ui = true;
585 self->queue_reinit = false;
586 return LV2_WORKER_SUCCESS;
587 }
588
589 static char*
mn_file(LV2_Handle instance)590 mn_file (LV2_Handle instance)
591 {
592 #include "midnam.h"
593 AVLSynth* self = (AVLSynth*)instance;
594 char* mn = strdup (AVL_Drumkits_midnam);
595 char inst[11];
596 snprintf (inst, 10, "%p", self);
597 inst[10] = '\0';
598 memcpy (&mn[339], inst, strlen(inst));
599 return mn;
600 }
601
602 static char*
mn_model(LV2_Handle instance)603 mn_model (LV2_Handle instance)
604 {
605 AVLSynth* self = (AVLSynth*)instance;
606 char* rv = strdup ("AVL-Drumkits-LV2:0xXXXXXXXX");
607 sprintf (rv, "AVL-Drumkits-LV2:0xXXXXXXXX");
608 char inst[11];
609 snprintf (inst, 10, "%p", self);
610 inst[10] = '\0';
611 memcpy (&rv[17], inst, strlen(inst));
612 return rv;
613 }
614
615 static void
mn_free(char * v)616 mn_free (char* v)
617 {
618 free (v);
619 }
620
621 static const void*
extension_data(const char * uri)622 extension_data (const char* uri)
623 {
624 static const LV2_Worker_Interface worker = { work, work_response, NULL };
625 static const LV2_Midnam_Interface midnam = { mn_file, mn_model, mn_free };
626 if (!strcmp (uri, LV2_WORKER__interface)) {
627 return &worker;
628 }
629 if (!strcmp (uri, LV2_MIDNAM__interface)) {
630 return &midnam;
631 }
632 return NULL;
633 }
634
635 #define mkdesc(ID, NAME) \
636 static const LV2_Descriptor descriptor ## ID = { \
637 AVL_URI NAME, \
638 instantiate, \
639 connect_port, \
640 NULL, \
641 run, \
642 deactivate, \
643 cleanup, \
644 extension_data \
645 };
646
647 mkdesc(0, "BlackPearl");
648 mkdesc(1, "BlackPearlMulti");
649 mkdesc(2, "RedZeppelin");
650 mkdesc(3, "RedZeppelinMulti");
651
652 #undef LV2_SYMBOL_EXPORT
653 #ifdef _WIN32
654 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
655 #else
656 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
657 #endif
658 LV2_SYMBOL_EXPORT
659 const LV2_Descriptor*
lv2_descriptor(uint32_t index)660 lv2_descriptor (uint32_t index)
661 {
662 switch (index) {
663 case 0: return &descriptor0;
664 case 1: return &descriptor1;
665 case 2: return &descriptor2;
666 case 3: return &descriptor3;
667 default:
668 return NULL;
669 }
670 }
671