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/>.
27 #define AFS_URN "urn:ardour:a-fluidsynth"
29 #ifdef HAVE_LV2_1_10_0
30 #define x_forge_object lv2_atom_forge_object
32 #define x_forge_object lv2_atom_forge_blank
35 #include "fluidsynth.h"
37 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
38 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
39 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
40 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
41 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
42 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
43 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
44 #include <lv2/lv2plug.in/ns/ext/state/state.h>
45 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
46 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
62 const LV2_Atom_Sequence* control;
63 LV2_Atom_Sequence* notify;
67 fluid_settings_t* settings;
76 LV2_URID midi_MidiEvent;
79 LV2_URID patch_property;
85 LV2_Log_Logger logger;
86 LV2_Worker_Schedule* schedule;
88 LV2_Atom_Forge_Frame frame;
95 char current_sf2_file_path[1024];
96 char queue_sf2_file_path[1024];
97 bool reinit_in_progress; // set in run, cleared in work_response
98 bool queue_reinit; // set in restore, cleared in work_response
100 fluid_midi_event_t* fmidi_event;
104 /* *****************************************************************************
108 load_sf2 (AFluidSynth* self, const char* fn)
110 const int synth_id = fluid_synth_sfload (self->synth, fn, 1);
112 if (synth_id == FLUID_FAILED) {
116 fluid_sfont_t* const sfont = fluid_synth_get_sfont_by_id (self->synth, synth_id);
122 fluid_preset_t preset;
123 sfont->iteration_start (sfont);
124 for (chn = 0; sfont->iteration_next (sfont, &preset) && chn < 15; ++chn) {
125 fluid_synth_program_select (self->synth, chn, synth_id,
126 preset.get_banknum (&preset), preset.get_num (&preset));
136 static const LV2_Atom*
137 parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
139 const LV2_Atom* property = NULL;
140 const LV2_Atom* file_path = NULL;
142 if (obj->body.otype != self->patch_Set) {
146 lv2_atom_object_get (obj, self->patch_property, &property, 0);
147 if (!property || property->type != self->atom_URID) {
149 } else if (((const LV2_Atom_URID*)property)->body != self->afs_sf2file) {
153 lv2_atom_object_get(obj, self->patch_value, &file_path, 0);
154 if (!file_path || file_path->type != self->atom_Path) {
163 inform_ui (AFluidSynth* self)
165 if (strlen (self->current_sf2_file_path) == 0) {
169 LV2_Atom_Forge_Frame frame;
170 lv2_atom_forge_frame_time (&self->forge, 0);
171 x_forge_object(&self->forge, &frame, 1, self->patch_Set);
172 lv2_atom_forge_property_head (&self->forge, self->patch_property, 0);
173 lv2_atom_forge_urid (&self->forge, self->afs_sf2file);
174 lv2_atom_forge_property_head (&self->forge, self->patch_value, 0);
175 lv2_atom_forge_path( &self->forge, self->current_sf2_file_path, strlen(self->current_sf2_file_path));
177 lv2_atom_forge_pop (&self->forge, &frame);
180 /* *****************************************************************************
185 instantiate (const LV2_Descriptor* descriptor,
187 const char* bundle_path,
188 const LV2_Feature* const* features)
190 AFluidSynth* self = (AFluidSynth*)calloc (1, sizeof (AFluidSynth));
196 LV2_URID_Map* map = NULL;
198 for (int i=0; features[i] != NULL; ++i) {
199 if (!strcmp (features[i]->URI, LV2_URID__map)) {
200 map = (LV2_URID_Map*)features[i]->data;
201 } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
202 self->log = (LV2_Log_Log*)features[i]->data;
203 } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) {
204 self->schedule = (LV2_Worker_Schedule*)features[i]->data;
208 lv2_log_logger_init (&self->logger, map, self->log);
211 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support urid:map\n");
216 if (!self->schedule) {
217 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support worker:schedule\n");
222 /* initialize fluid synth */
223 self->settings = new_fluid_settings ();
225 if (!self->settings) {
226 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Settings\n");
231 fluid_settings_setnum (self->settings, "synth.sample-rate", rate);
232 fluid_settings_setint (self->settings, "synth.parallel-render", 1);
233 fluid_settings_setint (self->settings, "synth.threadsafe-api", 0);
235 self->synth = new_fluid_synth (self->settings);
238 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Synth\n");
239 delete_fluid_settings (self->settings);
244 fluid_synth_set_gain (self->synth, 1.0f);
245 fluid_synth_set_polyphony (self->synth, 32);
246 fluid_synth_set_sample_rate (self->synth, (float)rate);
248 self->fmidi_event = new_fluid_midi_event ();
250 if (!self->fmidi_event) {
251 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Event\n");
252 delete_fluid_synth (self->synth);
253 delete_fluid_settings (self->settings);
258 /* initialize plugin state */
261 self->inform_ui = false;
262 self->initialized = false;
263 self->reinit_in_progress = false;
264 self->queue_reinit = false;
266 lv2_atom_forge_init (&self->forge, map);
269 self->atom_Blank = map->map (map->handle, LV2_ATOM__Blank);
270 self->atom_Object = map->map (map->handle, LV2_ATOM__Object);
271 self->atom_Path = map->map (map->handle, LV2_ATOM__Path);
272 self->atom_URID = map->map (map->handle, LV2_ATOM__URID);
273 self->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent);
274 self->patch_Get = map->map (map->handle, LV2_PATCH__Get);
275 self->patch_Set = map->map (map->handle, LV2_PATCH__Set);
276 self->patch_property = map->map (map->handle, LV2_PATCH__property);
277 self->patch_value = map->map (map->handle, LV2_PATCH__value);
278 self->afs_sf2file = map->map (map->handle, AFS_URN ":sf2file");
280 return (LV2_Handle)self;
284 connect_port (LV2_Handle instance,
288 AFluidSynth* self = (AFluidSynth*)instance;
291 case FS_PORT_CONTROL:
292 self->control = (const LV2_Atom_Sequence*)data;
295 self->notify = (LV2_Atom_Sequence*)data;
298 self->output[0] = (float*)data;
301 self->output[1] = (float*)data;
307 activate (LV2_Handle instance)
309 AFluidSynth* self = (AFluidSynth*)instance;
314 run (LV2_Handle instance, uint32_t n_samples)
316 AFluidSynth* self = (AFluidSynth*)instance;
318 if (!self->control || !self->notify) {
322 const uint32_t capacity = self->notify->atom.size;
323 lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->notify, capacity);
324 lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
326 if (!self->initialized || self->reinit_in_progress) {
327 memset (self->output[0], 0, n_samples * sizeof (float));
328 memset (self->output[1], 0, n_samples * sizeof (float));
329 } else if (self->panic) {
330 fluid_synth_all_notes_off (self->synth, -1);
331 fluid_synth_all_sounds_off (self->synth, -1);
337 LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
338 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
339 if (ev->body.type == self->atom_Blank || ev->body.type == self->atom_Object) {
340 if (obj->body.otype == self->patch_Get) {
341 self->inform_ui = false;
344 else if (obj->body.otype == self->patch_Set) {
345 const LV2_Atom* file_path = parse_patch_msg (self, obj);
346 if (file_path && !self->reinit_in_progress && !self->queue_reinit) {
347 const char *fn = (const char*)(file_path+1);
348 strncpy (self->queue_sf2_file_path, fn, 1023);
349 self->queue_sf2_file_path[1023] = '\0';
350 self->reinit_in_progress = true;
352 self->schedule->schedule_work (self->schedule->handle, sizeof(int), &magic);
356 else if (ev->body.type == self->midi_MidiEvent && self->initialized && !self->reinit_in_progress) {
357 if (ev->body.size > 3 || ev->time.frames >= n_samples) {
361 if (ev->time.frames > offset) {
362 fluid_synth_write_float (
364 ev->time.frames - offset,
365 &self->output[0][offset], 0, 1,
366 &self->output[1][offset], 0, 1);
369 offset = ev->time.frames;
371 const uint8_t* const data = (const uint8_t*)(ev + 1);
372 fluid_midi_event_set_type (self->fmidi_event, data[0] & 0xf0);
373 fluid_midi_event_set_channel (self->fmidi_event, data[0] & 0x0f);
374 if (ev->body.size > 1) {
375 fluid_midi_event_set_key (self->fmidi_event, data[1]);
377 if (ev->body.size > 2) {
378 fluid_midi_event_set_value (self->fmidi_event, data[2]);
380 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
384 if (self->queue_reinit && !self->reinit_in_progress) {
385 self->reinit_in_progress = true;
387 self->schedule->schedule_work (self->schedule->handle, sizeof(int), &magic);
391 if (self->inform_ui) {
392 self->inform_ui = false;
396 if (n_samples > offset && self->initialized && !self->reinit_in_progress) {
397 fluid_synth_write_float (
400 &self->output[0][offset], 0, 1,
401 &self->output[1][offset], 0, 1);
405 static void cleanup (LV2_Handle instance)
407 AFluidSynth* self = (AFluidSynth*)instance;
408 delete_fluid_synth (self->synth);
409 delete_fluid_settings (self->settings);
410 delete_fluid_midi_event (self->fmidi_event);
414 /* *****************************************************************************
418 static LV2_Worker_Status
419 work (LV2_Handle instance,
420 LV2_Worker_Respond_Function respond,
421 LV2_Worker_Respond_Handle handle,
425 AFluidSynth* self = (AFluidSynth*)instance;
427 if (size != sizeof(int)) {
428 return LV2_WORKER_ERR_UNKNOWN;
430 int magic = *((const int*)data);
431 if (magic != 0x4711) {
432 return LV2_WORKER_ERR_UNKNOWN;
435 self->initialized = load_sf2 (self, self->queue_sf2_file_path);
436 respond (handle, 1, "");
437 return LV2_WORKER_SUCCESS;
440 static LV2_Worker_Status
441 work_response (LV2_Handle instance,
445 AFluidSynth* self = (AFluidSynth*)instance;
446 self->reinit_in_progress = false;
447 self->queue_reinit = false;
448 self->inform_ui = true;
451 if (self->initialized) {
452 strcpy (self->current_sf2_file_path, self->queue_sf2_file_path);
454 self->current_sf2_file_path[0] = 0;
456 return LV2_WORKER_SUCCESS;
459 static LV2_State_Status
460 save (LV2_Handle instance,
461 LV2_State_Store_Function store,
462 LV2_State_Handle handle,
464 const LV2_Feature* const* features)
466 AFluidSynth* self = (AFluidSynth*)instance;
468 if (strlen (self->current_sf2_file_path) == 0) {
469 return LV2_STATE_ERR_NO_PROPERTY;
472 LV2_State_Map_Path* map_path = NULL;
474 for (int i = 0; features[i]; ++i) {
475 if (!strcmp (features[i]->URI, LV2_STATE__mapPath)) {
476 map_path = (LV2_State_Map_Path*) features[i]->data;
481 return LV2_STATE_ERR_NO_FEATURE;
484 char* apath = map_path->abstract_path (map_path->handle, self->current_sf2_file_path);
485 store (handle, self->afs_sf2file,
486 apath, strlen (apath) + 1,
488 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
490 return LV2_STATE_SUCCESS;
493 static LV2_State_Status
494 restore (LV2_Handle instance,
495 LV2_State_Retrieve_Function retrieve,
496 LV2_State_Handle handle,
498 const LV2_Feature* const* features)
500 AFluidSynth* self = (AFluidSynth*)instance;
501 if (self->reinit_in_progress || self->queue_reinit) {
502 lv2_log_warning (&self->logger, "a-fluidsynth.lv2: sf2 load already queued.\n");
503 return LV2_STATE_ERR_UNKNOWN;
510 const void* value = retrieve (handle, self->afs_sf2file, &size, &type, &valflags);
512 strncpy (self->queue_sf2_file_path, value, 1023);
513 self->queue_sf2_file_path[1023] = '\0';
514 self->queue_reinit = true;
516 return LV2_STATE_SUCCESS;
520 extension_data (const char* uri)
522 static const LV2_Worker_Interface worker = { work, work_response, NULL };
523 static const LV2_State_Interface state = { save, restore };
524 if (!strcmp (uri, LV2_WORKER__interface)) {
527 else if (!strcmp (uri, LV2_STATE__interface)) {
533 static const LV2_Descriptor descriptor = {
544 #undef LV2_SYMBOL_EXPORT
546 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
548 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
551 const LV2_Descriptor*
552 lv2_descriptor (uint32_t index)