1 /* a-fluidsynth -- simple & robust x-platform fluidsynth LV2
3 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
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)
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.
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/>.
28 #define AFS_URN "urn:ardour:a-fluidsynth"
30 #ifdef HAVE_LV2_1_10_0
31 #define x_forge_object lv2_atom_forge_object
33 #define x_forge_object lv2_atom_forge_blank
36 #include "fluidsynth.h"
38 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
39 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
40 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
41 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
42 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
43 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
44 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
45 #include <lv2/lv2plug.in/ns/ext/state/state.h>
46 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
47 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
76 const LV2_Atom_Sequence* control;
77 LV2_Atom_Sequence* notify;
79 float* p_ports[FS_PORT_LAST];
80 float v_ports[FS_PORT_LAST];
83 fluid_settings_t* settings;
92 LV2_URID midi_MidiEvent;
95 LV2_URID patch_property;
101 LV2_Log_Logger logger;
102 LV2_Worker_Schedule* schedule;
103 LV2_Atom_Forge forge;
104 LV2_Atom_Forge_Frame frame;
111 char current_sf2_file_path[1024];
112 char queue_sf2_file_path[1024];
113 bool reinit_in_progress; // set in run, cleared in work_response
114 bool queue_reinit; // set in restore, cleared in work_response
116 fluid_midi_event_t* fmidi_event;
120 /* *****************************************************************************
125 load_sf2 (AFluidSynth* self, const char* fn)
127 const int synth_id = fluid_synth_sfload (self->synth, fn, 1);
129 if (synth_id == FLUID_FAILED) {
133 fluid_sfont_t* const sfont = fluid_synth_get_sfont_by_id (self->synth, synth_id);
139 fluid_preset_t preset;
140 sfont->iteration_start (sfont);
141 for (chn = 0; sfont->iteration_next (sfont, &preset) && chn < 15; ++chn) {
142 fluid_synth_program_select (self->synth, chn, synth_id,
143 preset.get_banknum (&preset), preset.get_num (&preset));
153 static const LV2_Atom*
154 parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
156 const LV2_Atom* property = NULL;
157 const LV2_Atom* file_path = NULL;
159 if (obj->body.otype != self->patch_Set) {
163 lv2_atom_object_get (obj, self->patch_property, &property, 0);
164 if (!property || property->type != self->atom_URID) {
166 } else if (((const LV2_Atom_URID*)property)->body != self->afs_sf2file) {
170 lv2_atom_object_get (obj, self->patch_value, &file_path, 0);
171 if (!file_path || file_path->type != self->atom_Path) {
179 inform_ui (AFluidSynth* self)
181 if (strlen (self->current_sf2_file_path) == 0) {
185 LV2_Atom_Forge_Frame frame;
186 lv2_atom_forge_frame_time (&self->forge, 0);
187 x_forge_object (&self->forge, &frame, 1, self->patch_Set);
188 lv2_atom_forge_property_head (&self->forge, self->patch_property, 0);
189 lv2_atom_forge_urid (&self->forge, self->afs_sf2file);
190 lv2_atom_forge_property_head (&self->forge, self->patch_value, 0);
191 lv2_atom_forge_path (&self->forge, self->current_sf2_file_path, strlen (self->current_sf2_file_path));
193 lv2_atom_forge_pop (&self->forge, &frame);
197 db_to_coeff (float db)
199 if (db <= -80) { return 0; }
200 else if (db >= 20) { return 10; }
201 return powf (10.f, .05f * db);
204 /* *****************************************************************************
209 instantiate (const LV2_Descriptor* descriptor,
211 const char* bundle_path,
212 const LV2_Feature* const* features)
214 AFluidSynth* self = (AFluidSynth*)calloc (1, sizeof (AFluidSynth));
220 LV2_URID_Map* map = NULL;
222 for (int i=0; features[i] != NULL; ++i) {
223 if (!strcmp (features[i]->URI, LV2_URID__map)) {
224 map = (LV2_URID_Map*)features[i]->data;
225 } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
226 self->log = (LV2_Log_Log*)features[i]->data;
227 } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) {
228 self->schedule = (LV2_Worker_Schedule*)features[i]->data;
232 lv2_log_logger_init (&self->logger, map, self->log);
235 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support urid:map\n");
240 if (!self->schedule) {
241 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support worker:schedule\n");
246 /* initialize fluid synth */
247 self->settings = new_fluid_settings ();
249 if (!self->settings) {
250 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Settings\n");
255 fluid_settings_setnum (self->settings, "synth.sample-rate", rate);
256 fluid_settings_setint (self->settings, "synth.parallel-render", 1);
257 fluid_settings_setint (self->settings, "synth.threadsafe-api", 0);
259 self->synth = new_fluid_synth (self->settings);
262 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Synth\n");
263 delete_fluid_settings (self->settings);
268 fluid_synth_set_gain (self->synth, 1.0f);
269 fluid_synth_set_polyphony (self->synth, 32);
270 fluid_synth_set_sample_rate (self->synth, (float)rate);
272 self->fmidi_event = new_fluid_midi_event ();
274 if (!self->fmidi_event) {
275 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Event\n");
276 delete_fluid_synth (self->synth);
277 delete_fluid_settings (self->settings);
282 /* initialize plugin state */
285 self->inform_ui = false;
286 self->initialized = false;
287 self->reinit_in_progress = false;
288 self->queue_reinit = false;
290 lv2_atom_forge_init (&self->forge, map);
293 self->atom_Blank = map->map (map->handle, LV2_ATOM__Blank);
294 self->atom_Object = map->map (map->handle, LV2_ATOM__Object);
295 self->atom_Path = map->map (map->handle, LV2_ATOM__Path);
296 self->atom_URID = map->map (map->handle, LV2_ATOM__URID);
297 self->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent);
298 self->patch_Get = map->map (map->handle, LV2_PATCH__Get);
299 self->patch_Set = map->map (map->handle, LV2_PATCH__Set);
300 self->patch_property = map->map (map->handle, LV2_PATCH__property);
301 self->patch_value = map->map (map->handle, LV2_PATCH__value);
302 self->afs_sf2file = map->map (map->handle, AFS_URN ":sf2file");
304 return (LV2_Handle)self;
308 connect_port (LV2_Handle instance,
312 AFluidSynth* self = (AFluidSynth*)instance;
315 case FS_PORT_CONTROL:
316 self->control = (const LV2_Atom_Sequence*)data;
319 self->notify = (LV2_Atom_Sequence*)data;
322 if (port < FS_PORT_LAST) {
323 self->p_ports[port] = (float*)data;
330 deactivate (LV2_Handle instance)
332 AFluidSynth* self = (AFluidSynth*)instance;
337 run (LV2_Handle instance, uint32_t n_samples)
339 AFluidSynth* self = (AFluidSynth*)instance;
341 if (!self->control || !self->notify) {
345 const uint32_t capacity = self->notify->atom.size;
346 lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->notify, capacity);
347 lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
349 if (!self->initialized || self->reinit_in_progress) {
350 memset (self->p_ports[FS_PORT_OUT_L], 0, n_samples * sizeof (float));
351 memset (self->p_ports[FS_PORT_OUT_R], 0, n_samples * sizeof (float));
352 } else if (self->panic) {
353 fluid_synth_all_notes_off (self->synth, -1);
354 fluid_synth_all_sounds_off (self->synth, -1);
358 if (self->initialized && !self->reinit_in_progress) {
359 bool rev_change = false;
360 bool chr_change = false;
361 // TODO clamp values to ranges
362 if (self->v_ports[FS_OUT_GAIN] != *self->p_ports[FS_OUT_GAIN]) {
363 fluid_synth_set_gain (self->synth, db_to_coeff (*self->p_ports[FS_OUT_GAIN]));
365 if (self->v_ports[FS_REV_ENABLE] != *self->p_ports[FS_REV_ENABLE]) {
366 fluid_synth_set_reverb_on (self->synth, *self->p_ports[FS_REV_ENABLE] > 0 ? 1 : 0);
369 if (self->v_ports[FS_CHR_ENABLE] != *self->p_ports[FS_CHR_ENABLE]) {
370 fluid_synth_set_chorus_on (self->synth, *self->p_ports[FS_CHR_ENABLE] > 0 ? 1 : 0);
374 for (uint32_t p = FS_REV_ROOMSIZE; p <= FS_REV_LEVEL && !rev_change; ++p) {
375 if (self->v_ports[p] != *self->p_ports[p]) {
379 for (uint32_t p = FS_CHR_N; p <= FS_CHR_TYPE && !chr_change; ++p) {
380 if (self->v_ports[p] != *self->p_ports[p]) {
386 fluid_synth_set_reverb (self->synth,
387 *self->p_ports[FS_REV_ROOMSIZE],
388 *self->p_ports[FS_REV_DAMPING],
389 *self->p_ports[FS_REV_WIDTH],
390 *self->p_ports[FS_REV_LEVEL]);
394 fluid_synth_set_chorus (self->synth,
395 rintf (*self->p_ports[FS_CHR_N]),
396 db_to_coeff (*self->p_ports[FS_CHR_LEVEL]),
397 *self->p_ports[FS_CHR_SPEED],
398 *self->p_ports[FS_CHR_DEPTH],
399 (*self->p_ports[FS_CHR_TYPE] > 0) ? FLUID_CHORUS_MOD_SINE : FLUID_CHORUS_MOD_TRIANGLE);
401 for (uint32_t p = FS_OUT_GAIN; p < FS_PORT_LAST; ++p) {
402 self->v_ports[p] = *self->p_ports[p];
408 LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
409 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
410 if (ev->body.type == self->atom_Blank || ev->body.type == self->atom_Object) {
411 if (obj->body.otype == self->patch_Get) {
412 self->inform_ui = false;
415 else if (obj->body.otype == self->patch_Set) {
416 const LV2_Atom* file_path = parse_patch_msg (self, obj);
417 if (file_path && !self->reinit_in_progress && !self->queue_reinit) {
418 const char *fn = (const char*)(file_path+1);
419 strncpy (self->queue_sf2_file_path, fn, 1023);
420 self->queue_sf2_file_path[1023] = '\0';
421 self->reinit_in_progress = true;
423 self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
427 else if (ev->body.type == self->midi_MidiEvent && self->initialized && !self->reinit_in_progress) {
428 if (ev->body.size > 3 || ev->time.frames >= n_samples) {
432 if (ev->time.frames > offset) {
433 fluid_synth_write_float (
435 ev->time.frames - offset,
436 &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
437 &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
440 offset = ev->time.frames;
442 const uint8_t* const data = (const uint8_t*)(ev + 1);
443 fluid_midi_event_set_type (self->fmidi_event, data[0] & 0xf0);
444 fluid_midi_event_set_channel (self->fmidi_event, data[0] & 0x0f);
445 if (ev->body.size > 1) {
446 fluid_midi_event_set_key (self->fmidi_event, data[1]);
448 if (ev->body.size > 2) {
449 fluid_midi_event_set_value (self->fmidi_event, data[2]);
451 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
455 if (self->queue_reinit && !self->reinit_in_progress) {
456 self->reinit_in_progress = true;
458 self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
462 if (self->inform_ui) {
463 self->inform_ui = false;
467 if (n_samples > offset && self->initialized && !self->reinit_in_progress) {
468 fluid_synth_write_float (
471 &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
472 &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
476 static void cleanup (LV2_Handle instance)
478 AFluidSynth* self = (AFluidSynth*)instance;
479 delete_fluid_synth (self->synth);
480 delete_fluid_settings (self->settings);
481 delete_fluid_midi_event (self->fmidi_event);
485 /* *****************************************************************************
489 static LV2_Worker_Status
490 work (LV2_Handle instance,
491 LV2_Worker_Respond_Function respond,
492 LV2_Worker_Respond_Handle handle,
496 AFluidSynth* self = (AFluidSynth*)instance;
498 if (size != sizeof (int)) {
499 return LV2_WORKER_ERR_UNKNOWN;
501 int magic = *((const int*)data);
502 if (magic != 0x4711) {
503 return LV2_WORKER_ERR_UNKNOWN;
506 self->initialized = load_sf2 (self, self->queue_sf2_file_path);
508 if (self->initialized) {
509 fluid_synth_all_notes_off (self->synth, -1);
510 fluid_synth_all_sounds_off (self->synth, -1);
512 // boostrap synth engine.
515 fluid_synth_write_float (self->synth, 1024, l, 0, 1, r, 0, 1);
518 respond (handle, 1, "");
519 return LV2_WORKER_SUCCESS;
522 static LV2_Worker_Status
523 work_response (LV2_Handle instance,
527 AFluidSynth* self = (AFluidSynth*)instance;
529 if (self->initialized) {
530 strcpy (self->current_sf2_file_path, self->queue_sf2_file_path);
532 self->current_sf2_file_path[0] = 0;
535 self->reinit_in_progress = false;
536 self->inform_ui = true;
537 self->queue_reinit = false;
538 return LV2_WORKER_SUCCESS;
541 static LV2_State_Status
542 save (LV2_Handle instance,
543 LV2_State_Store_Function store,
544 LV2_State_Handle handle,
546 const LV2_Feature* const* features)
548 AFluidSynth* self = (AFluidSynth*)instance;
550 if (strlen (self->current_sf2_file_path) == 0) {
551 return LV2_STATE_ERR_NO_PROPERTY;
554 LV2_State_Map_Path* map_path = NULL;
556 for (int i = 0; features[i]; ++i) {
557 if (!strcmp (features[i]->URI, LV2_STATE__mapPath)) {
558 map_path = (LV2_State_Map_Path*) features[i]->data;
563 return LV2_STATE_ERR_NO_FEATURE;
566 char* apath = map_path->abstract_path (map_path->handle, self->current_sf2_file_path);
567 store (handle, self->afs_sf2file,
568 apath, strlen (apath) + 1,
569 self->atom_Path, LV2_STATE_IS_POD);
571 return LV2_STATE_SUCCESS;
574 static LV2_State_Status
575 restore (LV2_Handle instance,
576 LV2_State_Retrieve_Function retrieve,
577 LV2_State_Handle handle,
579 const LV2_Feature* const* features)
581 AFluidSynth* self = (AFluidSynth*)instance;
582 if (self->reinit_in_progress || self->queue_reinit) {
583 lv2_log_warning (&self->logger, "a-fluidsynth.lv2: sf2 load already queued.\n");
584 return LV2_STATE_ERR_UNKNOWN;
591 const void* value = retrieve (handle, self->afs_sf2file, &size, &type, &valflags);
593 strncpy (self->queue_sf2_file_path, value, 1023);
594 self->queue_sf2_file_path[1023] = '\0';
595 self->queue_reinit = true;
597 return LV2_STATE_SUCCESS;
601 extension_data (const char* uri)
603 static const LV2_Worker_Interface worker = { work, work_response, NULL };
604 static const LV2_State_Interface state = { save, restore };
605 if (!strcmp (uri, LV2_WORKER__interface)) {
608 else if (!strcmp (uri, LV2_STATE__interface)) {
614 static const LV2_Descriptor descriptor = {
625 #undef LV2_SYMBOL_EXPORT
627 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
629 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
632 const LV2_Descriptor*
633 lv2_descriptor (uint32_t index)