/*================================================================== * wavetbl_fluidsynth.c - Swami plugin for FluidSynth * * Swami * Copyright (C) 1999-2003 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net *==================================================================*/ #include "config.h" #include #include #include #include #include "GObjSup.h" #include #include #include #include #include #include #include #include #include "wavetbl_fluidsynth.h" #include "i18n.h" /* hack to prevent multi-thread crashes between synthesis process and libinstpatch routines */ G_LOCK_EXTERN (instp_voice_lock); /* MidiFluidSynth properties */ enum { MIDI_PROP_0, MIDI_PROP_WAVETBL }; /* maximum # of voices under real time control (voices exceeding this number just wont be controllable in real time, no fatal problems though) */ #define MAX_REALTIME_VOICES 64 /* additional data for sfloader sound fonts */ typedef struct { WavetblFluidSynth *wavetbl; /* wavetable object */ IPSFont *sf; /* sound font object */ } sfloader_sfont_data_t; typedef struct { WavetblFluidSynth *wavetbl; /* wavetable object */ IPPreset *preset; /* preset item */ } sfloader_preset_data_t; /* additional data for instp_item_foreach_layer callback used for noteon */ typedef struct { WavetblFluidSynth *wavetbl; /* wavetable object */ gboolean realtime; /* note on should allow realtime control? */ int chan; int key; int vel; } sfloader_noteon_data_t; /* note on info for real time generator control */ struct _realtime_noteon_t { IPItem *item; /* audible item for this note on event */ int key; /* MIDI key number of note on event */ int vel; /* MIDI velocity number of note on event */ int count; /* voice count */ fluid_voice_t *voices[MAX_REALTIME_VOICES]; }; static gboolean plugin_fluidsynth_init (GModule *module, SwamiPlugin *plugin); static void wavetbl_fluidsynth_class_init (WavetblFluidSynth *klass); static void wavetbl_fluidsynth_init (WavetblFluidSynth *wavetbl); static GType midi_fluidsynth_get_type (void); static void midi_fluidsynth_class_init (MidiFluidSynth *klass); static void midi_fluidsynth_init (MidiFluidSynth *midi); static void midi_fluidsynth_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void midi_fluidsynth_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static int midi_fluidsynth_init_driver (SwamiMidi *swami_midi); static int midi_fluidsynth_send_event (SwamiMidi *swami_midi, SwamiMidiEvent event, int chan, int param1, int param2); static int wavetbl_fluidsynth_init_driver (SwamiWavetbl *swami_wavetbl); static void wavetbl_fluidsynth_close_driver (SwamiWavetbl *swami_wavetbl); static int wavetbl_fluidsynth_load_patch (SwamiWavetbl *swami_wavetbl, IPItem *patch); static int wavetbl_fluidsynth_load_temp_item (SwamiWavetbl *swami_wavetbl, IPItem *item); static int sfloader_free (fluid_sfloader_t *loader); static fluid_sfont_t *sfloader_load_sfont (fluid_sfloader_t *loader, const char *filename); static int sfloader_sfont_free (fluid_sfont_t *sfont); static char *sfloader_sfont_get_name (fluid_sfont_t *sfont); static fluid_preset_t *sfloader_sfont_get_preset (fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum); static void sfloader_sfont_iteration_start (fluid_sfont_t *sfont); static int sfloader_sfont_iteration_next (fluid_sfont_t *sfont, fluid_preset_t *preset); static int sfloader_preset_free (fluid_preset_t *preset); static int sfloader_temp_preset_free (fluid_preset_t *preset); static char *sfloader_preset_get_name (fluid_preset_t *preset); static char *sfloader_temp_preset_get_name (fluid_preset_t *preset); static int sfloader_preset_get_banknum (fluid_preset_t *preset); static int sfloader_temp_preset_get_banknum (fluid_preset_t *preset); static int sfloader_preset_get_num (fluid_preset_t *preset); static int sfloader_temp_preset_get_num (fluid_preset_t *preset); static int sfloader_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); static int sfloader_temp_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); static gboolean sfloader_preset_foreach_voice (IPItem *item, IPSample *sample, IPGenAmount *gen_array, IPMod *mods, void *data); static void wavetbl_fluidsynth_set_gen_realtime (SwamiWavetbl *swami_wavetbl, IPItem *item, IPItem *layer, guint16 genid, int val); /* data */ static SwamiConfigStaticVars config_vars[] = { { "fluidsynth", "audio_type", G_TOKEN_STRING, {""}}, { NULL, "audio_device", G_TOKEN_STRING, {""}}, { NULL, "audio_bufsize", G_TOKEN_INT, {GINT_TO_POINTER (64)}}, { NULL, "audio_bufcount", G_TOKEN_INT, {GINT_TO_POINTER (16)}}, { NULL, "midi_type", G_TOKEN_STRING, {""}}, { NULL, "midi_device", G_TOKEN_STRING, {""}}, { NULL, "master_gain", G_TOKEN_FLOAT, {v_float:0.0}}, { NULL, "reverb", G_TOKEN_INT, {v_int:1}}, /* 0=disable,1=default,2=custom */ { NULL, "reverb_roomsize", G_TOKEN_FLOAT, {v_float:0.4}}, { NULL, "reverb_damp", G_TOKEN_FLOAT, {v_float:0.0}}, { NULL, "reverb_width", G_TOKEN_FLOAT, {v_float:2.0}}, { NULL, "reverb_level", G_TOKEN_FLOAT, {v_float:4.0}}, { NULL, "chorus", G_TOKEN_INT, {v_int:2}}, /* 0=disable,1=default,2=custom */ { NULL, "chorus_nr", G_TOKEN_INT, {v_int:3}}, /* number of delay lines */ { NULL, "chorus_level", G_TOKEN_FLOAT, {v_float:2.0}}, { NULL, "chorus_freq", G_TOKEN_FLOAT, {v_float:0.3}}, { NULL, "chorus_depth", G_TOKEN_FLOAT, {v_float:8.0}}, { NULL, "chorus_type", G_TOKEN_INT, {v_int:0}} }; #define CONFIG_COUNT (sizeof (config_vars) / sizeof (SwamiConfigStaticVars)) double default_gain; /* default gain fetched from new synth */ static float default_reverb_enable; /* default TRUE/FALSE reverb enable */ static float default_chorus_enable; /* default TRUE/FALSE chorus enable */ /* set plugin information */ SWAMI_PLUGIN_DESC (SWAMI_VERSION_MAJOR, SWAMI_VERSION_MINOR, "fluidsynth", N_("FluidSynth - software wavetable synthesizer plugin"), N_("Josh Green"), "", plugin_fluidsynth_init); /* --- functions --- */ /* plugin init function */ static int plugin_fluidsynth_init (GModule *module, SwamiPlugin *plugin) { int major, minor, micro; swami_config_add_domain ("fluidsynth", SWAMI_CONFIG_CATEGORY_PLUGIN); swami_config_add_static_variables (config_vars, CONFIG_COUNT); fluid_version (&major, &minor, µ); if (major != FLUIDSYNTH_VERSION_MAJOR || minor != FLUIDSYNTH_VERSION_MINOR || micro != FLUIDSYNTH_VERSION_MICRO) { g_critical (_("Plugin compiled with FluidSynth version %d.%d.%d but" " is being linked with %d.%d.%d, aborting plugin init!"), FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO, major, minor, micro); return (SWAMI_FAIL); } /* initialize types */ wavetbl_fluidsynth_get_type (); midi_fluidsynth_get_type (); return (SWAMI_OK); } GType wavetbl_fluidsynth_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (WavetblFluidSynthClass), NULL, NULL, (GClassInitFunc) wavetbl_fluidsynth_class_init, NULL, NULL, sizeof (WavetblFluidSynth), 0, (GInstanceInitFunc) wavetbl_fluidsynth_init, }; item_type = g_type_register_static (SWAMI_TYPE_WAVETBL, "WavetblFluidSynth", &item_info, 0); } return (item_type); } static void wavetbl_fluidsynth_class_init (WavetblFluidSynth *klass) { SwamiWavetblClass *wavetbl_class; wavetbl_class = SWAMI_WAVETBL_CLASS (klass); wavetbl_class->init_driver = wavetbl_fluidsynth_init_driver; wavetbl_class->close_driver = wavetbl_fluidsynth_close_driver; wavetbl_class->load_patch = wavetbl_fluidsynth_load_patch; wavetbl_class->load_temp_item = wavetbl_fluidsynth_load_temp_item; wavetbl_class->set_gen_realtime = wavetbl_fluidsynth_set_gen_realtime; } static void wavetbl_fluidsynth_init (WavetblFluidSynth *wavetbl) { wavetbl->synth = NULL; wavetbl->temp_item = NULL; wavetbl->realtime_noteon = g_malloc0 (sizeof (realtime_noteon_t)); } static GType midi_fluidsynth_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (MidiFluidSynthClass), NULL, NULL, (GClassInitFunc) midi_fluidsynth_class_init, NULL, NULL, sizeof (MidiFluidSynth), 0, (GInstanceInitFunc) midi_fluidsynth_init, }; item_type = g_type_register_static (SWAMI_TYPE_MIDI, "MidiFluidSynth", &item_info, 0); } return (item_type); } static void midi_fluidsynth_class_init (MidiFluidSynth *klass) { GObjectClass *gobject_class; SwamiMidiClass *midi_class; gobject_class = G_OBJECT_CLASS (klass); midi_class = SWAMI_MIDI_CLASS (klass); midi_class->init_driver = midi_fluidsynth_init_driver; midi_class->close_driver = NULL; midi_class->send_event = midi_fluidsynth_send_event; g_object_class_install_property (gobject_class, MIDI_PROP_WAVETBL, g_param_spec_pointer ("wavetbl", "Wavetbl", "Linked WavetblFluidSynth driver", G_PARAM_READWRITE)); gobject_class->set_property = midi_fluidsynth_set_property; gobject_class->get_property = midi_fluidsynth_get_property; } static void midi_fluidsynth_init (MidiFluidSynth *midi) { midi->wavetbl = NULL; } static void midi_fluidsynth_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MidiFluidSynth *midi_fluid = MIDI_FLUIDSYNTH (object); switch (prop_id) { case MIDI_PROP_WAVETBL: midi_fluid->wavetbl = WAVETBL_FLUIDSYNTH (g_value_get_object (value)); break; default: SWAMI_CRITICAL ("Invalid property"); break; } } static void midi_fluidsynth_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MidiFluidSynth *midi_fluid = MIDI_FLUIDSYNTH (object); switch (prop_id) { case MIDI_PROP_WAVETBL: g_value_set_object (value, G_OBJECT (midi_fluid->wavetbl)); break; default: SWAMI_CRITICAL ("Invalid property"); break; } } static int midi_fluidsynth_init_driver (SwamiMidi *swami_midi) { MidiFluidSynth *midi; g_return_val_if_fail (MIDI_IS_FLUIDSYNTH (swami_midi), SWAMI_FAIL); midi = MIDI_FLUIDSYNTH (swami_midi); if (!midi->wavetbl) { g_warning ("Parameter 'wavetbl' of FluidSynth MIDI driver" " has not been set"); return (SWAMI_FAIL); } return (SWAMI_OK); } /* MIDI event routine for FluidSynth MIDI driver */ static int midi_fluidsynth_send_event (SwamiMidi *swami_midi, SwamiMidiEvent event, int chan, int param1, int param2) { MidiFluidSynth *midi; fluid_synth_t *synth; g_return_val_if_fail (MIDI_IS_FLUIDSYNTH (swami_midi), SWAMI_FAIL); midi = MIDI_FLUIDSYNTH (swami_midi); if (!midi->wavetbl || !midi->wavetbl->synth) return (SWAMI_OK); /* silently fail */ synth = midi->wavetbl->synth; switch (event) { case SWAMI_MIDI_NOTE_ON: if (param2 != 0) { fluid_synth_noteon (synth, chan, param1, param2); break; } /* fall through on velocity 0 (note off) */ case SWAMI_MIDI_NOTE_OFF: fluid_synth_noteoff (synth, chan, param1); break; case SWAMI_MIDI_PITCH_WHEEL: fluid_synth_pitch_bend (synth, chan, param1); break; case SWAMI_MIDI_SET_CONTROL: fluid_synth_cc (synth, chan, param1, param2); break; case SWAMI_MIDI_BEND_RANGE: fluid_synth_pitch_wheel_sens (synth, chan, param1); break; case SWAMI_MIDI_PRESET_SELECT: fluid_synth_program_change (synth, chan, param1); break; case SWAMI_MIDI_BANK_SELECT: fluid_synth_bank_select (synth, chan, param1); break; default: break; } return (SWAMI_OK); } /** init function for FluidSynth Swami wavetable driver */ static int wavetbl_fluidsynth_init_driver (SwamiWavetbl *swami_wavetbl) { WavetblFluidSynth *wavetbl; fluid_sfloader_t *loader; int bufsize, bufcount; char *mdriver, *mdevice; char *s, *s2; float f; int reverb_enable, chorus_enable; g_return_val_if_fail (WAVETBL_IS_FLUIDSYNTH (swami_wavetbl), SWAMI_FAIL); wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); memset (wavetbl->realtime_noteon, 0, sizeof (realtime_noteon_t)); wavetbl->settings = new_fluid_settings (); /* 0=disabled, 1=default, 2=custom */ default_reverb_enable = 1; reverb_enable = swami_config_get_int ("fluidsynth", "reverb", NULL); fluid_settings_setstr (wavetbl->settings, "synth.reverb.active", (reverb_enable != 0) ? "yes" : "no"); /* 0=disabled, 1=default, 2=custom */ default_chorus_enable = 1; chorus_enable = swami_config_get_int ("fluidsynth", "chorus", NULL); fluid_settings_setstr (wavetbl->settings, "synth.chorus.active", (chorus_enable != 0) ? "yes" : "no"); bufsize = swami_config_get_int ("fluidsynth", "audio_bufsize", NULL); bufcount = swami_config_get_int ("fluidsynth", "audio_bufcount", NULL); /* only set bufsize/bufcount if not REALLY absurd, otherwise use defaults */ if (bufsize >= 4 && bufsize <= 65536 && bufcount >= 2 && bufcount <= 64) { fluid_settings_setint (wavetbl->settings, "audio.period-size", bufsize); fluid_settings_setint (wavetbl->settings, "audio.periods", bufcount); } s = swami_config_get_string ("fluidsynth", "audio_type"); if (s && strlen (s)) fluid_settings_setstr (wavetbl->settings, "audio.driver", s); else fluid_settings_getstr (wavetbl->settings, "audio.driver", &s); s2 = swami_config_get_string ("fluidsynth", "audio_device"); if (s && s2 && strlen (s2)) { s = g_strdup_printf ("audio.%s.driver", s); fluid_settings_setstr (wavetbl->settings, s, s2); g_free (s); } /* create new fluid synth */ wavetbl->synth = new_fluid_synth (wavetbl->settings); if (!wavetbl->synth) return (SWAMI_FAIL); /* get default gain */ fluid_settings_getnum (wavetbl->settings, "synth.gain", &default_gain); /* hook our sfloader */ loader = g_malloc0 (sizeof (fluid_sfloader_t)); loader->data = wavetbl; loader->free = sfloader_free; loader->load = sfloader_load_sfont; fluid_synth_add_sfloader (wavetbl->synth, loader); wavetbl->audio_driver = new_fluid_audio_driver (wavetbl->settings, wavetbl->synth); /* HACK: load dummy sound font to make temporary preset items work */ fluid_synth_sfload (wavetbl->synth, "!", TRUE); /* sfloader_load_sfont */ mdriver = swami_config_get_string ("fluidsynth", "midi_type"); mdevice = swami_config_get_string ("fluidsynth", "midi_device"); if (mdevice && !strlen (mdevice)) mdevice = NULL; if (mdriver && strlen (mdriver)) { fluid_settings_setstr (wavetbl->settings, "midi.driver", mdriver); if (mdevice) { s = g_strdup_printf ("midi.%s.device", mdriver); fluid_settings_setstr (wavetbl->settings, s, mdevice); g_free (s); } wavetbl->midi_router = new_fluid_midi_router (wavetbl->settings, fluid_synth_handle_midi_event, (void *)(wavetbl->synth)); if (wavetbl->midi_router) { fluid_synth_set_midi_router (wavetbl->synth, wavetbl->midi_router); wavetbl->midi_driver = new_fluid_midi_driver (wavetbl->settings, fluid_midi_router_handle_midi_event, (void *)(wavetbl->midi_router)); if (!wavetbl->midi_driver) g_warning (_("Failed to create FluidSynth MIDI input driver")); } else g_warning (_("Failed to create FluidSynth MIDI input router")); } /* set gain from config */ f = swami_config_get_float ("fluidsynth", "master_gain", NULL); if (f != 0.0) fluid_settings_setnum (wavetbl->settings, "synth.gain", f); if (reverb_enable == 2) /* set custom reverb? */ wavetbl_fluidsynth_update_reverb (wavetbl); if (chorus_enable == 2) /* set custom chorus? */ wavetbl_fluidsynth_update_chorus (wavetbl); return (SWAMI_OK); } /* close function for FluidSynth driver */ static void wavetbl_fluidsynth_close_driver (SwamiWavetbl *swami_wavetbl) { WavetblFluidSynth *wavetbl; g_return_if_fail (WAVETBL_IS_FLUIDSYNTH (swami_wavetbl)); wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); /* un-reference any current active temporary item */ if (wavetbl->temp_item) { instp_item_unref (wavetbl->temp_item); wavetbl->temp_item = NULL; } if (wavetbl->midi_router) { if (wavetbl->midi_driver) delete_fluid_midi_driver (wavetbl->midi_driver); delete_fluid_midi_router (wavetbl->midi_router); } if (wavetbl->audio_driver) delete_fluid_audio_driver (wavetbl->audio_driver); if (wavetbl->synth) delete_fluid_synth (wavetbl->synth); if (wavetbl->settings) delete_fluid_settings (wavetbl->settings); } /* patch load function for FluidSynth driver */ static int wavetbl_fluidsynth_load_patch (SwamiWavetbl *swami_wavetbl, IPItem *patch) { WavetblFluidSynth *wavetbl; char *s; g_return_val_if_fail (WAVETBL_IS_FLUIDSYNTH (swami_wavetbl), SWAMI_FAIL); wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); if (!wavetbl->synth || !INSTP_IS_SFONT (patch)) return (SWAMI_OK); /* fail silently */ /* load sound font by pointer (our FluidSynth sfloader plugin will use it) */ s = g_strdup_printf ("&%p", (void *)patch);/* pointer string for file name */ fluid_synth_sfload (wavetbl->synth, s, TRUE); g_free (s); return (SWAMI_OK); } /* temporary item load function for FluidSynth driver */ static int wavetbl_fluidsynth_load_temp_item (SwamiWavetbl *swami_wavetbl, IPItem *item) { WavetblFluidSynth *wavetbl; g_return_val_if_fail (WAVETBL_IS_FLUIDSYNTH (swami_wavetbl), SWAMI_FAIL); wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); if (wavetbl->temp_item) /* remove reference to any current temp item */ instp_item_unref (wavetbl->temp_item); if (item) instp_item_ref (item); /* add reference to new temp item */ wavetbl->temp_item = item; return (SWAMI_OK); } /* FluidSynth sfloader functions */ /** FluidSynth sfloader "free" function */ static int sfloader_free (fluid_sfloader_t *loader) { g_free (loader); return (0); } /** FluidSynth sfloader "load" function */ static fluid_sfont_t * sfloader_load_sfont (fluid_sfloader_t *loader, const char *filename) { fluid_sfont_t *sfont; sfloader_sfont_data_t *sfont_data; IPItem *item = NULL; /* file name should be a string in the printf form "&%p" where the pointer is a pointer to an IPSFont structure, or "!" for dummy sound font to get temporary preset item to work when no sound fonts loaded (HACK COUGH) */ if (filename[0] == '&') { sscanf (filename, "&%p", (void **)(&item)); if (!item) return (NULL); instp_item_ref (item); /* ++ Add a reference to the sound font */ } else if (filename[0] != '!') return (NULL); /* didn't begin with '&' or '!' */ sfont_data = g_malloc0 (sizeof (sfloader_sfont_data_t)); sfont_data->wavetbl = (WavetblFluidSynth *)(loader->data); sfont_data->sf = (IPSFont *)(item); sfont = g_malloc0 (sizeof (fluid_sfont_t)); sfont->data = sfont_data; sfont->free = sfloader_sfont_free; sfont->get_name = sfloader_sfont_get_name; sfont->get_preset = sfloader_sfont_get_preset; sfont->iteration_start = sfloader_sfont_iteration_start; sfont->iteration_next = sfloader_sfont_iteration_next; return (sfont); } /* sfloader callback to clean up an fluid_sfont_t structure */ static int sfloader_sfont_free (fluid_sfont_t *sfont) { sfloader_sfont_data_t *sfont_data; sfont_data = (sfloader_sfont_data_t *)(sfont->data); if (sfont_data->sf) instp_item_unref (INSTP_ITEM (sfont_data->sf)); /* -- remove reference */ g_free (sfont_data); g_free (sfont); return (0); } /* sfloader callback to get a sound font name */ static char * sfloader_sfont_get_name (fluid_sfont_t *sfont) { return (instp_get_info (INSTP_SFONT (sfont->data), IPINFO_NAME)); } /* sfloader callback to get a preset by bank and preset number */ static fluid_preset_t * sfloader_sfont_get_preset (fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum) { sfloader_sfont_data_t *sfont_data; sfloader_preset_data_t *preset_data; fluid_preset_t* preset; sfont_data = (sfloader_sfont_data_t *)(sfont->data); /* temporary item bank:preset requested? */ if (bank == swami_wavetbl_temp_bank && prenum == swami_wavetbl_temp_psetnum) { g_object_ref (G_OBJECT (sfont_data->wavetbl)); /* ++ inc wavetbl ref */ preset = g_malloc0 (sizeof (fluid_preset_t)); preset->data = sfont_data->wavetbl; preset->free = sfloader_temp_preset_free; preset->get_name = sfloader_temp_preset_get_name; preset->get_banknum = sfloader_temp_preset_get_banknum; preset->get_num = sfloader_temp_preset_get_num; preset->noteon = sfloader_temp_preset_noteon; } else /* regular preset request */ { IPPreset *pset; if (!sfont_data->sf) /* for temporary preset sound font HACK */ return (NULL); pset = instp_find_preset (INSTP_SFONT (sfont_data->sf), NULL, bank, prenum, NULL); if (!pset) return (NULL); preset_data = g_malloc (sizeof (sfloader_preset_data_t)); g_object_ref (G_OBJECT (sfont_data->wavetbl)); /* ++ inc wavetbl ref */ preset_data->wavetbl = sfont_data->wavetbl; instp_item_ref (INSTP_ITEM (pset)); /* ++ add reference to preset */ preset_data->preset = pset; preset = g_malloc0 (sizeof (fluid_preset_t)); preset->data = preset_data; preset->free = sfloader_preset_free; preset->get_name = sfloader_preset_get_name; preset->get_banknum = sfloader_preset_get_banknum; preset->get_num = sfloader_preset_get_num; preset->noteon = sfloader_preset_noteon; } return (preset); } /* sfloader callback to start a sound font preset iteration */ static void sfloader_sfont_iteration_start (fluid_sfont_t *sfont) { } /* sfloader callback to get next preset in a sound font iteration */ static int sfloader_sfont_iteration_next (fluid_sfont_t *sfont, fluid_preset_t *preset) { return (0); } /* sfloader callback to clean up an fluid_preset_t structure */ static int sfloader_preset_free (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data; preset_data = preset->data; /* -- remove item reference */ instp_item_unref (INSTP_ITEM (preset_data->preset)); /* remove wavetable object reference */ g_object_unref (G_OBJECT (preset_data->wavetbl)); g_free (preset_data); g_free (preset); return (0); } /* sfloader callback to clean up a temporary item preset structure */ static int sfloader_temp_preset_free (fluid_preset_t *preset) { g_object_unref (G_OBJECT (preset->data)); /* -- remove wavetbl obj ref */ g_free (preset); return (0); } /* sfloader callback to get the name of a preset */ static char * sfloader_preset_get_name (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = preset->data; return (preset_data->preset->name); } /* sfloader callback to get name of temporary preset */ static char * sfloader_temp_preset_get_name (fluid_preset_t *preset) { return (_("")); } /* sfloader callback to get the bank number of a preset */ static int sfloader_preset_get_banknum (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = preset->data; return (preset_data->preset->bank); } /* sfloader callback to get the bank number of temporary preset */ static int sfloader_temp_preset_get_banknum (fluid_preset_t *preset) { return (swami_wavetbl_temp_bank); } /* sfloader callback to get the preset number of a preset */ static int sfloader_preset_get_num (fluid_preset_t *preset) { sfloader_preset_data_t *preset_data = preset->data; return (preset_data->preset->psetnum); } /* sfloader callback to get the preset number of temporary preset */ static int sfloader_temp_preset_get_num (fluid_preset_t *preset) { return (swami_wavetbl_temp_psetnum); } /* sfloader callback for a noteon event */ static int sfloader_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { sfloader_preset_data_t *preset_data = preset->data; sfloader_noteon_data_t ndata = { preset_data->wavetbl, FALSE, chan, key, vel }; /* let libsoundfont do the dirty work :) */ instp_item_foreach_voice (INSTP_ITEM (preset_data->preset), key, vel, (IPItemForeachVoiceFunc)sfloader_preset_foreach_voice, &ndata); return (0); } /* handles noteon event for temporary item */ static int sfloader_temp_preset_noteon (fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) { sfloader_noteon_data_t ndata = { WAVETBL_FLUIDSYNTH (preset->data), TRUE, chan, key, vel }; realtime_noteon_t *n; WavetblFluidSynth *wavetbl; wavetbl = preset->data; /* wavetbl object is stored in data field */ /* fetch the current temporary item */ if (!wavetbl->temp_item) return (0); /* no item? Do nothing.. */ /* initialize realtime note on data */ n = wavetbl->realtime_noteon; if (n->item) instp_item_unref (n->item); instp_item_ref (wavetbl->temp_item); n->item = wavetbl->temp_item; n->key = key; n->vel = vel; n->count = 0; /* let libsoundfont do the dirty work :) */ instp_item_foreach_voice (wavetbl->temp_item, key, vel, (IPItemForeachVoiceFunc)sfloader_preset_foreach_voice, &ndata); return (0); } /* function to call for each voice in a preset */ static gboolean sfloader_preset_foreach_voice (IPItem *item, IPSample *sample, IPGenAmount *gen_array, IPMod *mods, void *data) { sfloader_noteon_data_t *ndata = (sfloader_noteon_data_t *)data; fluid_voice_t *voice; fluid_sample_t *wusample; fluid_mod_t *wumod; IPSampleStore *store; int i; /* make sure sample is valid and fetch the fastest readable store */ if (!sample->sampledata || sample->sampletype & IPSAMPLE_TYPE_ROM || !(store = instp_sample_data_find_store (sample->sampledata, 0, IPSAMPLE_STORE_FIND_FASTEST | IPSAMPLE_STORE_FIND_READABLE))) return (TRUE); /* fastest sample store is type RAM? if not, then create it */ if (store->method->type != IPSAMPLE_METHOD_RAM) if (!(store = instp_sample_store_duplicate (sample->sampledata, store, IPSAMPLE_METHOD_RAM))) return (TRUE); wusample = g_malloc0 (sizeof (fluid_sample_t)); strcpy (wusample->name, sample->name); wusample->start = 0; wusample->end = instp_sample_get_size (sample) - 1; wusample->loopstart = sample->loopstart; wusample->loopend = sample->loopend; wusample->samplerate = sample->samplerate; wusample->origpitch = sample->origpitch; wusample->pitchadj = sample->pitchadj; wusample->sampletype = sample->sampletype; wusample->valid = 1; wusample->data = instp_sample_method_RAM_get_pointer (sample->sampledata, store); /* allocate the fluidsynth voice */ voice = fluid_synth_alloc_voice (ndata->wavetbl->synth, wusample, ndata->chan, ndata->key, ndata->vel); if (!voice) { g_free (wusample); return (TRUE); } /* if original item is of type sample, force looping */ if (INSTP_IS_SAMPLE (item)) gen_array[IPGEN_SAMPLE_MODES].uword = IPSAMPLE_LOOP; /* set generator parameters */ for (i = 0; i < IPGEN_COUNT; i++) fluid_voice_gen_set (voice, i, (float)(gen_array[i].sword)); while (mods) { /* printf ("MOD src=%x amtsrc=%x dest=%d amount=%d\n", mods->src, mods->amtsrc, mods->dest, mods->amount); */ wumod = fluid_mod_new (); wumod->dest = mods->dest; wumod->src1 = mods->src & IPMOD_INDEX_MASK; wumod->flags1 = ((mods->src & (IPMOD_DIRECTION_FLAG | IPMOD_POLARITY_FLAG | IPMOD_TYPE_MASK)) >> IPMOD_DIRECTION_SHIFT) | ((mods->src & IPMOD_CC_FLAG) ? FLUID_MOD_CC : 0); wumod->src2 = mods->amtsrc & IPMOD_INDEX_MASK; wumod->flags2 = ((mods->amtsrc & (IPMOD_DIRECTION_FLAG | IPMOD_POLARITY_FLAG | IPMOD_TYPE_MASK)) >> IPMOD_DIRECTION_SHIFT) | ((mods->amtsrc & IPMOD_CC_FLAG) ? FLUID_MOD_CC : 0); wumod->amount = mods->amount; fluid_voice_add_mod (voice, wumod, FLUID_VOICE_OVERWRITE); fluid_mod_delete (wumod); mods = instp_mod_next (mods); } fluid_synth_start_voice (ndata->wavetbl->synth, voice); /* let 'er rip */ /* only temporary audible is set up for real time control */ if (ndata->realtime) { realtime_noteon_t *n = ndata->wavetbl->realtime_noteon; if (n->count < MAX_REALTIME_VOICES) n->voices[n->count++] = voice; } return (TRUE); } void wavetbl_fluidsynth_set_gain (WavetblFluidSynth *wavetbl, float gain) { g_return_if_fail (wavetbl != NULL); g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); if (!wavetbl->synth) return; fluid_synth_set_gain (wavetbl->synth, gain); } void wavetbl_fluidsynth_set_reverb_enable (WavetblFluidSynth *wavetbl, gboolean enable) { g_return_if_fail (wavetbl != NULL); g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); if (!wavetbl->synth) return; fluid_synth_set_reverb_on (wavetbl->synth, enable != 0); } void wavetbl_fluidsynth_set_chorus_enable (WavetblFluidSynth *wavetbl, gboolean enable) { g_return_if_fail (wavetbl != NULL); g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); if (!wavetbl->synth) return; fluid_synth_set_chorus_on (wavetbl->synth, enable != 0); } void wavetbl_fluidsynth_update_reverb (WavetblFluidSynth *wavetbl) { float roomsize, damp, width, level; g_return_if_fail (wavetbl != NULL); g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); roomsize = swami_config_get_float ("fluidsynth", "reverb_roomsize", NULL); damp = swami_config_get_float ("fluidsynth", "reverb_damp", NULL); width = swami_config_get_float ("fluidsynth", "reverb_width", NULL); level = swami_config_get_float ("fluidsynth", "reverb_level", NULL); if (!wavetbl->synth) return; fluid_synth_set_reverb (wavetbl->synth, roomsize, damp, width, level); } void wavetbl_fluidsynth_update_chorus (WavetblFluidSynth *wavetbl) { int nr, type; float level, freq, depth; g_return_if_fail (wavetbl != NULL); g_return_if_fail (SWAMI_IS_WAVETBL (wavetbl)); nr = swami_config_get_int ("fluidsynth", "chorus_nr", NULL); level = swami_config_get_float ("fluidsynth", "chorus_level", NULL); freq = swami_config_get_float ("fluidsynth", "chorus_freq", NULL); depth = swami_config_get_float ("fluidsynth", "chorus_depth", NULL); type = swami_config_get_int ("fluidsynth", "chorus_type", NULL); if (!wavetbl->synth) return; fluid_synth_set_chorus (wavetbl->synth, nr, level, freq, depth, type); } static void wavetbl_fluidsynth_set_gen_realtime (SwamiWavetbl *swami_wavetbl, IPItem *item, IPItem *layer, guint16 genid, int val) { WavetblFluidSynth *wavetbl; enum { GPZ, PZ, GIZ, IZ }; IPGenAmount gens[4]; IPZone *pzone, *izone; realtime_noteon_t *noteon; fluid_voice_t *voice; int voicendx = 0; int note, velocity; gboolean gpz_set, pz_set, giz_set, iz_set; gboolean set; g_return_if_fail (WAVETBL_IS_FLUIDSYNTH (swami_wavetbl)); wavetbl = WAVETBL_FLUIDSYNTH (swami_wavetbl); /* make sure its the realtime controllable item */ if (wavetbl->realtime_noteon->item != item) return; noteon = wavetbl->realtime_noteon; note = noteon->key; velocity = noteon->vel; G_LOCK (instp_voice_lock); switch (item->type) { case IPITEM_PRESET: pzone = INSTP_PRESET (item)->zone; gpz_set = FALSE; if (pzone && !pzone->refitem) /* global zone? */ { /* global preset zone is the layer to set? */ if ((void *)pzone == (void *)layer) { gpz_set = TRUE; gens[GPZ].sword = val; } else instp_zone_get_gen (pzone, genid, &gens[GPZ]); pzone = instp_zone_next (pzone); } else gens[GPZ].sword = 0; while (pzone) /* loop over preset zones */ { if (!instp_zone_in_range (pzone, note, velocity)) { pzone = instp_zone_next (pzone); continue; } /* get preset zone gen, sets to default value if not set in zone */ set = instp_zone_get_gen (pzone, genid, &gens[PZ]); pz_set = FALSE; /* preset zone is the layer to set? */ if ((void *)pzone == (void *)layer) { pz_set = TRUE; gens[PZ].sword = val; } else if (gpz_set && !set) { /* global preset zone set and preset zone not set? */ pz_set = TRUE; gens[PZ] = gens[GPZ]; } izone = INSTP_INST (pzone->refitem)->zone; giz_set = FALSE; if (izone && !izone->refitem) /* global instrument zone? */ { /* global inst zone is layer to set? */ if ((void *)izone == (void *)layer) { giz_set = TRUE; gens[GIZ].sword = val; } else instp_zone_get_gen (izone, genid, &gens[GIZ]); izone = instp_zone_next (izone); } else gens[GIZ].sword = instp_gen_info[genid].def; while (izone) /* loop over instrument zones */ { if (!instp_zone_in_range (izone, note, velocity)) { izone = instp_zone_next (izone); continue; } set = instp_zone_get_gen (izone, genid, &gens[IZ]); iz_set = FALSE; /* inst zone is the layer to set? */ if ((void *)izone == (void *)layer) { iz_set = TRUE; gens[IZ].sword = val; } else if (giz_set && !set) { /* global inst zone set and inst zone not set? */ iz_set = TRUE; gens[IZ] = gens[GIZ]; } if ((pz_set || iz_set) && voicendx < noteon->count) { instp_genid_offset (genid, &gens[IZ], gens[PZ]); voice = noteon->voices[voicendx]; if (voice) { fluid_voice_gen_set (voice, genid, gens[IZ].sword); fluid_voice_update_param (voice, genid); } } voicendx++; izone = instp_zone_next (izone); } pzone = instp_zone_next (pzone); } break; case IPITEM_INST: izone = INSTP_INST (item)->zone; giz_set = FALSE; if (izone && !izone->refitem) /* global zone? */ { /* global inst zone is layer to set? */ if ((void *)izone == (void *)layer) { giz_set = TRUE; gens[GIZ].sword = val; } else instp_zone_get_gen (izone, genid, &gens[GIZ]); izone = instp_zone_next (izone); } else gens[GIZ].sword = instp_gen_info[genid].def; while (izone) /* loop over instrument zones */ { if (!instp_zone_in_range (izone, note, velocity)) { izone = instp_zone_next (izone); continue; } set = instp_zone_get_gen (izone, genid, &gens[IZ]); iz_set = FALSE; /* inst zone is the layer to set? */ if ((void *)izone == (void *)layer) { iz_set = TRUE; gens[IZ].sword = val; } else if (giz_set && !set) { /* global inst zone set and inst zone not set? */ iz_set = TRUE; gens[IZ] = gens[GIZ]; } if (iz_set && voicendx < noteon->count) { voice = noteon->voices[voicendx]; if (voice) { fluid_voice_gen_set (voice, genid, gens[IZ].sword); fluid_voice_update_param (voice, genid); } } voicendx++; izone = instp_zone_next (izone); } break; case IPITEM_SAMPLE: if (noteon->count == 0 || noteon->item != item) break; voice = noteon->voices[0]; /* samples have only 1 voice */ if (voice) { fluid_voice_gen_set (voice, genid, val); fluid_voice_update_param (voice, genid); } break; } G_UNLOCK (instp_voice_lock); }