2 * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 #define AFS_URN "urn:ardour:a-fluidsynth"
35 #ifdef HAVE_LV2_1_10_0
36 #define x_forge_object lv2_atom_forge_object
38 #define x_forge_object lv2_atom_forge_blank
42 #include "../../ardour/ardour/lv2_extensions.h"
45 #include "fluidsynth.h"
47 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
48 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
49 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
50 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
51 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
52 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
53 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
54 #include <lv2/lv2plug.in/ns/ext/state/state.h>
55 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
56 #include <lv2/lv2plug.in/ns/ext/worker/worker.h>
84 BankProgram (const std::string& n, int b, int p)
90 BankProgram (const BankProgram& other)
93 , program (other.program)
101 typedef std::vector<BankProgram> BPList;
102 typedef std::map<int, BPList> BPMap;
103 typedef std::map<int, BankProgram> BPState;
107 const LV2_Atom_Sequence* control;
108 LV2_Atom_Sequence* notify;
110 float* p_ports[FS_PORT_LAST];
111 float v_ports[FS_PORT_LAST];
114 fluid_settings_t* settings;
115 fluid_synth_t* synth;
120 LV2_URID atom_Object;
123 LV2_URID midi_MidiEvent;
126 LV2_URID patch_property;
127 LV2_URID patch_value;
128 LV2_URID state_Changed;
129 LV2_URID afs_sf2file;
133 LV2_Log_Logger logger;
134 LV2_Worker_Schedule* schedule;
135 LV2_Atom_Forge forge;
136 LV2_Atom_Forge_Frame frame;
140 LV2_BankPatch* bankpatch;
143 pthread_mutex_t bp_lock;
151 char current_sf2_file_path[1024];
152 char queue_sf2_file_path[1024];
153 bool reinit_in_progress; // set in run, cleared in work_response
154 bool queue_reinit; // set in restore, cleared in work_response
156 BankProgram program_state[16];
158 fluid_midi_event_t* fmidi_event;
162 /* *****************************************************************************
167 load_sf2 (AFluidSynth* self, const char* fn)
169 const int synth_id = fluid_synth_sfload (self->synth, fn, 1);
172 pthread_mutex_lock (&self->bp_lock);
173 self->presets.clear ();
174 pthread_mutex_unlock (&self->bp_lock);
177 if (synth_id == FLUID_FAILED) {
181 fluid_sfont_t* const sfont = fluid_synth_get_sfont_by_id (self->synth, synth_id);
187 fluid_preset_t *preset;
188 fluid_sfont_iteration_start (sfont);
189 pthread_mutex_lock (&self->bp_lock);
190 for (chn = 0; (preset = fluid_sfont_iteration_next (sfont)); ++chn) {
192 fluid_synth_program_select (self->synth, chn, synth_id,
193 fluid_preset_get_banknum (preset), fluid_preset_get_num (preset));
198 self->presets[fluid_preset_get_banknum (preset)].push_back (
200 fluid_preset_get_name (preset),
201 fluid_preset_get_banknum (preset),
202 fluid_preset_get_num (preset)));
203 #endif // LV2_EXTENDED
205 pthread_mutex_unlock (&self->bp_lock);
214 static const LV2_Atom*
215 parse_patch_msg (AFluidSynth* self, const LV2_Atom_Object* obj)
217 const LV2_Atom* property = NULL;
218 const LV2_Atom* file_path = NULL;
220 if (obj->body.otype != self->patch_Set) {
224 lv2_atom_object_get (obj, self->patch_property, &property, 0);
225 if (!property || property->type != self->atom_URID) {
227 } else if (((const LV2_Atom_URID*)property)->body != self->afs_sf2file) {
231 lv2_atom_object_get (obj, self->patch_value, &file_path, 0);
232 if (!file_path || file_path->type != self->atom_Path) {
240 inform_ui (AFluidSynth* self)
242 if (strlen (self->current_sf2_file_path) == 0) {
246 LV2_Atom_Forge_Frame frame;
247 lv2_atom_forge_frame_time (&self->forge, 0);
248 x_forge_object (&self->forge, &frame, 1, self->patch_Set);
249 lv2_atom_forge_property_head (&self->forge, self->patch_property, 0);
250 lv2_atom_forge_urid (&self->forge, self->afs_sf2file);
251 lv2_atom_forge_property_head (&self->forge, self->patch_value, 0);
252 lv2_atom_forge_path (&self->forge, self->current_sf2_file_path, strlen (self->current_sf2_file_path));
254 lv2_atom_forge_pop (&self->forge, &frame);
258 db_to_coeff (float db)
260 if (db <= -80) { return 0; }
261 else if (db >= 20) { return 10; }
262 return powf (10.f, .05f * db);
265 /* *****************************************************************************
270 instantiate (const LV2_Descriptor* descriptor,
272 const char* bundle_path,
273 const LV2_Feature* const* features)
275 AFluidSynth* self = (AFluidSynth*)calloc (1, sizeof (AFluidSynth));
281 LV2_URID_Map* map = NULL;
283 for (int i=0; features[i] != NULL; ++i) {
284 if (!strcmp (features[i]->URI, LV2_URID__map)) {
285 map = (LV2_URID_Map*)features[i]->data;
286 } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
287 self->log = (LV2_Log_Log*)features[i]->data;
288 } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) {
289 self->schedule = (LV2_Worker_Schedule*)features[i]->data;
291 } else if (!strcmp (features[i]->URI, LV2_MIDNAM__update)) {
292 self->midnam = (LV2_Midnam*)features[i]->data;
293 } else if (!strcmp (features[i]->URI, LV2_BANKPATCH__notify)) {
294 self->bankpatch = (LV2_BankPatch*)features[i]->data;
299 lv2_log_logger_init (&self->logger, map, self->log);
302 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support urid:map\n");
307 if (!self->schedule) {
308 lv2_log_error (&self->logger, "a-fluidsynth.lv2: Host does not support worker:schedule\n");
313 /* initialize fluid synth */
314 self->settings = new_fluid_settings ();
316 if (!self->settings) {
317 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Settings\n");
322 fluid_settings_setnum (self->settings, "synth.sample-rate", rate);
323 fluid_settings_setint (self->settings, "synth.threadsafe-api", 0);
324 fluid_settings_setstr (self->settings, "synth.midi-bank-select", "mma");
326 self->synth = new_fluid_synth (self->settings);
329 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Synth\n");
330 delete_fluid_settings (self->settings);
335 fluid_synth_set_gain (self->synth, 1.0f);
336 fluid_synth_set_polyphony (self->synth, 32);
337 fluid_synth_set_sample_rate (self->synth, (float)rate);
339 fluid_synth_set_reverb_on (self->synth, 0);
340 fluid_synth_set_chorus_on (self->synth, 0);
342 self->fmidi_event = new_fluid_midi_event ();
344 if (!self->fmidi_event) {
345 lv2_log_error (&self->logger, "a-fluidsynth.lv2: cannot allocate Fluid Event\n");
346 delete_fluid_synth (self->synth);
347 delete_fluid_settings (self->settings);
352 /* initialize plugin state */
354 pthread_mutex_init (&self->bp_lock, NULL);
356 self->presets = BPMap();
359 self->inform_ui = false;
360 self->send_bankpgm = true;
361 self->initialized = false;
362 self->reinit_in_progress = false;
363 self->queue_reinit = false;
364 for (int chn = 0; chn < 16; ++chn) {
365 self->program_state[chn].program = -1;
368 lv2_atom_forge_init (&self->forge, map);
371 self->atom_Blank = map->map (map->handle, LV2_ATOM__Blank);
372 self->atom_Object = map->map (map->handle, LV2_ATOM__Object);
373 self->atom_Path = map->map (map->handle, LV2_ATOM__Path);
374 self->atom_URID = map->map (map->handle, LV2_ATOM__URID);
375 self->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent);
376 self->patch_Get = map->map (map->handle, LV2_PATCH__Get);
377 self->patch_Set = map->map (map->handle, LV2_PATCH__Set);
378 self->patch_property = map->map (map->handle, LV2_PATCH__property);
379 self->patch_value = map->map (map->handle, LV2_PATCH__value);
380 self->state_Changed = map->map (map->handle, "http://lv2plug.in/ns/ext/state#StateChanged");
381 self->afs_sf2file = map->map (map->handle, AFS_URN ":sf2file");
383 return (LV2_Handle)self;
387 connect_port (LV2_Handle instance,
391 AFluidSynth* self = (AFluidSynth*)instance;
394 case FS_PORT_CONTROL:
395 self->control = (const LV2_Atom_Sequence*)data;
398 self->notify = (LV2_Atom_Sequence*)data;
401 if (port < FS_PORT_LAST) {
402 self->p_ports[port] = (float*)data;
409 deactivate (LV2_Handle instance)
411 AFluidSynth* self = (AFluidSynth*)instance;
416 run (LV2_Handle instance, uint32_t n_samples)
418 AFluidSynth* self = (AFluidSynth*)instance;
420 if (!self->control || !self->notify) {
424 const uint32_t capacity = self->notify->atom.size;
425 lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->notify, capacity);
426 lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
428 if (!self->initialized || self->reinit_in_progress) {
429 memset (self->p_ports[FS_PORT_OUT_L], 0, n_samples * sizeof (float));
430 memset (self->p_ports[FS_PORT_OUT_R], 0, n_samples * sizeof (float));
431 } else if (self->panic) {
432 fluid_synth_all_notes_off (self->synth, -1);
433 fluid_synth_all_sounds_off (self->synth, -1);
437 if (self->initialized && !self->reinit_in_progress) {
438 bool rev_change = false;
439 bool chr_change = false;
440 // TODO clamp values to ranges
441 if (self->v_ports[FS_OUT_GAIN] != *self->p_ports[FS_OUT_GAIN]) {
442 fluid_synth_set_gain (self->synth, db_to_coeff (*self->p_ports[FS_OUT_GAIN]));
444 if (self->v_ports[FS_REV_ENABLE] != *self->p_ports[FS_REV_ENABLE]) {
445 fluid_synth_set_reverb_on (self->synth, *self->p_ports[FS_REV_ENABLE] > 0 ? 1 : 0);
448 if (self->v_ports[FS_CHR_ENABLE] != *self->p_ports[FS_CHR_ENABLE]) {
449 fluid_synth_set_chorus_on (self->synth, *self->p_ports[FS_CHR_ENABLE] > 0 ? 1 : 0);
453 for (uint32_t p = FS_REV_ROOMSIZE; p <= FS_REV_LEVEL && !rev_change; ++p) {
454 if (self->v_ports[p] != *self->p_ports[p]) {
458 for (uint32_t p = FS_CHR_N; p <= FS_CHR_TYPE && !chr_change; ++p) {
459 if (self->v_ports[p] != *self->p_ports[p]) {
465 fluid_synth_set_reverb (self->synth,
466 *self->p_ports[FS_REV_ROOMSIZE],
467 *self->p_ports[FS_REV_DAMPING],
468 *self->p_ports[FS_REV_WIDTH],
469 *self->p_ports[FS_REV_LEVEL]);
473 fluid_synth_set_chorus (self->synth,
474 rintf (*self->p_ports[FS_CHR_N]),
475 db_to_coeff (*self->p_ports[FS_CHR_LEVEL]),
476 *self->p_ports[FS_CHR_SPEED],
477 *self->p_ports[FS_CHR_DEPTH],
478 (*self->p_ports[FS_CHR_TYPE] > 0) ? FLUID_CHORUS_MOD_SINE : FLUID_CHORUS_MOD_TRIANGLE);
480 for (uint32_t p = FS_OUT_GAIN; p < FS_PORT_LAST; ++p) {
481 self->v_ports[p] = *self->p_ports[p];
487 LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) {
488 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
489 if (ev->body.type == self->atom_Blank || ev->body.type == self->atom_Object) {
490 if (obj->body.otype == self->patch_Get) {
491 self->inform_ui = false;
494 else if (obj->body.otype == self->patch_Set) {
495 const LV2_Atom* file_path = parse_patch_msg (self, obj);
496 if (file_path && !self->reinit_in_progress && !self->queue_reinit) {
497 const char *fn = (const char*)(file_path+1);
498 strncpy (self->queue_sf2_file_path, fn, 1023);
499 self->queue_sf2_file_path[1023] = '\0';
500 self->reinit_in_progress = true;
502 self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
506 else if (ev->body.type == self->midi_MidiEvent) {
507 if (ev->body.size > 3 || ev->time.frames >= n_samples) {
511 if (ev->time.frames > offset) {
512 fluid_synth_write_float (
514 ev->time.frames - offset,
515 &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
516 &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
519 offset = ev->time.frames;
521 const uint8_t* const data = (const uint8_t*)(ev + 1);
522 fluid_midi_event_set_type (self->fmidi_event, data[0] & 0xf0);
523 fluid_midi_event_set_channel (self->fmidi_event, data[0] & 0x0f);
524 if (ev->body.size > 1) {
525 fluid_midi_event_set_key (self->fmidi_event, data[1]);
527 if (ev->body.size > 2) {
528 if (0xe0 /*PITCH_BEND*/ == fluid_midi_event_get_type (self->fmidi_event)) {
529 fluid_midi_event_set_value (self->fmidi_event, 0);
530 fluid_midi_event_set_pitch (self->fmidi_event, ((data[2] & 0x7f) << 7) | (data[1] & 0x7f));
532 fluid_midi_event_set_value (self->fmidi_event, data[2]);
534 if (0xb0 /* CC */ == fluid_midi_event_get_type (self->fmidi_event)) {
535 int chn = fluid_midi_event_get_channel (self->fmidi_event);
536 assert (chn >= 0 && chn < 16);
537 if (data[1] == 0x00) {
538 self->program_state[chn].bank &= 0x7f;
539 self->program_state[chn].bank |= (data[2] &0x7f) << 7;
541 if (data[1] == 0x20) {
542 self->program_state[chn].bank &= 0x3F80;
543 self->program_state[chn].bank |= data[2] & 0x7f;
547 if (ev->body.size == 2 && 0xc0 /* Pgm */ == fluid_midi_event_get_type (self->fmidi_event)) {
548 int chn = fluid_midi_event_get_channel (self->fmidi_event);
549 assert (chn >= 0 && chn < 16);
550 self->program_state[chn].program = data[1];
552 if (self->bankpatch) {
553 self->bankpatch->notify (self->bankpatch->handle, chn,
554 self->program_state[chn].bank,
555 self->program_state[chn].program < 0 ? 255 : self->program_state[chn].program);
560 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
564 if (self->queue_reinit && !self->reinit_in_progress) {
565 self->reinit_in_progress = true;
567 self->schedule->schedule_work (self->schedule->handle, sizeof (int), &magic);
571 if (self->inform_ui) {
572 self->inform_ui = false;
574 /* emit stateChanged */
575 LV2_Atom_Forge_Frame frame;
576 lv2_atom_forge_frame_time(&self->forge, 0);
577 x_forge_object(&self->forge, &frame, 1, self->state_Changed);
578 lv2_atom_forge_pop(&self->forge, &frame);
580 /* send .sf2 filename */
584 self->midnam->update (self->midnam->handle);
589 if (self->send_bankpgm && self->bankpatch) {
590 self->send_bankpgm = false;
591 for (uint8_t chn = 0; chn < 16; ++chn) {
592 self->bankpatch->notify (self->bankpatch->handle, chn,
593 self->program_state[chn].bank,
594 self->program_state[chn].program < 0 ? 255 : self->program_state[chn].program);
599 if (n_samples > offset && self->initialized && !self->reinit_in_progress) {
600 fluid_synth_write_float (
603 &self->p_ports[FS_PORT_OUT_L][offset], 0, 1,
604 &self->p_ports[FS_PORT_OUT_R][offset], 0, 1);
608 static void cleanup (LV2_Handle instance)
610 AFluidSynth* self = (AFluidSynth*)instance;
611 delete_fluid_synth (self->synth);
612 delete_fluid_settings (self->settings);
613 delete_fluid_midi_event (self->fmidi_event);
614 pthread_mutex_destroy (&self->bp_lock);
618 /* *****************************************************************************
622 static LV2_Worker_Status
623 work (LV2_Handle instance,
624 LV2_Worker_Respond_Function respond,
625 LV2_Worker_Respond_Handle handle,
629 AFluidSynth* self = (AFluidSynth*)instance;
631 if (size != sizeof (int)) {
632 return LV2_WORKER_ERR_UNKNOWN;
634 int magic = *((const int*)data);
635 if (magic != 0x4711) {
636 return LV2_WORKER_ERR_UNKNOWN;
640 self->initialized = load_sf2 (self, self->queue_sf2_file_path);
642 if (self->initialized) {
643 fluid_synth_all_notes_off (self->synth, -1);
644 fluid_synth_all_sounds_off (self->synth, -1);
646 // boostrap synth engine.
649 fluid_synth_write_float (self->synth, 1024, l, 0, 1, r, 0, 1);
652 respond (handle, 1, "");
653 return LV2_WORKER_SUCCESS;
656 static LV2_Worker_Status
657 work_response (LV2_Handle instance,
661 AFluidSynth* self = (AFluidSynth*)instance;
663 if (self->initialized) {
664 strcpy (self->current_sf2_file_path, self->queue_sf2_file_path);
666 for (int chn = 0; chn < 16; ++chn) {
667 if (self->program_state[chn].program < 0) {
670 /* cannot direcly call fluid_channel_set_bank_msb/fluid_channel_set_bank_lsb, use CCs */
671 fluid_midi_event_set_type (self->fmidi_event, 0xb0 /* CC */);
672 fluid_midi_event_set_channel (self->fmidi_event, chn);
674 fluid_midi_event_set_control (self->fmidi_event, 0x00); // BANK_SELECT_MSB
675 fluid_midi_event_set_value (self->fmidi_event, (self->program_state[chn].bank >> 7) & 0x7f);
676 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
678 fluid_midi_event_set_control (self->fmidi_event, 0x20); // BANK_SELECT_LSB
679 fluid_midi_event_set_value (self->fmidi_event, self->program_state[chn].bank & 0x7f);
680 fluid_synth_handle_midi_event (self->synth, self->fmidi_event);
682 fluid_synth_program_change (self->synth, chn, self->program_state[chn].program);
685 for (int chn = 0; chn < 16; ++chn) {
689 if (FLUID_OK == fluid_synth_get_program (self->synth, chn, &sfid, &bank, &program)) {
690 self->program_state[chn].bank = bank;
691 self->program_state[chn].program = program;
696 self->current_sf2_file_path[0] = 0;
699 self->reinit_in_progress = false;
700 self->inform_ui = true;
701 self->send_bankpgm = true;
702 self->queue_reinit = false;
703 return LV2_WORKER_SUCCESS;
706 static LV2_State_Status
707 save (LV2_Handle instance,
708 LV2_State_Store_Function store,
709 LV2_State_Handle handle,
711 const LV2_Feature* const* features)
713 AFluidSynth* self = (AFluidSynth*)instance;
715 if (strlen (self->current_sf2_file_path) == 0) {
716 return LV2_STATE_ERR_NO_PROPERTY;
719 LV2_State_Map_Path* map_path = NULL;
721 for (int i = 0; features[i]; ++i) {
722 if (!strcmp (features[i]->URI, LV2_STATE__mapPath)) {
723 map_path = (LV2_State_Map_Path*) features[i]->data;
728 return LV2_STATE_ERR_NO_FEATURE;
731 char* apath = map_path->abstract_path (map_path->handle, self->current_sf2_file_path);
732 store (handle, self->afs_sf2file,
733 apath, strlen (apath) + 1,
734 self->atom_Path, LV2_STATE_IS_POD);
736 #ifndef _WIN32 // TODO need lilv_free() -- https://github.com/drobilla/lilv/issues/14
740 return LV2_STATE_SUCCESS;
743 static LV2_State_Status
744 restore (LV2_Handle instance,
745 LV2_State_Retrieve_Function retrieve,
746 LV2_State_Handle handle,
748 const LV2_Feature* const* features)
750 AFluidSynth* self = (AFluidSynth*)instance;
751 if (self->reinit_in_progress || self->queue_reinit) {
752 lv2_log_warning (&self->logger, "a-fluidsynth.lv2: sf2 load already queued.\n");
753 return LV2_STATE_ERR_UNKNOWN;
756 LV2_State_Map_Path* map_path = NULL;
758 for (int i = 0; features[i]; ++i) {
759 if (!strcmp (features[i]->URI, LV2_STATE__mapPath)) {
760 map_path = (LV2_State_Map_Path*) features[i]->data;
765 return LV2_STATE_ERR_NO_FEATURE;
772 const void* value = retrieve (handle, self->afs_sf2file, &size, &type, &valflags);
774 char* apath = map_path->absolute_path (map_path->handle, (const char*) value);
775 strncpy (self->queue_sf2_file_path, apath, 1023);
776 printf ("XXX %s -> %s\n", (const char*) value, apath);
777 self->queue_sf2_file_path[1023] = '\0';
778 self->queue_reinit = true;
779 #ifndef _WIN32 // TODO need lilv_free() -- https://github.com/drobilla/lilv/issues/14
783 return LV2_STATE_SUCCESS;
786 static std::string xml_escape (const std::string& s)
789 std::replace (r.begin (), r.end(), '"', '\'');
791 while((pos = r.find ("&", pos)) != std::string::npos) {
792 r.replace (pos, 1, "&");
800 mn_file (LV2_Handle instance)
802 AFluidSynth* self = (AFluidSynth*)instance;
806 rv = (char*) calloc (1, sizeof (char));
810 snprintf (tmp, sizeof(tmp), __VA_ARGS__); \
811 tmp[sizeof(tmp) - 1] = '\0'; \
812 rv = (char*)realloc (rv, strlen (rv) + strlen(tmp) + 1); \
816 pf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
817 "<!DOCTYPE MIDINameDocument PUBLIC \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\" \"http://dev.midi.org/dtds/MIDINameDocument10.dtd\">\n"
818 "<MIDINameDocument>\n"
820 " <MasterDeviceNames>\n"
821 " <Manufacturer>Ardour Foundation</Manufacturer>\n"
822 " <Model>%s:%p</Model>\n", AFS_URN, (void*) self);
825 pf (" <CustomDeviceMode Name=\"Default\">\n");
826 pf (" <ChannelNameSetAssignments>\n");
827 for (int c = 0; c < 16; ++c) {
828 pf (" <ChannelNameSetAssign Channel=\"%d\" NameSet=\"Presets\"/>\n", c + 1);
830 pf (" </ChannelNameSetAssignments>\n");
831 pf (" </CustomDeviceMode>\n");
833 // TODO collect used banks, std::set<> would be a nice here
835 pf (" <ChannelNameSet Name=\"Presets\">\n");
836 pf (" <AvailableForChannels>\n");
837 for (int c = 0; c < 16; ++c) {
838 pf (" <AvailableChannel Channel=\"%d\" Available=\"true\"/>\n", c + 1);
840 pf (" </AvailableForChannels>\n");
841 pf (" <UsesControlNameList Name=\"Controls\"/>\n");
844 pthread_mutex_lock (&self->bp_lock);
845 const BPMap& ps (self->presets);
846 pthread_mutex_unlock (&self->bp_lock);
848 for (BPMap::const_iterator i = ps.begin (); i != ps.end (); ++i, ++bn) {
849 pf (" <PatchBank Name=\"Patch Bank %d\">\n", i->first);
850 if (i->second.size() > 0) {
851 pf (" <MIDICommands>\n");
852 pf (" <ControlChange Control=\"0\" Value=\"%d\"/>\n", (i->first >> 7) & 127);
853 pf (" <ControlChange Control=\"32\" Value=\"%d\"/>\n", i->first & 127);
854 pf (" </MIDICommands>\n");
855 pf (" <PatchNameList>\n");
857 for (BPList::const_iterator j = i->second.begin(); j != i->second.end(); ++j, ++n) {
858 pf (" <Patch Number=\"%d\" Name=\"%s\" ProgramChange=\"%d\"/>\n",
859 n, xml_escape (j->name).c_str(), j->program);
861 pf (" </PatchNameList>\n");
863 pf (" </PatchBank>\n");
865 pf (" </ChannelNameSet>\n");
867 pf (" <ControlNameList Name=\"Controls\">\n");
868 pf (" <Control Type=\"7bit\" Number=\"1\" Name=\"Modulation\"/>\n");
869 pf (" <Control Type=\"7bit\" Number=\"2\" Name=\"Breath\"/>\n");
870 pf (" <Control Type=\"7bit\" Number=\"5\" Name=\"Portamento Time\"/>\n");
871 pf (" <Control Type=\"7bit\" Number=\"7\" Name=\"Channel Volume\"/>\n");
872 pf (" <Control Type=\"7bit\" Number=\"8\" Name=\"Stereo Balance\"/>\n");
873 pf (" <Control Type=\"7bit\" Number=\"10\" Name=\"Pan\"/>\n");
874 pf (" <Control Type=\"7bit\" Number=\"11\" Name=\"Expression\"/>\n");
875 pf (" <Control Type=\"7bit\" Number=\"37\" Name=\"Portamento Time (Fine)\"/>\n");
876 pf (" <Control Type=\"7bit\" Number=\"39\" Name=\"Channel Volume (Fine)\"/>\n");
877 pf (" <Control Type=\"7bit\" Number=\"40\" Name=\"Stereo Balance (Fine)\"/>\n");
878 pf (" <Control Type=\"7bit\" Number=\"42\" Name=\"Pan (Fine)\"/>\n");
879 pf (" <Control Type=\"7bit\" Number=\"64\" Name=\"Sustain On/Off\"/>\n");
880 pf (" <Control Type=\"7bit\" Number=\"65\" Name=\"Portamento On/Off\"/>\n");
881 pf (" <Control Type=\"7bit\" Number=\"66\" Name=\"Sostenuto On/Off\"/>\n");
882 pf (" <Control Type=\"7bit\" Number=\"68\" Name=\"Legato On/Off\"/>\n");
883 pf (" <Control Type=\"7bit\" Number=\"91\" Name=\"Reverb\"/>\n");
884 pf (" <Control Type=\"7bit\" Number=\"93\" Name=\"Chorus\"/>\n");
885 pf (" </ControlNameList>\n");
888 " </MasterDeviceNames>\n"
889 "</MIDINameDocument>"
892 //printf("-----\n%s\n------\n", rv);
897 mn_model (LV2_Handle instance)
899 AFluidSynth* self = (AFluidSynth*)instance;
900 char* rv = (char*) malloc (64 * sizeof (char));
901 snprintf (rv, 64, "%s:%p", AFS_URN, (void*) self);
914 extension_data (const char* uri)
916 if (!strcmp (uri, LV2_WORKER__interface)) {
917 static const LV2_Worker_Interface worker = { work, work_response, NULL };
920 else if (!strcmp (uri, LV2_STATE__interface)) {
921 static const LV2_State_Interface state = { save, restore };
925 else if (!strcmp (uri, LV2_MIDNAM__interface)) {
926 static const LV2_Midnam_Interface midnam = { mn_file, mn_model, mn_free };
933 static const LV2_Descriptor descriptor = {
944 #undef LV2_SYMBOL_EXPORT
946 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
948 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
951 const LV2_Descriptor*
952 lv2_descriptor (uint32_t index)