From 2814f73c87c5a56a0ee32886d82cd66213f19061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Mon, 12 Jun 2006 15:55:38 +0000 Subject: [PATCH] imported a stripped down fluidsynth 1.0.7 * a shared library is built, no audio driver is included * implemented some platform specific functions (fluid_sys.*) * a test app is provided in fluidsynth.c * on target Haiku, posix mutex are used, else mutexes are implemented with simple semaphores (I could be wrong here) well I hope it can provide a good enough backend for a softsynth Note that needed SF2 banks would probably not be included. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17799 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/libs/fluidsynth/fluidsynth.h | 105 + headers/libs/fluidsynth/fluidsynth/audio.h | 56 + headers/libs/fluidsynth/fluidsynth/event.h | 114 + headers/libs/fluidsynth/fluidsynth/gen.h | 129 + headers/libs/fluidsynth/fluidsynth/log.h | 85 + headers/libs/fluidsynth/fluidsynth/midi.h | 134 + headers/libs/fluidsynth/fluidsynth/misc.h | 65 + headers/libs/fluidsynth/fluidsynth/mod.h | 112 + headers/libs/fluidsynth/fluidsynth/ramsfont.h | 113 + headers/libs/fluidsynth/fluidsynth/seq.h | 109 + headers/libs/fluidsynth/fluidsynth/seqbind.h | 44 + headers/libs/fluidsynth/fluidsynth/settings.h | 223 ++ headers/libs/fluidsynth/fluidsynth/sfont.h | 193 + headers/libs/fluidsynth/fluidsynth/shell.h | 137 + headers/libs/fluidsynth/fluidsynth/synth.h | 700 ++++ headers/libs/fluidsynth/fluidsynth/types.h | 66 + headers/libs/fluidsynth/fluidsynth/version.h | 44 + headers/libs/fluidsynth/fluidsynth/voice.h | 97 + src/libs/Jamfile | 1 + src/libs/fluidsynth/AUTHORS | 117 + src/libs/fluidsynth/COPYING | 482 +++ src/libs/fluidsynth/Jamfile | 3 + src/libs/fluidsynth/src/Jamfile | 40 + src/libs/fluidsynth/src/config.h | 191 + src/libs/fluidsynth/src/fluid_adriver.c | 308 ++ src/libs/fluidsynth/src/fluid_adriver.h | 39 + src/libs/fluidsynth/src/fluid_aufile.c | 204 ++ src/libs/fluidsynth/src/fluid_chan.c | 352 ++ src/libs/fluidsynth/src/fluid_chan.h | 105 + src/libs/fluidsynth/src/fluid_chorus.c | 605 ++++ src/libs/fluidsynth/src/fluid_chorus.h | 56 + src/libs/fluidsynth/src/fluid_cmd.c | 1852 ++++++++++ src/libs/fluidsynth/src/fluid_cmd.h | 102 + src/libs/fluidsynth/src/fluid_conv.c | 320 ++ src/libs/fluidsynth/src/fluid_conv.h | 62 + src/libs/fluidsynth/src/fluid_defsfont.c | 3185 +++++++++++++++++ src/libs/fluidsynth/src/fluid_defsfont.h | 608 ++++ src/libs/fluidsynth/src/fluid_dsp_core.c | 26 + src/libs/fluidsynth/src/fluid_dsp_float.c | 281 ++ src/libs/fluidsynth/src/fluid_dsp_simple.c | 120 + src/libs/fluidsynth/src/fluid_dsp_sse.c | 387 ++ src/libs/fluidsynth/src/fluid_event.c | 495 +++ src/libs/fluidsynth/src/fluid_event_priv.h | 81 + src/libs/fluidsynth/src/fluid_gen.c | 148 + src/libs/fluidsynth/src/fluid_gen.h | 44 + src/libs/fluidsynth/src/fluid_hash.c | 388 ++ src/libs/fluidsynth/src/fluid_hash.h | 64 + src/libs/fluidsynth/src/fluid_io.c | 121 + src/libs/fluidsynth/src/fluid_io.h | 42 + src/libs/fluidsynth/src/fluid_ladspa.c | 1118 ++++++ src/libs/fluidsynth/src/fluid_ladspa.h | 240 ++ src/libs/fluidsynth/src/fluid_lash.c | 300 ++ src/libs/fluidsynth/src/fluid_lash.h | 51 + src/libs/fluidsynth/src/fluid_list.c | 257 ++ src/libs/fluidsynth/src/fluid_list.h | 61 + src/libs/fluidsynth/src/fluid_mdriver.c | 184 + src/libs/fluidsynth/src/fluid_mdriver.h | 42 + src/libs/fluidsynth/src/fluid_midi.c | 1529 ++++++++ src/libs/fluidsynth/src/fluid_midi.h | 324 ++ src/libs/fluidsynth/src/fluid_midi_router.c | 879 +++++ src/libs/fluidsynth/src/fluid_midi_router.h | 112 + src/libs/fluidsynth/src/fluid_mod.c | 429 +++ src/libs/fluidsynth/src/fluid_mod.h | 40 + src/libs/fluidsynth/src/fluid_phase.h | 177 + src/libs/fluidsynth/src/fluid_ramsfont.c | 1115 ++++++ src/libs/fluidsynth/src/fluid_ramsfont.h | 114 + src/libs/fluidsynth/src/fluid_rev.c | 560 +++ src/libs/fluidsynth/src/fluid_rev.h | 67 + src/libs/fluidsynth/src/fluid_seq.c | 1037 ++++++ src/libs/fluidsynth/src/fluid_seqbind.c | 159 + src/libs/fluidsynth/src/fluid_settings.c | 883 +++++ src/libs/fluidsynth/src/fluid_settings.h | 55 + src/libs/fluidsynth/src/fluid_sfont.h | 68 + src/libs/fluidsynth/src/fluid_sse.h | 263 ++ src/libs/fluidsynth/src/fluid_strtok.c | 123 + src/libs/fluidsynth/src/fluid_strtok.h | 44 + src/libs/fluidsynth/src/fluid_synth.c | 3083 ++++++++++++++++ src/libs/fluidsynth/src/fluid_synth.h | 221 ++ src/libs/fluidsynth/src/fluid_sys.c | 1334 +++++++ src/libs/fluidsynth/src/fluid_sys.h | 305 ++ src/libs/fluidsynth/src/fluid_tuning.c | 108 + src/libs/fluidsynth/src/fluid_tuning.h | 64 + src/libs/fluidsynth/src/fluid_voice.c | 2091 +++++++++++ src/libs/fluidsynth/src/fluid_voice.h | 288 ++ src/libs/fluidsynth/src/fluidsynth.c | 705 ++++ src/libs/fluidsynth/src/fluidsynth_priv.h | 326 ++ 86 files changed, 32111 insertions(+) create mode 100644 headers/libs/fluidsynth/fluidsynth.h create mode 100644 headers/libs/fluidsynth/fluidsynth/audio.h create mode 100644 headers/libs/fluidsynth/fluidsynth/event.h create mode 100644 headers/libs/fluidsynth/fluidsynth/gen.h create mode 100644 headers/libs/fluidsynth/fluidsynth/log.h create mode 100644 headers/libs/fluidsynth/fluidsynth/midi.h create mode 100644 headers/libs/fluidsynth/fluidsynth/misc.h create mode 100644 headers/libs/fluidsynth/fluidsynth/mod.h create mode 100644 headers/libs/fluidsynth/fluidsynth/ramsfont.h create mode 100644 headers/libs/fluidsynth/fluidsynth/seq.h create mode 100644 headers/libs/fluidsynth/fluidsynth/seqbind.h create mode 100644 headers/libs/fluidsynth/fluidsynth/settings.h create mode 100644 headers/libs/fluidsynth/fluidsynth/sfont.h create mode 100644 headers/libs/fluidsynth/fluidsynth/shell.h create mode 100644 headers/libs/fluidsynth/fluidsynth/synth.h create mode 100644 headers/libs/fluidsynth/fluidsynth/types.h create mode 100644 headers/libs/fluidsynth/fluidsynth/version.h create mode 100644 headers/libs/fluidsynth/fluidsynth/voice.h create mode 100644 src/libs/fluidsynth/AUTHORS create mode 100644 src/libs/fluidsynth/COPYING create mode 100644 src/libs/fluidsynth/Jamfile create mode 100644 src/libs/fluidsynth/src/Jamfile create mode 100644 src/libs/fluidsynth/src/config.h create mode 100644 src/libs/fluidsynth/src/fluid_adriver.c create mode 100644 src/libs/fluidsynth/src/fluid_adriver.h create mode 100644 src/libs/fluidsynth/src/fluid_aufile.c create mode 100644 src/libs/fluidsynth/src/fluid_chan.c create mode 100644 src/libs/fluidsynth/src/fluid_chan.h create mode 100644 src/libs/fluidsynth/src/fluid_chorus.c create mode 100644 src/libs/fluidsynth/src/fluid_chorus.h create mode 100644 src/libs/fluidsynth/src/fluid_cmd.c create mode 100644 src/libs/fluidsynth/src/fluid_cmd.h create mode 100644 src/libs/fluidsynth/src/fluid_conv.c create mode 100644 src/libs/fluidsynth/src/fluid_conv.h create mode 100644 src/libs/fluidsynth/src/fluid_defsfont.c create mode 100644 src/libs/fluidsynth/src/fluid_defsfont.h create mode 100644 src/libs/fluidsynth/src/fluid_dsp_core.c create mode 100644 src/libs/fluidsynth/src/fluid_dsp_float.c create mode 100644 src/libs/fluidsynth/src/fluid_dsp_simple.c create mode 100644 src/libs/fluidsynth/src/fluid_dsp_sse.c create mode 100644 src/libs/fluidsynth/src/fluid_event.c create mode 100644 src/libs/fluidsynth/src/fluid_event_priv.h create mode 100644 src/libs/fluidsynth/src/fluid_gen.c create mode 100644 src/libs/fluidsynth/src/fluid_gen.h create mode 100644 src/libs/fluidsynth/src/fluid_hash.c create mode 100644 src/libs/fluidsynth/src/fluid_hash.h create mode 100644 src/libs/fluidsynth/src/fluid_io.c create mode 100644 src/libs/fluidsynth/src/fluid_io.h create mode 100644 src/libs/fluidsynth/src/fluid_ladspa.c create mode 100644 src/libs/fluidsynth/src/fluid_ladspa.h create mode 100644 src/libs/fluidsynth/src/fluid_lash.c create mode 100644 src/libs/fluidsynth/src/fluid_lash.h create mode 100644 src/libs/fluidsynth/src/fluid_list.c create mode 100644 src/libs/fluidsynth/src/fluid_list.h create mode 100644 src/libs/fluidsynth/src/fluid_mdriver.c create mode 100644 src/libs/fluidsynth/src/fluid_mdriver.h create mode 100644 src/libs/fluidsynth/src/fluid_midi.c create mode 100644 src/libs/fluidsynth/src/fluid_midi.h create mode 100644 src/libs/fluidsynth/src/fluid_midi_router.c create mode 100644 src/libs/fluidsynth/src/fluid_midi_router.h create mode 100644 src/libs/fluidsynth/src/fluid_mod.c create mode 100644 src/libs/fluidsynth/src/fluid_mod.h create mode 100644 src/libs/fluidsynth/src/fluid_phase.h create mode 100644 src/libs/fluidsynth/src/fluid_ramsfont.c create mode 100644 src/libs/fluidsynth/src/fluid_ramsfont.h create mode 100644 src/libs/fluidsynth/src/fluid_rev.c create mode 100644 src/libs/fluidsynth/src/fluid_rev.h create mode 100644 src/libs/fluidsynth/src/fluid_seq.c create mode 100644 src/libs/fluidsynth/src/fluid_seqbind.c create mode 100644 src/libs/fluidsynth/src/fluid_settings.c create mode 100644 src/libs/fluidsynth/src/fluid_settings.h create mode 100644 src/libs/fluidsynth/src/fluid_sfont.h create mode 100644 src/libs/fluidsynth/src/fluid_sse.h create mode 100644 src/libs/fluidsynth/src/fluid_strtok.c create mode 100644 src/libs/fluidsynth/src/fluid_strtok.h create mode 100644 src/libs/fluidsynth/src/fluid_synth.c create mode 100644 src/libs/fluidsynth/src/fluid_synth.h create mode 100644 src/libs/fluidsynth/src/fluid_sys.c create mode 100644 src/libs/fluidsynth/src/fluid_sys.h create mode 100644 src/libs/fluidsynth/src/fluid_tuning.c create mode 100644 src/libs/fluidsynth/src/fluid_tuning.h create mode 100644 src/libs/fluidsynth/src/fluid_voice.c create mode 100644 src/libs/fluidsynth/src/fluid_voice.h create mode 100644 src/libs/fluidsynth/src/fluidsynth.c create mode 100644 src/libs/fluidsynth/src/fluidsynth_priv.h diff --git a/headers/libs/fluidsynth/fluidsynth.h b/headers/libs/fluidsynth/fluidsynth.h new file mode 100644 index 0000000000..9e59a1c056 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth.h @@ -0,0 +1,105 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_H +#define _FLUIDSYNTH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) +#if defined(FLUIDSYNTH_DLL_EXPORTS) +#define FLUIDSYNTH_API __declspec(dllexport) +#elif defined(FLUIDSYNTH_NOT_A_DLL) +#define FLUIDSYNTH_API +#else +#define FLUIDSYNTH_API __declspec(dllimport) +#endif + +#elif defined(MACOS9) +#define FLUIDSYNTH_API __declspec(export) + +#else +#define FLUIDSYNTH_API +#endif + + + /** + + \file fluidsynth.h + + \brief fluidsynth is a real-time SoundFont(R) synthesizer. This is + the header of the fluidsynth library and contains the + synthesizer's public API. + + Depending on how you want to use or extend the synthesizer you + will need different API functions. You probably do not need all + of them. Here is what you might want to do: + + o Embedded synthesizer: create a new synthesizer and send MIDI + events to it. The sound goes directly to the audio output of + your system. + + o Plugin synthesizer: create a synthesizer and send MIDI events + but pull the audio back into your application. + + o SoundFont plugin: create a new type of "SoundFont" and allow + the synthesizer to load your type of SoundFonts. + + o MIDI input: Create a MIDI handler to read the MIDI input on your + machine and send the MIDI events directly to the synthesizer. + + o MIDI files: Open MIDI files and send the MIDI events to the + synthesizer. + + o Command lines: You can send textual commands to the synthesizer. + + + SoundFont(R) is a registered trademark of E-mu Systems, Inc. + + */ + +#include "fluidsynth/types.h" +#include "fluidsynth/settings.h" +#include "fluidsynth/synth.h" +#include "fluidsynth/shell.h" +#include "fluidsynth/sfont.h" +#include "fluidsynth/ramsfont.h" +#include "fluidsynth/audio.h" +#include "fluidsynth/event.h" +#include "fluidsynth/midi.h" +#include "fluidsynth/seq.h" +#include "fluidsynth/seqbind.h" +#include "fluidsynth/log.h" +#include "fluidsynth/misc.h" +#include "fluidsynth/mod.h" +#include "fluidsynth/gen.h" +#include "fluidsynth/voice.h" +#include "fluidsynth/version.h" + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/audio.h b/headers/libs/fluidsynth/fluidsynth/audio.h new file mode 100644 index 0000000000..45d6935657 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/audio.h @@ -0,0 +1,56 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_AUDIO_H +#define _FLUIDSYNTH_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + /** Audio driver + * + * + */ + + + /** The function should return non-zero if an error occured. */ +typedef int (*fluid_audio_func_t)(void* data, int len, + int nin, float** in, + int nout, float** out); + +FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); + +FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, + void* data); + +FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t* driver); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_AUDIO_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/event.h b/headers/libs/fluidsynth/fluidsynth/event.h new file mode 100644 index 0000000000..c724cbb3db --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/event.h @@ -0,0 +1,114 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_EVENT_H +#define _FLUIDSYNTH_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +enum fluid_seq_event_type { + FLUID_SEQ_NOTE = 0, + FLUID_SEQ_NOTEON, + FLUID_SEQ_NOTEOFF, + FLUID_SEQ_ALLSOUNDSOFF, + FLUID_SEQ_ALLNOTESOFF, + FLUID_SEQ_BANKSELECT, + FLUID_SEQ_PROGRAMCHANGE, + FLUID_SEQ_PROGRAMSELECT, + FLUID_SEQ_PITCHBEND, + FLUID_SEQ_PITCHWHHELSENS, + FLUID_SEQ_MODULATION, + FLUID_SEQ_SUSTAIN, + FLUID_SEQ_CONTROLCHANGE, + FLUID_SEQ_PAN, + FLUID_SEQ_VOLUME, + FLUID_SEQ_REVERBSEND, + FLUID_SEQ_CHORUSSEND, + FLUID_SEQ_TIMER, + FLUID_SEQ_ANYCONTROLCHANGE, // used for remove_events only + FLUID_SEQ_LASTEVENT +}; + +/* Event alloc/free */ +FLUIDSYNTH_API fluid_event_t* new_fluid_event(void); +FLUIDSYNTH_API void delete_fluid_event(fluid_event_t* evt); + +/* Initializing events */ +FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t* evt, short src); +FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t* evt, short dest); + +/* Timer events */ +FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data); + +/* Note events */ +FLUIDSYNTH_API void fluid_event_note(fluid_event_t* evt, int channel, + short key, short vel, + unsigned int duration); + +FLUIDSYNTH_API void fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel); +FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_t* evt, int channel, short key); +FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_t* evt, int channel); +FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_t* evt, int channel); + +/* Instrument selection */ +FLUIDSYNTH_API void fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num); +FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t* evt, int channel, short preset_num); +FLUIDSYNTH_API void fluid_event_program_select(fluid_event_t* evt, int channel, unsigned int sfont_id, short bank_num, short preset_num); + +/* Real-time generic instrument controllers */ +FLUIDSYNTH_API +void fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val); + +/* Real-time instrument controllers shortcuts */ +FLUIDSYNTH_API void fluid_event_pitch_bend(fluid_event_t* evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_pan(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_volume(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val); + +/* Only for removing events */ +FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel); + +/* Accessing event data */ +FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_source(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_dest(fluid_event_t* evt); +FLUIDSYNTH_API int fluid_event_get_channel(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_key(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_control(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_value(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_program(fluid_event_t* evt); +FLUIDSYNTH_API void* fluid_event_get_data(fluid_event_t* evt); +FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_t* evt); +FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_t* evt); +FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_t* evt); +FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_t* evt); + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_EVENT_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/gen.h b/headers/libs/fluidsynth/fluidsynth/gen.h new file mode 100644 index 0000000000..bdd4857a38 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/gen.h @@ -0,0 +1,129 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_GEN_H +#define _FLUIDSYNTH_GEN_H + +#ifdef __cplusplus +extern "C" { +#endif + + + /** List of generator numbers + Soundfont 2.01 specifications section 8.1.3 */ +enum fluid_gen_type { + GEN_STARTADDROFS, + GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, + GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS, + GEN_MODLFOTOPITCH, + GEN_VIBLFOTOPITCH, + GEN_MODENVTOPITCH, + GEN_FILTERFC, + GEN_FILTERQ, + GEN_MODLFOTOFILTERFC, + GEN_MODENVTOFILTERFC, + GEN_ENDADDRCOARSEOFS, + GEN_MODLFOTOVOL, + GEN_UNUSED1, + GEN_CHORUSSEND, + GEN_REVERBSEND, + GEN_PAN, + GEN_UNUSED2, + GEN_UNUSED3, + GEN_UNUSED4, + GEN_MODLFODELAY, + GEN_MODLFOFREQ, + GEN_VIBLFODELAY, + GEN_VIBLFOFREQ, + GEN_MODENVDELAY, + GEN_MODENVATTACK, + GEN_MODENVHOLD, + GEN_MODENVDECAY, + GEN_MODENVSUSTAIN, + GEN_MODENVRELEASE, + GEN_KEYTOMODENVHOLD, + GEN_KEYTOMODENVDECAY, + GEN_VOLENVDELAY, + GEN_VOLENVATTACK, + GEN_VOLENVHOLD, + GEN_VOLENVDECAY, + GEN_VOLENVSUSTAIN, + GEN_VOLENVRELEASE, + GEN_KEYTOVOLENVHOLD, + GEN_KEYTOVOLENVDECAY, + GEN_INSTRUMENT, + GEN_RESERVED1, + GEN_KEYRANGE, + GEN_VELRANGE, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, + GEN_VELOCITY, + GEN_ATTENUATION, + GEN_RESERVED2, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_COARSETUNE, + GEN_FINETUNE, + GEN_SAMPLEID, + GEN_SAMPLEMODE, + GEN_RESERVED3, + GEN_SCALETUNE, + GEN_EXCLUSIVECLASS, + GEN_OVERRIDEROOTKEY, + + /* the initial pitch is not a "standard" generator. It is not + * mentioned in the list of generator in the SF2 specifications. It + * is used, however, as the destination for the default pitch wheel + * modulator. */ + GEN_PITCH, + GEN_LAST +}; + + + /* + * fluid_gen_t + * Sound font generator + */ +typedef struct _fluid_gen_t +{ + unsigned char flags; /* is it used or not */ + double val; /* The nominal value */ + double mod; /* Change by modulators */ + double nrpn; /* Change by NRPN messages */ +} fluid_gen_t; + +enum fluid_gen_flags +{ + GEN_UNUSED, + GEN_SET, + GEN_ABS_NRPN +}; + + /** Reset an array of generators to the SF2.01 default values */ +FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen); + + + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_GEN_H */ + diff --git a/headers/libs/fluidsynth/fluidsynth/log.h b/headers/libs/fluidsynth/fluidsynth/log.h new file mode 100644 index 0000000000..f731e48d3a --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/log.h @@ -0,0 +1,85 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_LOG_H +#define _FLUIDSYNTH_LOG_H + + +#ifdef __cplusplus +extern "C" { +#endif + + + /** + * + * Logging interface + * + * The default logging function of the fluidsynth prints its messages + * to the stderr. The synthesizer uses four level of messages: FLUID_PANIC, + * ERR, WARN, and FLUID_DBG. They are commented in the definition below. + * + * A client application can install a new log function to handle the + * messages differently. In the following example, the application + * sets a callback function to display "FLUID_PANIC" messages in a dialog, + * and ignores all other messages by setting the log function to + * NULL: + * + * ... + * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); + * fluid_set_log_function(ERR, NULL, NULL); + * fluid_set_log_function(WARN, NULL, NULL); + * fluid_set_log_function(FLUID_DBG, NULL, NULL); + * ... + * + */ + +enum fluid_log_level { + FLUID_PANIC, /* the synth can't function correctly any more */ + FLUID_ERR, /* the synth can function, but with serious limitation */ + FLUID_WARN, /* the synth might not function as expected */ + FLUID_INFO, /* verbose messages */ + FLUID_DBG, /* debugging messages */ + LAST_LOG_LEVEL +}; + +typedef void (*fluid_log_function_t)(int level, char* message, void* data); + + /** fluid_set_log_function installs a new log function for the + * specified level. It returns the previously installed function. + */ +FLUIDSYNTH_API +fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data); + + + /** fluid_default_log_function is the fluid's default log function. It + * prints to the stderr. */ +FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data); + + /** print a message to the log */ +FLUIDSYNTH_API int fluid_log(int level, char * fmt, ...); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_LOG_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/midi.h b/headers/libs/fluidsynth/fluidsynth/midi.h new file mode 100644 index 0000000000..f55bc39cdd --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/midi.h @@ -0,0 +1,134 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_MIDI_H +#define _FLUIDSYNTH_MIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + + + +FLUIDSYNTH_API fluid_midi_event_t* new_fluid_midi_event(void); +FLUIDSYNTH_API int delete_fluid_midi_event(fluid_midi_event_t* event); + +FLUIDSYNTH_API int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type); +FLUIDSYNTH_API int fluid_midi_event_get_type(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan); +FLUIDSYNTH_API int fluid_midi_event_get_channel(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_get_key(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_key(fluid_midi_event_t* evt, int key); +FLUIDSYNTH_API int fluid_midi_event_get_velocity(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int vel); +FLUIDSYNTH_API int fluid_midi_event_get_control(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_control(fluid_midi_event_t* evt, int ctrl); +FLUIDSYNTH_API int fluid_midi_event_get_value(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_value(fluid_midi_event_t* evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_program(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_pitch(fluid_midi_event_t* evt); +FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val); + + + /* Generic callback function for MIDI events. + * Will be used between + * - MIDI driver and MIDI router + * - MIDI router and synth + * to communicate events. + * In the not-so-far future... + */ +typedef int (*handle_midi_event_func_t)(void* data, fluid_midi_event_t* event); + + /* + * + * MIDI router + * + * The MIDI handler forwards incoming MIDI events to the synthesizer + * + */ + + /** Create a new midi router. A midi handler connects to a midi input + * device and forwards incoming midi events to the synthesizer. + */ +FLUIDSYNTH_API fluid_midi_router_t* new_fluid_midi_router(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); + + /** Delete the midi router. + * + * \param handler a pointer to the midi handler + * \return 0 if no error occured, -1 otherwise + */ +FLUIDSYNTH_API int delete_fluid_midi_router(fluid_midi_router_t* handler); + + /** The standard handler function. Every MIDI event goes through + this. */ +FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event); + + /** An optional link in the MIDI chain to dump MIDI data between MIDI + driver and router */ +FLUIDSYNTH_API int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event); + + /** An optional link in the MIDI chain to dump MIDI data between MIDI + router and the synthesizer */ +FLUIDSYNTH_API int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event); + +/* + * + * MIDI driver + * + * The MIDI handler forwards incoming MIDI events to the synthesizer + * + */ + + +FLUIDSYNTH_API +fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); + +FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver); + + + +/* + * + * MIDI file player + * + * The MIDI player allows you to play MIDI files with the FLUID Synth + * + */ + +FLUIDSYNTH_API fluid_player_t* new_fluid_player(fluid_synth_t* synth); +FLUIDSYNTH_API int delete_fluid_player(fluid_player_t* player); +FLUIDSYNTH_API int fluid_player_add(fluid_player_t* player, char* midifile); +FLUIDSYNTH_API int fluid_player_play(fluid_player_t* player); +FLUIDSYNTH_API int fluid_player_stop(fluid_player_t* player); +FLUIDSYNTH_API int fluid_player_join(fluid_player_t* player); +FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t* player, int loop); +FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo); +FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t* player, int bpm); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MIDI_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/misc.h b/headers/libs/fluidsynth/fluidsynth/misc.h new file mode 100644 index 0000000000..b7d4fdf7ad --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/misc.h @@ -0,0 +1,65 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_MISC_H +#define _FLUIDSYNTH_MISC_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * + * Utility functions + */ + +/** + * fluid_is_soundfont returns 1 if the specified filename is a + * soundfont. It retuns 0 otherwise. The current implementation only + * checks for the "RIFF" header in the file. It is useful only to + * distinguish between SoundFonts and MIDI files. + */ +FLUIDSYNTH_API int fluid_is_soundfont(char* filename); + +/** + * fluid_is_midifile returns 1 if the specified filename is a MIDI + * file. It retuns 0 otherwise. The current implementation only checks + * for the "MThd" header in the file. + */ +FLUIDSYNTH_API int fluid_is_midifile(char* filename); + + + + +#ifdef WIN32 +/** Set the handle to the instance of the application on the Windows + platform. The handle is needed to open DirectSound. */ +FLUIDSYNTH_API void* fluid_get_hinstance(void); +FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MISC_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/mod.h b/headers/libs/fluidsynth/fluidsynth/mod.h new file mode 100644 index 0000000000..5fe86cda65 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/mod.h @@ -0,0 +1,112 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_MOD_H +#define _FLUIDSYNTH_MOD_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* Modulator-related definitions */ + + /* Maximum number of modulators in a voice */ +#define FLUID_NUM_MOD 64 + + /* + * fluid_mod_t + */ +struct _fluid_mod_t +{ + unsigned char dest; + unsigned char src1; + unsigned char flags1; + unsigned char src2; + unsigned char flags2; + double amount; + /* The 'next' field allows to link modulators into a list. It is + * not used in fluid_voice.c, there each voice allocates memory for a + * fixed number of modulators. Since there may be a huge number of + * different zones, this is more efficient. + */ + fluid_mod_t * next; +}; + +/* Flags telling the polarity of a modulator. Compare with SF2.01 + section 8.2. Note: The numbers of the bits are different! (for + example: in the flags of a SF modulator, the polarity bit is bit + nr. 9) */ +enum fluid_mod_flags +{ + FLUID_MOD_POSITIVE = 0, + FLUID_MOD_NEGATIVE = 1, + FLUID_MOD_UNIPOLAR = 0, + FLUID_MOD_BIPOLAR = 2, + FLUID_MOD_LINEAR = 0, + FLUID_MOD_CONCAVE = 4, + FLUID_MOD_CONVEX = 8, + FLUID_MOD_SWITCH = 12, + FLUID_MOD_GC = 0, + FLUID_MOD_CC = 16 +}; + +/* Flags telling the source of a modulator. This corresponds to + * SF2.01 section 8.2.1 */ +enum fluid_mod_src +{ + FLUID_MOD_NONE = 0, + FLUID_MOD_VELOCITY = 2, + FLUID_MOD_KEY = 3, + FLUID_MOD_KEYPRESSURE = 10, + FLUID_MOD_CHANNELPRESSURE = 13, + FLUID_MOD_PITCHWHEEL = 14, + FLUID_MOD_PITCHWHEELSENS = 16 +}; + +/* Allocates memory for a new modulator */ +FLUIDSYNTH_API fluid_mod_t * fluid_mod_new(void); + +/* Frees the modulator */ +FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod); + + +FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst); +FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount); + +FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod); +FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod); +FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod); +FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod); +FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod); +FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod); + + +/* Determines, if two modulators are 'identical' (all parameters + except the amount match) */ +FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2); + + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_MOD_H */ + diff --git a/headers/libs/fluidsynth/fluidsynth/ramsfont.h b/headers/libs/fluidsynth/fluidsynth/ramsfont.h new file mode 100644 index 0000000000..75a8d6761a --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/ramsfont.h @@ -0,0 +1,113 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_RAMSFONT_H +#define _FLUIDSYNTH_RAMSFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************************************************************************/ +/********************************************************************************/ +/* ram soundfonts: + October 2002 - Antoine Schmitt + + ram soundfonts live in ram. The samples are loaded from files + or from RAM. A minimal API manages a soundFont structure, + with presets, each preset having only one preset-zone, which + instrument has potentially many instrument-zones. No global + zones, and nor generator nor modulator other than the default + ones are permitted. This may be extensible in the future. +*/ +/********************************************************************************/ +/********************************************************************************/ + +/* + We are not using the sfloader protocol, as we need more arguments + than what it provides. +*/ + +/** Creates a fluid_sfont_t wrapping an fluid_ramsfont_t */ +FLUIDSYNTH_API fluid_sfont_t* fluid_ramsfont_create_sfont(void); + +/*********************** + * ramsfont specific API + ***********************/ +FLUIDSYNTH_API int fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name); + +/* Creates one instrument zone for the sample inside the preset defined + * by bank/num + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int lokey, int hikey); + +/* Removes the instrument zone corresponding to bank/num and to the sample + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample); + +/* Sets a generator on an instrument zone + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int gen_type, float value); + +/* Utility : sets the loop start/end values + * \on = 0 or 1; if 0, loopstart and loopend are not used + * \loopstart and loopend are floats, in frames + * \loopstart is counted from frame 0 + * \loopend is counted from the last frame, thus is < 0 + * \returns 0 if success + */ +FLUIDSYNTH_API +int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int on, float loopstart, float loopend); + +/*************************************** + * sample_t specific API for ramsfont + ***************************************/ +FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void); +FLUIDSYNTH_API int delete_fluid_ramsample(fluid_sample_t* sample); +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, char * name); + +/* Sets the sound data of the sample + * Warning : if copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end. + */ +FLUIDSYNTH_API +int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, + unsigned int nbframes, short copy_data, int rootkey); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_RAMSFONT_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/seq.h b/headers/libs/fluidsynth/fluidsynth/seq.h new file mode 100644 index 0000000000..25ab0598b6 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/seq.h @@ -0,0 +1,109 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SEQ_H +#define _FLUIDSYNTH_SEQ_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t* event, + fluid_sequencer_t* seq, void* data); + + +/** Allocate a new sequencer structure */ +FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer(void); + +/** Free the sequencer structure */ +FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t* seq); + +/** clients can be sources or destinations of events. These functions ensure a unique ID for any +source or dest, for filtering purposes. +sources only dont need to register a callback. +*/ + +/** Register a client. The registration returns a unique client ID (-1 if error) */ +FLUIDSYNTH_API +short fluid_sequencer_register_client(fluid_sequencer_t* seq, char* name, + fluid_event_callback_t callback, void* data); + +/** Unregister a previously registered client. */ +FLUIDSYNTH_API void fluid_sequencer_unregister_client(fluid_sequencer_t* seq, short id); + +/** Returns the number of register clients. */ +FLUIDSYNTH_API int fluid_sequencer_count_clients(fluid_sequencer_t* seq); + +/** Returns the id of a registered client (-1 if non existing) */ +FLUIDSYNTH_API short fluid_sequencer_get_client_id(fluid_sequencer_t* seq, int index); + +/** Returns the name of a registered client, given its id. */ +FLUIDSYNTH_API char* fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int id); + +/** Returns 1 if client is a destination (has a callback) */ +FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id); + + + +/** Sending an event immediately. */ +FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt); + + +/** Schedule an event for later sending. If absolute is 0, the time of + the event will be offset with the current tick of the + sequencer. If absolute is different from 0, the time will assumed + to be absolute (starting from the creation of the sequencer). + MAKES A COPY */ +FLUIDSYNTH_API +int fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, + unsigned int time, int absolute); + +/** Remove events from the event queue. The events can be filtered on + the source, the destination, and the type of the event. To avoid + filtering, set either source, dest, or type to -1. */ +FLUIDSYNTH_API +void fluid_sequencer_remove_events(fluid_sequencer_t* seq, short source, short dest, int type); + + +/** Get the current tick */ +FLUIDSYNTH_API unsigned int fluid_sequencer_get_tick(fluid_sequencer_t* seq); + +/** Set the conversion from tick to absolute time. scale should be + expressed as ticks per second. */ +FLUIDSYNTH_API void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale); + +/** Set the conversion from tick to absolute time (ticks per + second). */ +FLUIDSYNTH_API double fluid_sequencer_get_time_scale(fluid_sequencer_t* seq); + +// compile in internal traceing functions +#define FLUID_SEQ_WITH_TRACE 0 + +#if FLUID_SEQ_WITH_TRACE +FLUIDSYNTH_API char * fluid_seq_gettrace(fluid_sequencer_t* seq); +FLUIDSYNTH_API void fluid_seq_cleartrace(fluid_sequencer_t* seq); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SEQ_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/seqbind.h b/headers/libs/fluidsynth/fluidsynth/seqbind.h new file mode 100644 index 0000000000..09fb46ac55 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/seqbind.h @@ -0,0 +1,44 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SEQBIND_H +#define _FLUIDSYNTH_SEQBIND_H + +#include "fluidsynth/seq.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** registers fluidsynth as a client of the given sequencer. + The fluidsynth is registered with the name "fluidsynth". + + \returns the fluidsynth destID. + */ +FLUIDSYNTH_API +short fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth); + + + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_SEQBIND_H */ + diff --git a/headers/libs/fluidsynth/fluidsynth/settings.h b/headers/libs/fluidsynth/fluidsynth/settings.h new file mode 100644 index 0000000000..61f0431368 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/settings.h @@ -0,0 +1,223 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SETTINGS_H +#define _FLUIDSYNTH_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * + * Synthesizer settings + * + * + * The create a synthesizer object you will have to specify its + * settings. These settings are stored in the structure below. + + * void my_synthesizer() + * { + * fluid_settings_t* settings; + * fluid_synth_t* synth; + * fluid_audio_driver_t* adriver; + * + * + * settings = new_fluid_settings(); + * fluid_settings_setstr(settings, "audio.driver", "alsa"); + * // ... change settings ... + * synth = new_fluid_synth(settings); + * adriver = new_fluid_audio_driver(settings, synth); + * + * ... + * + * } + * + * + */ + + + + +/* Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field + of the FLUID_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) lower + bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + specified then the value of LowerBound should be multiplied by the + sample rate. */ +#define FLUID_HINT_BOUNDED_BELOW 0x1 + +/* Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the FLUID_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define FLUID_HINT_BOUNDED_ABOVE 0x2 + +/* Hint FLUID_HINT_TOGGLED indicates that the data item should be + considered a Boolean toggle. Data less than or equal to zero should + be considered `off' or `false,' and data above zero should be + considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in + conjunction with any other hint except FLUID_HINT_DEFAULT_0 or + FLUID_HINT_DEFAULT_1. */ +#define FLUID_HINT_TOGGLED 0x4 + +/* Hint FLUID_HINT_SAMPLE_RATE indicates that any bounds specified + should be interpreted as multiples of the sample rate. For + instance, a frequency range from 0Hz to the Nyquist frequency (half + the sample rate) could be requested by this hint in conjunction + with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds + at all must support this hint to retain meaning. */ +#define FLUID_HINT_SAMPLE_RATE 0x8 + +/* Hint FLUID_HINT_LOGARITHMIC indicates that it is likely that the + user will find it more intuitive to view values using a logarithmic + scale. This is particularly useful for frequencies and gains. */ +#define FLUID_HINT_LOGARITHMIC 0x10 + +/* Hint FLUID_HINT_INTEGER indicates that a user interface would + probably wish to provide a stepped control taking only integer + values. Any bounds set should be slightly wider than the actual + integer range required to avoid floating point rounding errors. For + instance, the integer set {0,1,2,3} might be described as [-0.1, + 3.1]. */ +#define FLUID_HINT_INTEGER 0x20 + + +#define FLUID_HINT_FILENAME 0x01 +#define FLUID_HINT_OPTIONLIST 0x02 + + + +enum fluid_types_enum { + FLUID_NO_TYPE = -1, + FLUID_NUM_TYPE, + FLUID_INT_TYPE, + FLUID_STR_TYPE, + FLUID_SET_TYPE +}; + + +FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void); +FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings); + + + +FLUIDSYNTH_API +int fluid_settings_get_type(fluid_settings_t* settings, char* name); + +FLUIDSYNTH_API +int fluid_settings_get_hints(fluid_settings_t* settings, char* name); + +/** Returns whether the setting is changeable in real-time. */ +FLUIDSYNTH_API int fluid_settings_is_realtime(fluid_settings_t* settings, char* name); + + +/** returns 1 if the value has been set, 0 otherwise */ +FLUIDSYNTH_API +int fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str); + +/** + Get the value of a string setting. If the value does not exists, + 'str' is set to NULL. Otherwise, 'str' will point to the + value. The application does not own the returned value. Instead, + the application should make a copy of the value if it needs it + later. + + \returns 1 if the value exists, 0 otherwise +*/ +FLUIDSYNTH_API +int fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str); + +/** Get the default value of a string setting. */ +FLUIDSYNTH_API +char* fluid_settings_getstr_default(fluid_settings_t* settings, char* name); + +/** Get the value of a numeric setting. + + \returns 1 if the value exists and is equal to 'value', 0 + otherwise +*/ +FLUIDSYNTH_API +int fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* value); + + +/** returns 1 if the value has been set, 0 otherwise */ +FLUIDSYNTH_API +int fluid_settings_setnum(fluid_settings_t* settings, char* name, double val); + +/** returns 1 if the value exists, 0 otherwise */ +FLUIDSYNTH_API +int fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val); + +/** Get the default value of a string setting. */ +FLUIDSYNTH_API +double fluid_settings_getnum_default(fluid_settings_t* settings, char* name); + +/** Get the range of values of a numeric settings. */ +FLUIDSYNTH_API +void fluid_settings_getnum_range(fluid_settings_t* settings, char* name, + double* min, double* max); + + +/** returns 1 if the value has been set, 0 otherwise */ +FLUIDSYNTH_API +int fluid_settings_setint(fluid_settings_t* settings, char* name, int val); + +/** returns 1 if the value exists, 0 otherwise */ +FLUIDSYNTH_API +int fluid_settings_getint(fluid_settings_t* settings, char* name, int* val); + +/** Get the default value of a string setting. */ +FLUIDSYNTH_API +int fluid_settings_getint_default(fluid_settings_t* settings, char* name); + +/** Get the range of values of a numeric settings. */ +FLUIDSYNTH_API +void fluid_settings_getint_range(fluid_settings_t* settings, char* name, + int* min, int* max); + + + +typedef void (*fluid_settings_foreach_option_t)(void* data, char* name, char* option); + + + +FLUIDSYNTH_API +void fluid_settings_foreach_option(fluid_settings_t* settings, + char* name, void* data, + fluid_settings_foreach_option_t func); + + +typedef void (*fluid_settings_foreach_t)(void* data, char* s, int type); + +FLUIDSYNTH_API +void fluid_settings_foreach(fluid_settings_t* settings, void* data, + fluid_settings_foreach_t func); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SETTINGS_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/sfont.h b/headers/libs/fluidsynth/fluidsynth/sfont.h new file mode 100644 index 0000000000..edf0671936 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/sfont.h @@ -0,0 +1,193 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SFONT_H +#define _FLUIDSYNTH_SFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + /** + * + * SoundFont plugins + * + * It is possible to add new SoundFont loaders to the + * synthesizer. The API uses a couple of "interfaces" (structures + * with callback functions): fluid_sfloader_t, fluid_sfont_t, and + * fluid_preset_t. + * + * To add a new SoundFont loader to the synthesizer, call + * fluid_synth_add_sfloader() and pass a pointer to an + * fluid_sfloader_t structure. The important callback function in + * this structure is "load", which should try to load a file and + * returns a fluid_sfont_t structure, or NULL if it fails. + * + * The fluid_sfont_t structure contains a callback to obtain the + * name of the soundfont. It contains two functions to iterate + * though the contained presets, and one function to obtain a + * preset corresponding to a bank and preset number. This + * function should return an fluid_preset_t structure. + * + * The fluid_preset_t structure contains some functions to obtain + * information from the preset (name, bank, number). The most + * important callback is the noteon function. The noteon function + * should call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * fluid_sample_t structure and returns a pointer to the opaque + * fluid_voice_t structure. To set or increments the values of a + * generator, use fluid_voice_gen_{set,incr}. When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. + * */ + + enum { + FLUID_PRESET_SELECTED, + FLUID_PRESET_UNSELECTED, + FLUID_SAMPLE_DONE + }; + + +/* + * fluid_sfloader_t + */ + +struct _fluid_sfloader_t { + /** Private data */ + void* data; + + /** The free must free the memory allocated for the loader in + * addition to any private data. It should return 0 if no error + * occured, non-zero otherwise.*/ + int (*free)(fluid_sfloader_t* loader); + + /** Load a file. Returns NULL if an error occured. */ + fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename); +}; + + +/* + * fluid_sfont_t + */ + +struct _fluid_sfont_t { + void* data; + unsigned int id; + + /** The 'free' callback function should return 0 when it was able to + free all resources. It should return a non-zero value if some of + the samples could not be freed because they are still in use. */ + int (*free)(fluid_sfont_t* sfont); + + /** Return the name of the sfont */ + char* (*get_name)(fluid_sfont_t* sfont); + + /** Return the preset with the specified bank and preset number. All + * the fields, including the 'sfont' field, should * be filled + * in. If the preset cannot be found, the function returns NULL. */ + fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); + + void (*iteration_start)(fluid_sfont_t* sfont); + + /* return 0 when no more presets are available, 1 otherwise */ + int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset); +}; + +#define fluid_sfont_get_id(_sf) ((_sf)->id) + + +/* + * fluid_preset_t + */ + +struct _fluid_preset_t { + void* data; + fluid_sfont_t* sfont; + int (*free)(fluid_preset_t* preset); + char* (*get_name)(fluid_preset_t* preset); + int (*get_banknum)(fluid_preset_t* preset); + int (*get_num)(fluid_preset_t* preset); + + /** handle a noteon event. Returns 0 if no error occured. */ + int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + /** Implement this function if the preset needs to be notified about + preset select and unselect events. */ + int (*notify)(fluid_preset_t* preset, int reason, int chan); +}; + + +/* + * fluid_sample_t + */ + +struct _fluid_sample_t +{ + char name[21]; + unsigned int start; + unsigned int end; + unsigned int loopstart; + unsigned int loopend; + unsigned int samplerate; + int origpitch; + int pitchadj; + int sampletype; + int valid; + short* data; + + /** The amplitude, that will lower the level of the sample's loop to + the noise floor. Needed for note turnoff optimization, will be + filled out automatically */ + /* Set this to zero, when submitting a new sample. */ + int amplitude_that_reaches_noise_floor_is_valid; + double amplitude_that_reaches_noise_floor; + + /** Count the number of playing voices that use this sample. */ + unsigned int refcount; + + /** Implement this function if the sample or SoundFont needs to be + notified when the sample is no longer used. */ + int (*notify)(fluid_sample_t* sample, int reason); + + /** Pointer to SoundFont specific data */ + void* userdata; +}; + + +#define fluid_sample_refcount(_sample) ((_sample)->refcount) + + +/** Sample types */ + +#define FLUID_SAMPLETYPE_MONO 1 +#define FLUID_SAMPLETYPE_RIGHT 2 +#define FLUID_SAMPLETYPE_LEFT 4 +#define FLUID_SAMPLETYPE_LINKED 8 +#define FLUID_SAMPLETYPE_ROM 0x8000 + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SFONT_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/shell.h b/headers/libs/fluidsynth/fluidsynth/shell.h new file mode 100644 index 0000000000..58151a849d --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/shell.h @@ -0,0 +1,137 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SHELL_H +#define _FLUIDSYNTH_SHELL_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * + * Shell interface + * + * The shell interface allows you to send simple textual commands to + * the synthesizer, to parse a command file, or to read commands + * from the stdin or other input streams. + * + * To find the list of currently supported commands, please check the + * fluid_cmd.c file. + * + */ + +FLUIDSYNTH_API fluid_istream_t fluid_get_stdin(void); +FLUIDSYNTH_API fluid_ostream_t fluid_get_stdout(void); + +FLUIDSYNTH_API char* fluid_get_userconf(char* buf, int len); +FLUIDSYNTH_API char* fluid_get_sysconf(char* buf, int len); + + +/** The command structure */ + +typedef int (*fluid_cmd_func_t)(void* data, int ac, char** av, fluid_ostream_t out); + +typedef struct { + char* name; /** The name of the command, as typed in in the shell */ + char* topic; /** The help topic group of this command */ + fluid_cmd_func_t handler; /** Pointer to the handler for this command */ + void* data; /** Pointer to the user data */ + char* help; /** A help string */ +} fluid_cmd_t; + + +/** The command handler */ + +/** + Create a new command handler. If the synth object passed as + argument is not NULL, the handler will add all the default + synthesizer commands to the command list. + + \param synth The synthesizer object + \returns A new command handler +*/ +FLUIDSYNTH_API +fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth); + +FLUIDSYNTH_API +void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler); + +FLUIDSYNTH_API +void fluid_cmd_handler_set_synth(fluid_cmd_handler_t* handler, fluid_synth_t* synth); + +/** + Register a new command to the handler. The handler makes a private + copy of the 'cmd' structure passed as argument. + + \param handler A pointer to the command handler + \param cmd A pointer to the command structure + \returns 0 if the command was inserted, non-zero if error +*/ +FLUIDSYNTH_API +int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd); + +FLUIDSYNTH_API +int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, char* cmd); + + +/** Command function */ + +FLUIDSYNTH_API +int fluid_command(fluid_cmd_handler_t* handler, char* cmd, fluid_ostream_t out); + +FLUIDSYNTH_API +int fluid_source(fluid_cmd_handler_t* handler, char* filename); + +FLUIDSYNTH_API +void fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler); + + +/** Shell */ + +FLUIDSYNTH_API +fluid_shell_t* new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* handler, + fluid_istream_t in, fluid_ostream_t out, int thread); + +FLUIDSYNTH_API void delete_fluid_shell(fluid_shell_t* shell); + + + +/** TCP/IP server */ + +typedef fluid_cmd_handler_t* (*fluid_server_newclient_func_t)(void* data, char* addr); + +FLUIDSYNTH_API +fluid_server_t* new_fluid_server(fluid_settings_t* settings, + fluid_server_newclient_func_t func, + void* data); + +FLUIDSYNTH_API void delete_fluid_server(fluid_server_t* server); + +FLUIDSYNTH_API int fluid_server_join(fluid_server_t* server); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SHELL_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/synth.h b/headers/libs/fluidsynth/fluidsynth/synth.h new file mode 100644 index 0000000000..912e4c5601 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/synth.h @@ -0,0 +1,700 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_SYNTH_H +#define _FLUIDSYNTH_SYNTH_H + + +#ifdef __cplusplus +extern "C" { +#endif + + + /** Embedded synthesizer + * + * You create a new synthesizer with new_fluid_synth() and you destroy + * if with delete_fluid_synth(). Use the settings structure to specify + * the synthesizer characteristics. + * + * You have to load a SoundFont in order to hear any sound. For that + * you use the fluid_synth_sfload() function. + * + * You can use the audio driver functions described below to open + * the audio device and create a background audio thread. + * + * The API for sending MIDI events is probably what you expect: + * fluid_synth_noteon(), fluid_synth_noteoff(), ... + * + */ + + + /** Creates a new synthesizer object. + * + * Creates a new synthesizer object. As soon as the synthesizer is + * created, it will start playing. + * + * \param settings a pointer to a settings structure + * \return a newly allocated synthesizer or NULL in case of error + */ +FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); + + + /** + * Deletes the synthesizer previously created with new_fluid_synth. + * + * \param synth the synthesizer object + * \return 0 if no error occured, -1 otherwise + */ +FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth); + + + /** Get a reference to the settings of the synthesizer. + * + * \param synth the synthesizer object + * \return pointer to the settings + */ +FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); + + + /* + * + * MIDI channel messages + * + */ + + /** Send a noteon message. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); + + /** Send a noteoff message. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); + + /** Send a control change message. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); + + /** Get a control value. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); + + /** Send a pitch bend message. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); + + /** Get the pitch bend value. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API +int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); + + /** Set the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); + + /** Get the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); + + /** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); + + /** Select a bank. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API +int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); + + /** Select a sfont. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API +int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); + + /** Select a preset for a channel. The preset is specified by the + SoundFont ID, the bank number, and the preset number. This + allows any preset to be selected and circumvents preset masking + due to previously loaded SoundFonts on the SoundFont stack. + + \param synth The synthesizer + \param chan The channel on which to set the preset + \param sfont_id The ID of the SoundFont + \param bank_num The bank number + \param preset_num The preset number + \return 0 if no errors occured, -1 otherwise + */ +FLUIDSYNTH_API +int fluid_synth_program_select(fluid_synth_t* synth, int chan, + unsigned int sfont_id, + unsigned int bank_num, + unsigned int preset_num); + + /** Returns the program, bank, and SoundFont number of the preset on + a given channel. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API +int fluid_synth_get_program(fluid_synth_t* synth, int chan, + unsigned int* sfont_id, + unsigned int* bank_num, + unsigned int* preset_num); + + /** Send a bank select and a program change to every channel to + * reinitialize the preset of the channel. This function is useful + * mainly after a SoundFont has been loaded, unloaded or + * reloaded. . Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth); + + /** Send a reset. A reset turns all the notes off and resets the + controller values. */ +FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth); + + + /* + * + * Low level access + * + */ + + /** Create and start voices using a preset. The id passed as + * argument will be used as the voice group id. */ +FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id, + fluid_preset_t* preset, int audio_chan, + int midi_chan, int key, int vel); + + /** Stop the voices in the voice group defined by id. */ +FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id); + + /** Change the value of a generator of the voices in the voice group + * defined by id. */ +/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */ +/* int gen, float value, */ +/* int absolute, int normalized); */ + + + /* + * + * SoundFont management + * + */ + + /** Loads a SoundFont file and creates a new SoundFont. The newly + loaded SoundFont will be put on top of the SoundFont + stack. Presets are searched starting from the SoundFont on the + top of the stack, working the way down the stack until a preset + is found. + + \param synth The synthesizer object + \param filename The file name + \param reset_presets If non-zero, the presets on the channels will be reset + \returns The ID of the loaded SoundFont, or -1 in case of error + */ +FLUIDSYNTH_API +int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets); + + /** Reload a SoundFont. The reloaded SoundFont retains its ID and + index on the stack. + + \param synth The synthesizer object + \param id The id of the SoundFont + \returns The ID of the loaded SoundFont, or -1 in case of error + */ +FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id); + + /** Removes a SoundFont from the stack and deallocates it. + + \param synth The synthesizer object + \param id The id of the SoundFont + \param reset_presets If TRUE then presets will be reset for all channels + \returns 0 if no error, -1 otherwise + */ +FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets); + + /** Add a SoundFont. The SoundFont will be put on top of + the SoundFont stack. + + \param synth The synthesizer object + \param sfont The SoundFont + \returns The ID of the loaded SoundFont, or -1 in case of error + */ +FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); + + /** Remove a SoundFont that was previously added using + * fluid_synth_add_sfont(). The synthesizer does not delete the + * SoundFont; this is responsability of the caller. + + \param synth The synthesizer object + \param sfont The SoundFont + */ +FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); + + /** Count the number of loaded SoundFonts. + + \param synth The synthesizer object + \returns The number of loaded SoundFonts + */ +FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); + + /** Get a SoundFont. The SoundFont is specified by its index on the + stack. The top of the stack has index zero. + + \param synth The synthesizer object + \param num The number of the SoundFont (0 <= num < sfcount) + \returns A pointer to the SoundFont + */ +FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); + + /** Get a SoundFont. The SoundFont is specified by its ID. + + \param synth The synthesizer object + \param id The id of the sfont + \returns A pointer to the SoundFont + */ +FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); + + + /** Get the preset of a channel */ +FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); + + /** Offset the bank numbers in a SoundFont. Returns -1 if an error + * occured (out of memory or negative offset) */ +FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); + + /** Get the offset of the bank numbers in a SoundFont. */ +FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); + + + + /* + * + * Reverb + * + */ + + /** Set the parameters for the built-in reverb unit */ +FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, + double damping, double width, double level); + + /** Turn on (1) / off (0) the built-in reverb unit */ +FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on); + + + /** Query the current state of the reverb. */ +FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth); + + /* Those are the default settings for the reverb */ +#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f +#define FLUID_REVERB_DEFAULT_DAMP 0.0f +#define FLUID_REVERB_DEFAULT_WIDTH 0.5f +#define FLUID_REVERB_DEFAULT_LEVEL 0.9f + + + + /* + * + * Chorus + * + */ + +enum fluid_chorus_mod { + FLUID_CHORUS_MOD_SINE = 0, + FLUID_CHORUS_MOD_TRIANGLE = 1 +}; + + /** Set up the chorus. It should be turned on with fluid_synth_set_chorus_on. + * If faulty parameters are given, all new settings are discarded. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + */ +FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, + double speed, double depth_ms, int type); + + /** Turn on (1) / off (0) the built-in chorus unit */ +FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on); + + /** Query the current state of the chorus. */ +FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth); +FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */ + + /* Those are the default settings for the chorus. */ +#define FLUID_CHORUS_DEFAULT_N 3 +#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f +#define FLUID_CHORUS_DEFAULT_SPEED 0.3f +#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f +#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE + + + + /* + * + * Audio and MIDI channels + * + */ + + /** Returns the number of MIDI channels that the synthesizer uses + internally */ +FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth); + + /** Returns the number of audio channels that the synthesizer uses + internally */ +FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth); + + /** Returns the number of audio groups that the synthesizer uses + internally. This is usually identical to audio_channels. */ +FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth); + + /** Returns the number of effects channels that the synthesizer uses + internally */ +FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth); + + + + /* + * + * Synthesis parameters + * + */ + + /** Set the master gain */ +FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain); + + /** Get the master gain */ +FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth); + + /** Set the polyphony limit (FluidSynth >= 1.0.6) */ +FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony); + + /** Get the polyphony limit (FluidSynth >= 1.0.6) */ +FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth); + + /** Get the internal buffer size. The internal buffer size if not the + same thing as the buffer size specified in the + settings. Internally, the synth *always* uses a specific buffer + size independent of the buffer size used by the audio driver. The + internal buffer size is normally 64 samples. The reason why it + uses an internal buffer size is to allow audio drivers to call the + synthesizer with a variable buffer length. The internal buffer + size is useful for client who want to optimize their buffer sizes. + */ +FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth); + + /** Set the interpolation method for one channel or all channels (chan = -1) */ +FLUIDSYNTH_API +int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method); + + /* Flags to choose the interpolation method */ +enum fluid_interp { + /* no interpolation: Fastest, but questionable audio quality */ + FLUID_INTERP_NONE = 0, + /* Straight-line interpolation: A bit slower, reasonable audio quality */ + FLUID_INTERP_LINEAR = 1, + /* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality + * Default. */ + FLUID_INTERP_DEFAULT = 4, + FLUID_INTERP_4THORDER = 4, + FLUID_INTERP_7THORDER = 7, + FLUID_INTERP_HIGHEST=7 +}; + + + + + /* + * + * Generator interface + * + */ + + /** Change the value of a generator. This function allows to control + all synthesis parameters in real-time. The changes are additive, + i.e. they add up to the existing parameter value. This function is + similar to sending an NRPN message to the synthesizer. The + function accepts a float as the value of the parameter. The + parameter numbers and ranges are described in the SoundFont 2.01 + specification, paragraph 8.1.3, page 48. See also 'fluid_gen_type'. + + \param synth The synthesizer object. + \param chan The MIDI channel number. + \param param The parameter number. + \param value The parameter value. + \returns Your favorite dish. + */ +FLUIDSYNTH_API +int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); + + + /** Retreive the value of a generator. This function returns the value + set by a previous call 'fluid_synth_set_gen' or by an NRPN message. + + \param synth The synthesizer object. + \param chan The MIDI channel number. + \param param The generator number. + \returns The value of the generator. + */ +FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); + + + + + /* + * + * Tuning + * + */ + + /** Create a new key-based tuning with given name, number, and + pitches. The array 'pitches' should have length 128 and contains + the pitch in cents of every key in cents. However, if 'pitches' is + NULL, a new tuning is created with the well-tempered scale. + + \param synth The synthesizer object + \param tuning_bank The tuning bank number [0-127] + \param tuning_prog The tuning program number [0-127] + \param name The name of the tuning + \param pitch The array of pitch values. The array length has to be 128. + */ +FLUIDSYNTH_API +int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, + char* name, double* pitch); + + /** Create a new octave-based tuning with given name, number, and + pitches. The array 'pitches' should have length 12 and contains + derivation in cents from the well-tempered scale. For example, if + pitches[0] equals -33, then the C-keys will be tuned 33 cents + below the well-tempered C. + + \param synth The synthesizer object + \param tuning_bank The tuning bank number [0-127] + \param tuning_prog The tuning program number [0-127] + \param name The name of the tuning + \param pitch The array of pitch derivations. The array length has to be 12. + */ +FLUIDSYNTH_API +int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, + char* name, double* pitch); + + /** Request a note tuning changes. Both they 'keys' and 'pitches' + arrays should be of length 'num_pitches'. If 'apply' is non-zero, + the changes should be applied in real-time, i.e. sounding notes + will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The + changes will be available for newly triggered notes only. + + \param synth The synthesizer object + \param tuning_bank The tuning bank number [0-127] + \param tuning_prog The tuning program number [0-127] + \param len The length of the keys and pitch arrays + \param keys The array of keys values. + \param pitch The array of pitch values. + \param apply Flag to indicate whether to changes should be applied in real-time. + */ +FLUIDSYNTH_API +int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog, + int len, int *keys, double* pitch, int apply); + + /** Select a tuning for a channel. + + \param synth The synthesizer object + \param chan The channel number [0-max channels] + \param tuning_bank The tuning bank number [0-127] + \param tuning_prog The tuning program number [0-127] + */ +FLUIDSYNTH_API +int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog); + + /** Set the tuning to the default well-tempered tuning on a channel. + + \param synth The synthesizer object + \param chan The channel number [0-max channels] + */ +FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); + + /** Start the iteration throught the list of available tunings. + + \param synth The synthesizer object + */ +FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); + + + /** Get the next tuning in the iteration. This functions stores the + bank and program number of the next tuning in the pointers given as + arguments. + + \param synth The synthesizer object + \param bank Pointer to an int to store the bank number + \param prog Pointer to an int to store the program number + \returns 1 if there is a next tuning, 0 otherwise + */ +FLUIDSYNTH_API +int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); + + + /** Dump the data of a tuning. This functions stores the name and + pitch values of a tuning in the pointers given as arguments. Both + name and pitch can be NULL is the data is not needed. + + \param synth The synthesizer object + \param bank The tuning bank number [0-127] + \param prog The tuning program number [0-127] + \param name Pointer to a buffer to store the name + \param len The length of the name buffer + \param pitch Pointer to buffer to store the pitch values + */ +FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, + char* name, int len, double* pitch); + + + + + /* + * + * Misc + * + */ + + /** Get an estimation of the CPU load due to the audio synthesis. + Returns a percentage (0-100). + + \param synth The synthesizer object + */ +FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t* synth); + + /** Get a textual representation of the last error */ +FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); + + + /* + * + * Synthesizer plugin + * + * + * To create a synthesizer plugin, create the synthesizer as + * explained above. Once the synthesizer is created you can call + * any of the functions below to get the audio. + * + */ + + /** Generate a number of samples. This function expects two signed + * 16bits buffers (left and right channel) that will be filled with + * samples. + * + * \param synth The synthesizer + * \param len The number of samples to generate + * \param lout The sample buffer for the left channel + * \param loff The offset, in samples, in the left buffer where the writing pointer starts + * \param lincr The increment, in samples, of the writing pointer in the left buffer + * \param rout The sample buffer for the right channel + * \param roff The offset, in samples, in the right buffer where the writing pointer starts + * \param rincr The increment, in samples, of the writing pointer in the right buffer + * \returns 0 if no error occured, non-zero otherwise + */ + +FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t* synth, int len, + void* lout, int loff, int lincr, + void* rout, int roff, int rincr); + + + /** Generate a number of samples. This function expects two floating + * point buffers (left and right channel) that will be filled with + * samples. + * + * \param synth The synthesizer + * \param len The number of samples to generate + * \param lout The sample buffer for the left channel + * \param loff The offset, in samples, in the left buffer where the writing pointer starts + * \param lincr The increment, in samples, of the writing pointer in the left buffer + * \param rout The sample buffer for the right channel + * \param roff The offset, in samples, in the right buffer where the writing pointer starts + * \param rincr The increment, in samples, of the writing pointer in the right buffer + * \returns 0 if no error occured, non-zero otherwise + */ + +FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t* synth, int len, + void* lout, int loff, int lincr, + void* rout, int roff, int rincr); + +FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, + float** left, float** right, + float** fx_left, float** fx_right); + + /** Generate a number of samples. This function implements the + * default interface defined in fluidsynth/audio.h. This function + * ignores the input buffers and expects at least two output + * buffer. + * + * \param synth The synthesizer + * \param len The number of samples to generate + * \param nin The number of input buffers + * \param in The array of input buffers + * \param nout The number of output buffers + * \param out The array of output buffers + * \returns 0 if no error occured, non-zero otherwise + */ + +FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len, + int nin, float** in, + int nout, float** out); + + + + /* Type definition of the synthesizer's audio callback function. */ +typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, + void* out1, int loff, int lincr, + void* out2, int roff, int rincr); + + + + + + /* + * Synthesizer's interface to handle SoundFont loaders + */ + + + /** Add a SoundFont loader to the synthesizer. Note that SoundFont + loader don't necessarily load SoundFonts. They can load any type + of wavetable data but export a SoundFont interface. */ +FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); + + /** Allocate a synthesis voice. This function is called by a + soundfont's preset in response to a noteon event. + The returned voice comes with default modulators installed (velocity-to-attenuation, + velocity to filter, ...) + Note: A single noteon event may create any number of voices, when the preset is layered. + Typically 1 (mono) or 2 (stereo).*/ +FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, + int channum, int key, int vel); + + /** Start a synthesis voice. This function is called by a + soundfont's preset in response to a noteon event after the voice + has been allocated with fluid_synth_alloc_voice() and + initialized. + Exclusive classes are processed here.*/ +FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); + + + /** Write a list of all voices matching ID into buf, but not more than bufsize voices. + * If ID <0, return all voices. */ +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, + fluid_voice_t* buf[], int bufsize, int ID); + + + /** Callback function for the MIDI router. Any event goes through this. */ +FLUIDSYNTH_API int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event); + + + /** This is a hack to get command handlers working */ +FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth, + fluid_midi_router_t* router); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SYNTH_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/types.h b/headers/libs/fluidsynth/fluidsynth/types.h new file mode 100644 index 0000000000..d36f2fb109 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/types.h @@ -0,0 +1,66 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_TYPES_H +#define _FLUIDSYNTH_TYPES_H + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + + Forward declarations + +*/ +typedef struct _fluid_hashtable_t fluid_settings_t; +typedef struct _fluid_synth_t fluid_synth_t; +typedef struct _fluid_voice_t fluid_voice_t; +typedef struct _fluid_sfloader_t fluid_sfloader_t; +typedef struct _fluid_sfont_t fluid_sfont_t; +typedef struct _fluid_preset_t fluid_preset_t; +typedef struct _fluid_sample_t fluid_sample_t; +typedef struct _fluid_mod_t fluid_mod_t; +typedef struct _fluid_audio_driver_t fluid_audio_driver_t; +typedef struct _fluid_player_t fluid_player_t; +typedef struct _fluid_midi_event_t fluid_midi_event_t; +typedef struct _fluid_midi_driver_t fluid_midi_driver_t; +typedef struct _fluid_midi_router_t fluid_midi_router_t; +typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; +typedef struct _fluid_hashtable_t fluid_cmd_handler_t; +typedef struct _fluid_shell_t fluid_shell_t; +typedef struct _fluid_server_t fluid_server_t; +typedef struct _fluid_event_t fluid_event_t; +typedef struct _fluid_sequencer_t fluid_sequencer_t; +typedef struct _fluid_ramsfont_t fluid_ramsfont_t; +typedef struct _fluid_rampreset_t fluid_rampreset_t; + +typedef int fluid_istream_t; +typedef int fluid_ostream_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_TYPES_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/version.h b/headers/libs/fluidsynth/fluidsynth/version.h new file mode 100644 index 0000000000..e3a92ad492 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/version.h @@ -0,0 +1,44 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_VERSION_H +#define _FLUIDSYNTH_VERSION_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLUIDSYNTH_VERSION "1.0.7" +#define FLUIDSYNTH_VERSION_MAJOR 1 +#define FLUIDSYNTH_VERSION_MINOR 0 +#define FLUIDSYNTH_VERSION_MICRO 7 + + +FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); + +FLUIDSYNTH_API char* fluid_version_str(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_VERSION_H */ diff --git a/headers/libs/fluidsynth/fluidsynth/voice.h b/headers/libs/fluidsynth/fluidsynth/voice.h new file mode 100644 index 0000000000..13b6b804a4 --- /dev/null +++ b/headers/libs/fluidsynth/fluidsynth/voice.h @@ -0,0 +1,97 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUIDSYNTH_VOICE_H +#define _FLUIDSYNTH_VOICE_H + +#ifdef __cplusplus +extern "C" { +#endif + + + /* + * The interface to the synthesizer's voices + * Examples on using them can be found in fluid_defsfont.c + */ + + /** Update all the synthesis parameters, which depend on generator gen. + This is only necessary after changing a generator of an already operating voice. + Most applications will not need this function.*/ + +FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen); + + + /* for fluid_voice_add_mod */ +enum fluid_voice_add_mod{ + FLUID_VOICE_OVERWRITE, + FLUID_VOICE_ADD, + FLUID_VOICE_DEFAULT +}; + + /* Add a modulator to a voice (SF2.1 only). */ +FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode); + + /** Set the value of a generator */ +FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val); + + /** Get the value of a generator */ +FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen); + + /** Modify the value of a generator by val */ +FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); + + + /** Return the unique ID of the noteon-event. A sound font loader + * may store the voice processes it has created for * real-time + * control during the operation of a voice (for example: parameter + * changes in sound font editor). The synth uses a pool of + * voices, which are 'recycled' and never deallocated. + * + * Before modifying an existing voice, check + * - that its state is still 'playing' + * - that the ID is still the same + * Otherwise the voice has finished playing. + */ +FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice); + + +FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice); + + /** If the peak volume during the loop is known, then the voice can + * be released earlier during the release phase. Otherwise, the + * voice will operate (inaudibly), until the envelope is at the + * nominal turnoff point. In many cases the loop volume is many dB + * below the maximum volume. For example, the loop volume for a + * typical acoustic piano is 20 dB below max. Taking that into + * account in the turn-off algorithm we can save 20 dB / 100 dB => + * 1/5 of the total release time. + * So it's a good idea to call fluid_voice_optimize_sample + * on each sample once. + */ + +FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s); + + + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_VOICE_H */ + diff --git a/src/libs/Jamfile b/src/libs/Jamfile index 7492b02a49..ac37234f51 100644 --- a/src/libs/Jamfile +++ b/src/libs/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src libs ; SubInclude HAIKU_TOP src libs agg ; +SubInclude HAIKU_TOP src libs fluidsynth ; SubInclude HAIKU_TOP src libs freetype2 ; SubInclude HAIKU_TOP src libs zlib ; SubInclude HAIKU_TOP src libs png ; diff --git a/src/libs/fluidsynth/AUTHORS b/src/libs/fluidsynth/AUTHORS new file mode 100644 index 0000000000..96ddb9aad8 --- /dev/null +++ b/src/libs/fluidsynth/AUTHORS @@ -0,0 +1,117 @@ + +[:Idea:] + +* Samuel Bianchini, Peter Hanappe and Johnathan Lee + + +[:Development:] + +Many people contributed to FluidSynth, send suggestions or bug +fixes. The project was started by Peter Hanappe who is the main +author. Josh Green is the current maintainer. Below you'll find a +summery of contributions. + + +* Peter Hanappe. Initiated the project. files: sticked his nose in all + files. + +* Josh Green is the current maintainer and contributed a lot of code + directly or indirectly through the Swami and Smurf code base. + The SoundFont loader is completely based on his code. He also wrote + the alsa sequencer driver. He made many changes and bug fixes, + but above all, he's one of the driver forces behind the synthesizer. + He also created the current FluidSynth graphic logo with Blender + (the blue waves with FluidSynth letters partially submerged). + files: iiwu_defsfont.{c,h}, iiwu_alsa{c,h} and others. + +* Stephane Letz from Grame wrote most of the MidiShare driver, all of + the PortAudio driver, ported iiwusynth to MacOS X, and sent in many + fixes. files: iiwu_midishare.c, iiwu_portaudio.c + +* Markus Nentwig (re-)designed the resonant filter, the chorus, the + LADSPA subsystem, the MIDI router, optimized for SSE, made many + changes and bug fixes and got the synthesizer to actually work. Most + importantly, he used it on stage to make music. files: + iiwu_ladspa.{c,h}, iiwu_cmd.{c,h}, iiwu_voice.{c,h}, iiwu_synth.c, + iiwu_sse.h, iiwu_dsp_core.h, iiwu_rev.{c,h}, and basically all the + other files. + +* Antoine Schmitt added the sequencer support, support for sample + loading (RAM Sfont), developed the + MacroMedia Director Xtra, and send in many many bug reports. Thanks + to Antoine, the synthesizer finds its way to multi-media + developers. files: in bindings/director/ and iiwu_seq.{c,h}, + iiwu_event.{c,h}, iiwu_event_priv.h, iiwu_seqbind.{c,h}, + iiwu_ramsfont.{c,h} + +* Bob Ham added the code for "bank select" MIDI messages and send code + to define the synth's ALSA sequencer client name. files: + iiwu_midi.c, iiwu_alsa.c, iiwusynth.c, iiwusynth.h. + +* Tim Goetze sent many patches and implemented the all_notes_off. He + also sent his code for the new ALSA driver. files: iiwu_synth.c, + iiwu_chan.c, iiwu_voice.c, iiwu_alsa.c + +* Norbert Schnell from Ircam's jMax Team wrote most of the jMax/FTS + interface in a record time. He also pointed me to the technique of + using a lookup table for the interpolation coefficients. file: + iiwu_fts.c, iiwu_synth.c + +* The initial alsa driver was based on the jMax alsa driver by + Francois Dechelle and his Real-time Team at Ircam + (http://www.ircam.fr/jmax). The jMax code was based upon Ardour's + alsa_device.cc by Paul Barton-Davis. file: iiwu_alsa.c + +* Code was borrowed from the glib library to the smurf files. The goal was + to make iiwusynth independent from any library for maximum + portability. + +* The midi device uses code from jMax's alsarawmidi.c file and from + Smurf's midi_alsaraw.c by Josh Green. Josh also added support for + the alsa sequencer interface. file: iiwu_alsa.c + +* The reverb algorithm was written by Jezar + (http://www.dreampoint.co.uk). His code is public domain. The code + was translated to C by Peter Hanappe. file: iiwu_synth.c + +* The original code for the chorus effect was written by Juergen + Mueller and sundry contributors. + +* Bob Ham added LADCCA support. + +* Ebrahim Mayat made big efforts for compiling and running FluidSynth + on MacOS X. He also wrote the README-OSX file. + +* Martin Uddén's midi package was used. His files are integrated into + the iiwu_midi file. Martin Uddén file: + iiwu_midi.c + +* Ken Ellinwood send in a patch to add bank offsets to SoundFonts. An + adapted version was integrated in the source code. files: + fluid_cmd.c, fluidsynth/synth.h, fluid_synth.c. + +* Some interpolation algorihms were used that were found in + the music-dsp archives (http://www.smartelectronix.com/musicdsp). + They were written by Joshua Scholar and others. file: iiwu_synth.c + +* Macros to {increment,decrement} the 64-bit fixed point phase were + borrowed from Mozilla's macros to handle the Long-long type (64-bit + signed integer type). Mozilla NSPR library, www.mozilla.org. file: + iiwu_phase.h + +* Growing list of individuals who contributed bug fixes and corrections: +Werner Schweer +Dave Philips +Anthony Green +Jake Commander +Fernando Pablo Lopez-Lezcano +Raoul Bonisch +Sergey Pavlishin +Eric Van Buggenhaut +Ken Ellinwood +Takashi Iwai +Bob Ham +Gerald Pye +Rui Nuno Capela +Frieder Bürzele +Henri Manson diff --git a/src/libs/fluidsynth/COPYING b/src/libs/fluidsynth/COPYING new file mode 100644 index 0000000000..03a4f9819f --- /dev/null +++ b/src/libs/fluidsynth/COPYING @@ -0,0 +1,482 @@ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/src/libs/fluidsynth/Jamfile b/src/libs/fluidsynth/Jamfile new file mode 100644 index 0000000000..1537accf23 --- /dev/null +++ b/src/libs/fluidsynth/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src libs fluidsynth ; + +SubInclude HAIKU_TOP src libs fluidsynth src ; diff --git a/src/libs/fluidsynth/src/Jamfile b/src/libs/fluidsynth/src/Jamfile new file mode 100644 index 0000000000..58d725fb2a --- /dev/null +++ b/src/libs/fluidsynth/src/Jamfile @@ -0,0 +1,40 @@ +SubDir HAIKU_TOP src libs fluidsynth src ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UseLibraryHeaders fluidsynth ; + +SubDirCcFlags -DHAVE_CONFIG_H ; + +SharedLibrary libfluidsynth.so : + fluid_adriver.c + fluid_aufile.c + fluid_chan.c + fluid_chorus.c + fluid_cmd.c + fluid_conv.c + fluid_defsfont.c + fluid_event.c + fluid_gen.c + fluid_hash.c + fluid_io.c + fluid_list.c + fluid_mdriver.c + fluid_midi.c + fluid_midi_router.c + fluid_mod.c + fluid_ramsfont.c + fluid_rev.c + fluid_seq.c + fluid_seqbind.c + fluid_settings.c + fluid_strtok.c + fluid_synth.c + fluid_sys.c + fluid_tuning.c + fluid_voice.c +; + +# // for testing purpose +#StdBinCommands fluidsynth.c : libfluidsynth.so ; + diff --git a/src/libs/fluidsynth/src/config.h b/src/libs/fluidsynth/src/config.h new file mode 100644 index 0000000000..ff0e78178e --- /dev/null +++ b/src/libs/fluidsynth/src/config.h @@ -0,0 +1,191 @@ +/* src/config.h. Generated by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to enable ALSA driver */ +/* #undef ALSA_SUPPORT */ + +/* whether or not we are supporting CoreAudio */ +/* #undef COREAUDIO_SUPPORT */ + +/* Define if building for Mac OS X Darwin */ +/* #undef DARWIN */ + +/* Define to activate debugging message */ +#define DEBUG 0 + +/* Use the SSE instructions of Pentium3+ (not recommended) */ +/* #undef ENABLE_SSE */ + +/* Define to 1 if you have the header file. */ +#ifdef HAIKU_TARGET_PLATFORM_HAIKU +#define HAVE_ARPA_INET_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* whether or not we are supporting ladcca */ +/* #undef HAVE_LADCCA */ + +/* whether or not we are supporting lash */ +/* #undef HAVE_LASH */ + +/* Define to 1 if you have the `dl' library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define to 1 if you have the `MidiShare' library (-lMidiShare). */ +/* #undef HAVE_LIBMIDISHARE */ + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +/* #undef HAVE_LIBPTHREAD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACHINE_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MIDISHARE_H */ + +/* Define to 1 if you have the header file. */ +#ifdef HAIKU_TARGET_PLATFORM_HAIKU +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to enable JACK driver */ +/* #undef JACK_SUPPORT */ + +/* Include the LADSPA Fx unit */ +/* #undef LADSPA */ + +/* Define to enable MidiShare driver */ +/* #undef MIDISHARE_SUPPORT */ + +/* Define if using the MinGW32 environment */ +/* #undef MINGW32 */ + +/* Define to enable OSS driver */ +/* #undef OSS_SUPPORT */ + +/* Name of package */ +#define PACKAGE "fluidsynth" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to use long long type, where appropriate */ +/* #undef USE_LONGLONG */ + +/* Version number of package */ +#define VERSION "1.0.7" + +/* Define to do all DSP in single floating point precision */ +#define WITH_FLOAT 1 + +/* Define to profile the DSP code */ +/* #undef WITH_PROFILING */ + +/* Define to use the readline library for line editing */ +/* #undef WITH_READLINE */ + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Additional config for us */ +#ifdef __BEOS__ + +#define WITHOUT_SERVER 1 + +#endif diff --git a/src/libs/fluidsynth/src/fluid_adriver.c b/src/libs/fluidsynth/src/fluid_adriver.c new file mode 100644 index 0000000000..d5185a3db5 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_adriver.c @@ -0,0 +1,308 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_adriver.h" +#include "fluid_settings.h" + +/* + * fluid_adriver_definition_t + */ + +typedef struct _fluid_audriver_definition_t +{ + char* name; + fluid_audio_driver_t* (*new)(fluid_settings_t* settings, fluid_synth_t* synth); + fluid_audio_driver_t* (*new2)(fluid_settings_t* settings, + fluid_audio_func_t func, + void* data); + int (*free)(fluid_audio_driver_t* driver); + void (*settings)(fluid_settings_t* settings); +} fluid_audriver_definition_t; + + + +#if ALSA_SUPPORT +fluid_audio_driver_t* new_fluid_alsa_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +fluid_audio_driver_t* new_fluid_alsa_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, void* data); +int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p); +void fluid_alsa_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if OSS_SUPPORT +fluid_audio_driver_t* new_fluid_oss_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +fluid_audio_driver_t* new_fluid_oss_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, void* data); +int delete_fluid_oss_audio_driver(fluid_audio_driver_t* p); +void fluid_oss_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if COREAUDIO_SUPPORT +fluid_audio_driver_t* new_fluid_core_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +fluid_audio_driver_t* new_fluid_core_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, + void* data); +int delete_fluid_core_audio_driver(fluid_audio_driver_t* p); +void fluid_core_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if DSOUND_SUPPORT +fluid_audio_driver_t* new_fluid_dsound_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* p); +void fluid_dsound_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if PORTAUDIO_SUPPORT +fluid_audio_driver_t* new_fluid_portaudio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +int delete_fluid_portaudio_driver(fluid_audio_driver_t* p); +#endif + +#if JACK_SUPPORT +fluid_audio_driver_t* new_fluid_jack_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); +fluid_audio_driver_t* new_fluid_jack_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, void* data); +int delete_fluid_jack_audio_driver(fluid_audio_driver_t* p); +void fluid_jack_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if SNDMAN_SUPPORT +fluid_audio_driver_t* new_fluid_sndmgr_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +fluid_audio_driver_t* new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings, + fluid_audio_func_t func, + void* data); +int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p); +#endif + +#define AUFILE_SUPPORT 1 +#if AUFILE_SUPPORT +fluid_audio_driver_t* new_fluid_file_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +int delete_fluid_file_audio_driver(fluid_audio_driver_t* p); +void fluid_file_audio_driver_settings(fluid_settings_t* settings); +#endif + +fluid_audriver_definition_t fluid_audio_drivers[] = { +#if OSS_SUPPORT + { "oss", + new_fluid_oss_audio_driver, + new_fluid_oss_audio_driver2, + delete_fluid_oss_audio_driver, + fluid_oss_audio_driver_settings }, +#endif +#if ALSA_SUPPORT + { "alsa", + new_fluid_alsa_audio_driver, + new_fluid_alsa_audio_driver2, + delete_fluid_alsa_audio_driver, + fluid_alsa_audio_driver_settings }, +#endif +#if COREAUDIO_SUPPORT + { "coreaudio", + new_fluid_core_audio_driver, + new_fluid_core_audio_driver2, + delete_fluid_core_audio_driver, + fluid_core_audio_driver_settings }, +#endif +#if DSOUND_SUPPORT + { "dsound", + new_fluid_dsound_audio_driver, + NULL, + delete_fluid_dsound_audio_driver, + fluid_dsound_audio_driver_settings }, +#endif +#if PORTAUDIO_SUPPORT + { "portaudio", + new_fluid_portaudio_driver, + NULL, + delete_fluid_portaudio_driver, + NULL }, +#endif +#if SNDMAN_SUPPORT + { "sndman", + new_fluid_sndmgr_audio_driver, + new_fluid_sndmgr_audio_driver2, + delete_fluid_sndmgr_audio_driver, + NULL }, +#endif +#if JACK_SUPPORT + { "jack", + new_fluid_jack_audio_driver, + new_fluid_jack_audio_driver2, + delete_fluid_jack_audio_driver, + fluid_jack_audio_driver_settings }, +#endif +#if AUFILE_SUPPORT + { "file", + new_fluid_file_audio_driver, + NULL, + delete_fluid_file_audio_driver, + fluid_file_audio_driver_settings }, +#endif + { NULL, NULL, NULL, NULL, NULL } +}; + + + + +void fluid_audio_driver_settings(fluid_settings_t* settings) +{ + int i; + + fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0, NULL, NULL); + fluid_settings_add_option(settings, "audio.sample-format", "16bits"); + fluid_settings_add_option(settings, "audio.sample-format", "float"); + + fluid_settings_register_int(settings, "audio.output-channels", 2, 2, 32, 0, NULL, NULL); + fluid_settings_register_int(settings, "audio.input-channels", 0, 0, 2, 0, NULL, NULL); + + +#if defined(WIN32) + fluid_settings_register_int(settings, "audio.period-size", 512, 64, 8192, 0, NULL, NULL); + fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0, NULL, NULL); +#elif defined(MACOS9) + fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0, NULL, NULL); + fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0, NULL, NULL); +#else + fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0, NULL, NULL); + fluid_settings_register_int(settings, "audio.periods", 16, 2, 64, 0, NULL, NULL); +#endif + + /* Set the default driver */ +#if ALSA_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "alsa", 0, NULL, NULL); +#elif OSS_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "oss", 0, NULL, NULL); +#elif COREAUDIO_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "coreaudio", 0, NULL, NULL); +#elif DSOUND_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "dsound", 0, NULL, NULL); +#elif SNDMAN_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "sndman", 0, NULL, NULL); +#elif PORTAUDIO_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "portaudio", 0, NULL, NULL); +#elif JACK_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "jack", 0, NULL, NULL); +#elif AUFILE_SUPPORT + fluid_settings_register_str(settings, "audio.driver", "file", 0, NULL, NULL); +#else + fluid_settings_register_str(settings, "audio.driver", "", 0, NULL, NULL); +#endif + + /* Add all drivers to the list of options */ +#if ALSA_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "alsa"); +#endif +#if OSS_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "oss"); +#endif +#if COREAUDIO_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "coreaudio"); +#endif +#if DSOUND_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "dsound"); +#endif +#if SNDMAN_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "sndman"); +#endif +#if PORTAUDIO_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "portaudio"); +#endif +#if JACK_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "jack"); +#endif +#if AUFILE_SUPPORT + fluid_settings_add_option(settings, "audio.driver", "file"); +#endif + + for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { + if (fluid_audio_drivers[i].settings != NULL) { + fluid_audio_drivers[i].settings(settings); + } + } +} + + +fluid_audio_driver_t* +new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) +{ + int i; + fluid_audio_driver_t* driver = NULL; + char* name; + + fluid_settings_getstr(settings, "audio.driver", &name); + + for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { + if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name)) { + FLUID_LOG(FLUID_DBG, "Using '%s' audio driver", fluid_audio_drivers[i].name); + driver = (*fluid_audio_drivers[i].new)(settings, synth); + if (driver) { + driver->name = fluid_audio_drivers[i].name; + } + return driver; + } + } + + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver: %s", name); + return NULL; +} + +fluid_audio_driver_t* +new_fluid_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) +{ + int i; + fluid_audio_driver_t* driver = NULL; + char* name; + + fluid_settings_getstr(settings, "audio.driver", &name); + + for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { + if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name) && + (fluid_audio_drivers[i].new2 != NULL)) { + FLUID_LOG(FLUID_DBG, "Using '%s' audio driver", fluid_audio_drivers[i].name); + driver = (*fluid_audio_drivers[i].new2)(settings, func, data); + if (driver) { + driver->name = fluid_audio_drivers[i].name; + } + return driver; + } + } + + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver: %s", name); + return NULL; +} + +void +delete_fluid_audio_driver(fluid_audio_driver_t* driver) +{ + int i; + + for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { + if (fluid_audio_drivers[i].name == driver->name) { + fluid_audio_drivers[i].free(driver); + return; + } + } +} diff --git a/src/libs/fluidsynth/src/fluid_adriver.h b/src/libs/fluidsynth/src/fluid_adriver.h new file mode 100644 index 0000000000..0b925d69d7 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_adriver.h @@ -0,0 +1,39 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_AUDRIVER_H +#define _FLUID_AUDRIVER_H + +#include "fluidsynth_priv.h" + +void fluid_audio_driver_settings(fluid_settings_t* settings); + + +/* + * fluid_audio_driver_t + */ + +struct _fluid_audio_driver_t +{ + char* name; +}; + + +#endif /* _FLUID_AUDRIVER_H */ diff --git a/src/libs/fluidsynth/src/fluid_aufile.c b/src/libs/fluidsynth/src/fluid_aufile.c new file mode 100644 index 0000000000..905c829f2d --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_aufile.c @@ -0,0 +1,204 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* fluid_aufile.c + * + * Audio driver, outputs the audio to a file (non real-time) + * + */ + +#include "fluid_adriver.h" +#include "fluid_settings.h" +#include "fluid_sys.h" +#include + + +/** fluid_file_audio_driver_t + * + * This structure should not be accessed directly. Use audio port + * functions instead. + */ +typedef struct { + fluid_audio_driver_t driver; + fluid_audio_func_t callback; + void* data; + int period_size; + double sample_rate; + FILE* file; + fluid_timer_t* timer; + float* left; + float* right; + short* buf; + int buf_size; + unsigned int samples; +} fluid_file_audio_driver_t; + + +fluid_audio_driver_t* new_fluid_file_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); + +int delete_fluid_file_audio_driver(fluid_audio_driver_t* p); +void fluid_file_audio_driver_settings(fluid_settings_t* settings); +static int fluid_file_audio_run_s16(void* d, unsigned int msec); + +/************************************************************** + * + * 'file' audio driver + * + */ + +void fluid_file_audio_driver_settings(fluid_settings_t* settings) +{ + fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0, NULL, NULL); +} + + +fluid_audio_driver_t* +new_fluid_file_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth) +{ + fluid_file_audio_driver_t* dev; + int err; + char* filename; + int msec; + + dev = FLUID_NEW(fluid_file_audio_driver_t); + if (dev == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(dev, 0, sizeof(fluid_file_audio_driver_t)); + + fluid_settings_getint(settings, "audio.period-size", &dev->period_size); + fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate); + + dev->data = synth; + dev->callback = (fluid_audio_func_t) fluid_synth_process; + dev->samples = 0; + dev->left = FLUID_ARRAY(float, dev->period_size); + dev->right = FLUID_ARRAY(float, dev->period_size); + dev->buf = FLUID_ARRAY(short, 2 * dev->period_size); + dev->buf_size = 2 * dev->period_size * sizeof(short); + + if (fluid_settings_getstr(settings, "audio.file.name", &filename) == 0) { + FLUID_LOG(FLUID_ERR, "No file name specified"); + goto error_recovery; + } + + dev->file = fopen(filename, "wb"); + if (dev->file == NULL) { + FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename); + goto error_recovery; + } + + msec = (int) (0.5 + dev->period_size / dev->sample_rate * 1000.0); + dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, 1, 0); + if (dev->timer == NULL) { + FLUID_LOG(FLUID_PANIC, "Couldn't create the audio thread."); + goto error_recovery; + } + + return (fluid_audio_driver_t*) dev; + + error_recovery: + delete_fluid_file_audio_driver((fluid_audio_driver_t*) dev); + return NULL; +} + +int delete_fluid_file_audio_driver(fluid_audio_driver_t* p) +{ + fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) p; + + if (dev == NULL) { + return FLUID_OK; + } + + if (dev->timer != NULL) { + delete_fluid_timer(dev->timer); + } + + if (dev->file != NULL) { + fclose(dev->file); + } + + if (dev->left != NULL) { + FLUID_FREE(dev->left); + } + + if (dev->right != NULL) { + FLUID_FREE(dev->right); + } + + if (dev->buf != NULL) { + FLUID_FREE(dev->buf); + } + + FLUID_FREE(dev); + return FLUID_OK; +} + +static int fluid_file_audio_run_s16(void* d, unsigned int clock_time) +{ + fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) d; + float* handle[2]; + int i, k, n, offset; + float s; + unsigned int sample_time; + + handle[0] = dev->left; + handle[1] = dev->right; + + + sample_time = (unsigned int) (dev->samples / dev->sample_rate * 1000.0); + if (sample_time > clock_time) { + return 1; + } + + (*dev->callback)(dev->data, dev->period_size, 0, NULL, 2, handle); + + for (i = 0, k = 0; i < dev->period_size; i++, k += 2) { + s = 32768.0f * dev->left[i]; + fluid_clip(s, -32768.0f, 32767.0f); + dev->buf[k] = (short) s; + } + + for (i = 0, k = 1; i < dev->period_size; i++, k += 2) { + s = 32768.0f * dev->right[i]; + fluid_clip(s, -32768.0f, 32767.0f); + dev->buf[k] = (short) s; + } + + for (offset = 0; offset < dev->buf_size; offset += n) { + + n = fwrite((char*) dev->buf + offset, 1, dev->buf_size - offset, dev->file); + if (n < 0) { + FLUID_LOG(FLUID_ERR, "Audio file error"); + return 0; + } + } + + dev->samples += dev->period_size; + + return 1; + + error_recovery: + + return 0; +} diff --git a/src/libs/fluidsynth/src/fluid_chan.c b/src/libs/fluidsynth/src/fluid_chan.c new file mode 100644 index 0000000000..81eaf5038d --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_chan.c @@ -0,0 +1,352 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_chan.h" +#include "fluid_mod.h" +#include "fluid_synth.h" +#include "fluid_sfont.h" + +#define SETCC(_c,_n,_v) _c->cc[_n] = _v + +/* + * new_fluid_channel + */ +fluid_channel_t* +new_fluid_channel(fluid_synth_t* synth, int num) +{ + fluid_channel_t* chan; + + chan = FLUID_NEW(fluid_channel_t); + if (chan == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + chan->synth = synth; + chan->channum = num; + + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan); + + return chan; +} + +void +fluid_channel_init(fluid_channel_t* chan) +{ + chan->prognum = (chan->channum == 9)? 0 : chan->channum; + chan->banknum = (chan->channum == 9)? 128 : 0; + chan->sfontnum = 0; + chan->preset = fluid_synth_find_preset(chan->synth, chan->banknum, chan->prognum); + chan->interp_method = FLUID_INTERP_DEFAULT; + chan->tuning = NULL; + chan->nrpn_select = 0; +} + +void +fluid_channel_init_ctrl(fluid_channel_t* chan) +{ + int i; + + chan->key_pressure = 0; + chan->channel_pressure = 0; + chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ + chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ + chan->bank_msb = 0; + + for (i = 0; i < GEN_LAST; i++) { + chan->gen[i] = 0.0f; + chan->gen_abs[i] = 0; + } + + for (i = 0; i < 128; i++) { + SETCC(chan, i, 0); + } + + /* Volume / initial attenuation (MSB & LSB) */ + SETCC(chan, 7, 127); + SETCC(chan, 39, 0); + + /* Pan (MSB & LSB) */ + SETCC(chan, 10, 64); + SETCC(chan, 10, 64); + + /* Expression (MSB & LSB) */ + SETCC(chan, 11, 127); + SETCC(chan, 43, 127); +} + +void +fluid_channel_reset(fluid_channel_t* chan) +{ + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan); +} + +/* + * delete_fluid_channel + */ +int +delete_fluid_channel(fluid_channel_t* chan) +{ + FLUID_FREE(chan); + return FLUID_OK; +} + +/* + * fluid_channel_set_preset + */ +int +fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) +{ + fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); + fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); + + chan->preset = preset; + return FLUID_OK; +} + +/* + * fluid_channel_get_preset + */ +fluid_preset_t* +fluid_channel_get_preset(fluid_channel_t* chan) +{ + return chan->preset; +} + +/* + * fluid_channel_get_banknum + */ +unsigned int +fluid_channel_get_banknum(fluid_channel_t* chan) +{ + return chan->banknum; +} + +/* + * fluid_channel_set_prognum + */ +int +fluid_channel_set_prognum(fluid_channel_t* chan, int prognum) +{ + chan->prognum = prognum; + return FLUID_OK; +} + +/* + * fluid_channel_get_prognum + */ +int +fluid_channel_get_prognum(fluid_channel_t* chan) +{ + return chan->prognum; +} + +/* + * fluid_channel_set_banknum + */ +int +fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int banknum) +{ + chan->banknum = banknum; + return FLUID_OK; +} + +/* + * fluid_channel_cc + */ +int +fluid_channel_cc(fluid_channel_t* chan, int num, int value) +{ + chan->cc[num] = value; + + switch (num) { + + case SUSTAIN_SWITCH: + { + if (value < 64) { +/* printf("** sustain off\n"); */ + fluid_synth_damp_voices(chan->synth, chan->channum); + } else { +/* printf("** sustain on\n"); */ + } + } + break; + + case BANK_SELECT_MSB: + { + chan->bank_msb = (unsigned char) (value & 0x7f); +/* printf("** bank select msb recieved: %d\n", value); */ + + /* I fixed the handling of a MIDI bank select controller 0, + e.g., bank select MSB (or "coarse" bank select according to + my spec). Prior to this fix a channel's bank number was only + changed upon reception of MIDI bank select controller 32, + e.g, bank select LSB (or "fine" bank-select according to my + spec). [KLE] + + FIXME: is this correct? [PH] */ + fluid_channel_set_banknum(chan, (unsigned int)(value & 0x7f)); /* KLE */ + } + break; + + case BANK_SELECT_LSB: + { + /* FIXME: according to the Downloadable Sounds II specification, + bit 31 should be set when we receive the message on channel + 10 (drum channel) */ + fluid_channel_set_banknum(chan, (((unsigned int) value & 0x7f) + + ((unsigned int) chan->bank_msb << 7))); + } + break; + + case ALL_NOTES_OFF: + fluid_synth_all_notes_off(chan->synth, chan->channum); + break; + + case ALL_SOUND_OFF: + fluid_synth_all_sounds_off(chan->synth, chan->channum); + break; + + case ALL_CTRL_OFF: + fluid_channel_init_ctrl(chan); + fluid_synth_modulate_voices_all(chan->synth, chan->channum); + break; + + case DATA_ENTRY_MSB: + { + int data = (value << 7) + chan->cc[DATA_ENTRY_LSB]; + + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100)) { + float val = fluid_gen_scale_nrpn(chan->nrpn_select, data); + FLUID_LOG(FLUID_WARN, "%s: %d: Data = %d, value = %f", __FILE__, __LINE__, data, val); + fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val); + } + break; + } + + case NRPN_MSB: + chan->cc[NRPN_LSB] = 0; + chan->nrpn_select = 0; + break; + + case NRPN_LSB: + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if (chan->cc[NRPN_MSB] == 120) { + if (value == 100) { + chan->nrpn_select += 100; + } else if (value == 101) { + chan->nrpn_select += 1000; + } else if (value == 102) { + chan->nrpn_select += 10000; + } else if (value < 100) { + chan->nrpn_select += value; + FLUID_LOG(FLUID_WARN, "%s: %d: NRPN Select = %d", __FILE__, __LINE__, chan->nrpn_select); + } + } + break; + + case RPN_MSB: + break; + + case RPN_LSB: + /* erase any previously received NRPN message */ + chan->cc[NRPN_MSB] = 0; + chan->cc[NRPN_LSB] = 0; + chan->nrpn_select = 0; + break; + + default: + fluid_synth_modulate_voices(chan->synth, chan->channum, 1, num); + } + + return FLUID_OK; +} + +/* + * fluid_channel_get_cc + */ +int +fluid_channel_get_cc(fluid_channel_t* chan, int num) +{ + return ((num >= 0) && (num < 128))? chan->cc[num] : 0; +} + +/* + * fluid_channel_pitch_bend + */ +int +fluid_channel_pitch_bend(fluid_channel_t* chan, int val) +{ + chan->pitch_bend = val; + fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEEL); + return FLUID_OK; +} + +/* + * fluid_channel_pitch_wheel_sens + */ +int +fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val) +{ + chan->pitch_wheel_sensitivity = val; + fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEELSENS); + return FLUID_OK; +} + +/* + * fluid_channel_get_num + */ +int +fluid_channel_get_num(fluid_channel_t* chan) +{ + return chan->channum; +} + +/* Purpose: + * Sets the index of the interpolation method used on this channel, + * as in fluid_interp in fluidsynth.h + */ +void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method) +{ + chan->interp_method = new_method; +} + +/* Purpose: + * Returns the index of the interpolation method used on this channel, + * as in fluid_interp in fluidsynth.h + */ +int fluid_channel_get_interp_method(fluid_channel_t* chan) +{ + return chan->interp_method; +} + +unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan) +{ + return chan->sfontnum; +} + +int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfontnum) +{ + chan->sfontnum = sfontnum; + return FLUID_OK; +} diff --git a/src/libs/fluidsynth/src/fluid_chan.h b/src/libs/fluidsynth/src/fluid_chan.h new file mode 100644 index 0000000000..788efb6bb0 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_chan.h @@ -0,0 +1,105 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_CHAN_H +#define _FLUID_CHAN_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_tuning.h" + +/* + * fluid_channel_t + */ +struct _fluid_channel_t +{ + int channum; + unsigned int sfontnum; + unsigned int banknum; + unsigned int prognum; + fluid_preset_t* preset; + fluid_synth_t* synth; + short key_pressure; + short channel_pressure; + short pitch_bend; + short pitch_wheel_sensitivity; + + /* controller values */ + short cc[128]; + + /* cached values of last MSB values of MSB/LSB controllers */ + unsigned char bank_msb; + int interp_method; + + /* the micro-tuning */ + fluid_tuning_t* tuning; + + /* NRPN system */ + short nrpn_select; + + /* The values of the generators, set by NRPN messages, or by + * fluid_synth_set_gen(), are cached in the channel so they can be + * applied to future notes. They are copied to a voice's generators + * in fluid_voice_init(), wihich calls fluid_gen_init(). */ + fluid_real_t gen[GEN_LAST]; + + /* By default, the NRPN values are relative to the values of the + * generators set in the SoundFont. For example, if the NRPN + * specifies an attack of 100 msec then 100 msec will be added to the + * combined attack time of the sound font and the modulators. + * + * However, it is useful to be able to specify the generator value + * absolutely, completely ignoring the generators of the sound font + * and the values of modulators. The gen_abs field, is a boolean + * flag indicating whether the NRPN value is absolute or not. + */ + char gen_abs[GEN_LAST]; +}; + +fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); +int delete_fluid_channel(fluid_channel_t* chan); +void fluid_channel_init(fluid_channel_t* chan); +void fluid_channel_init_ctrl(fluid_channel_t* chan); +void fluid_channel_reset(fluid_channel_t* chan); +int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset); +fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan); +unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan); +int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfont); +unsigned int fluid_channel_get_banknum(fluid_channel_t* chan); +int fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int bank); +int fluid_channel_set_prognum(fluid_channel_t* chan, int prognum); +int fluid_channel_get_prognum(fluid_channel_t* chan); +int fluid_channel_cc(fluid_channel_t* chan, int ctrl, int val); +int fluid_channel_pitch_bend(fluid_channel_t* chan, int val); +int fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val); +int fluid_channel_get_cc(fluid_channel_t* chan, int num); +int fluid_channel_get_num(fluid_channel_t* chan); +void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method); +int fluid_channel_get_interp_method(fluid_channel_t* chan); + +#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } +#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) +#define fluid_channel_get_tuning(_c) ((_c)->tuning) +#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) +#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } +#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) +#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n]) + +#endif /* _FLUID_CHAN_H */ diff --git a/src/libs/fluidsynth/src/fluid_chorus.c b/src/libs/fluidsynth/src/fluid_chorus.c new file mode 100644 index 0000000000..74cf5d8867 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_chorus.c @@ -0,0 +1,605 @@ +/* + * August 24, 1998 + * Copyright (C) 1998 Juergen Mueller And Sundry Contributors + * This source code is freely redistributable and may be used for + * any purpose. This copyright notice must be maintained. + * Juergen Mueller And Sundry Contributors are not responsible for + * the consequences of using this software. + */ + +/* + + CHANGES + + - Adapted for fluidsynth, Peter Hanappe, March 2002 + + - Variable delay line implementation using bandlimited + interpolation, code reorganization: Markus Nentwig May 2002 + + */ + + +/* + * Chorus effect. + * + * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): + * + * * gain-in ___ + * ibuff -----+--------------------------------------------->| | + * | _________ | | + * | | | * level 1 | | + * +---->| delay 1 |----------------------------->| | + * | |_________| | | + * | /|\ | | + * : | | | + * : +-----------------+ +--------------+ | + | + * : | Delay control 1 |<--| mod. speed 1 | | | + * : +-----------------+ +--------------+ | | + * | _________ | | + * | | | * level n | | + * +---->| delay n |----------------------------->| | + * |_________| | | + * /|\ |___| + * | | + * +-----------------+ +--------------+ | * gain-out + * | Delay control n |<--| mod. speed n | | + * +-----------------+ +--------------+ +----->obuff + * + * + * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). + * + * The delay of each block is modulated between 0..depth ms + * + */ + + +/* Variable delay line implementation + * ================================== + * + * The modulated delay needs the value of the delayed signal between + * samples. A lowpass filter is used to obtain intermediate values + * between samples (bandlimited interpolation). The sample pulse + * train is convoluted with the impulse response of the low pass + * filter (sinc function). To make it work with a small number of + * samples, the sinc function is windowed (Hamming window). + * + */ + +#include "fluid_chorus.h" +#include "fluid_sys.h" + +#define MAX_CHORUS 99 +#define MAX_DELAY 100 +#define MAX_DEPTH 10 +#define MIN_SPEED_HZ 0.29 +#define MAX_SPEED_HZ 5 + +/* Length of one delay line in samples: + * Set through MAX_SAMPLES_LN2. + * For example: + * MAX_SAMPLES_LN2=12 + * => MAX_SAMPLES=pow(2,12)=4096 + * => MAX_SAMPLES_ANDMASK=4095 + */ +#define MAX_SAMPLES_LN2 12 + +#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1)) +#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1) + + +/* Interpolate how many steps between samples? Must be power of two + For example: 8 => use a resolution of 256 steps between any two + samples +*/ +#define INTERPOLATION_SUBSAMPLES_LN2 8 +#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1)) +#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1) + +/* Use how many samples for interpolation? Must be odd. '7' sounds + relatively clean, when listening to the modulated delay signal + alone. For a demo on aliasing try '1' With '3', the aliasing is + still quite pronounced for some input frequencies +*/ +#define INTERPOLATION_SAMPLES 5 + +/* Private data for SKEL file */ +struct _fluid_chorus_t { + /* Store the values between fluid_chorus_set_xxx and fluid_chorus_update + * Logic behind this: + * - both 'parameter' and 'new_parameter' hold the same value. + * - To change the chorus settings, 'new_parameter' is modified and + * fluid_chorus_update is called. + * - If the new value is valid, it is copied to 'parameter'. + * - If it is invalid, 'new_parameter' is restored to 'parameter'. + */ + int type; /* current value */ + int new_type; /* next value, if parameter check is OK */ + fluid_real_t depth_ms; /* current value */ + fluid_real_t new_depth_ms; /* next value, if parameter check is OK */ + fluid_real_t level; /* current value */ + fluid_real_t new_level; /* next value, if parameter check is OK */ + fluid_real_t speed_Hz; /* current value */ + fluid_real_t new_speed_Hz; /* next value, if parameter check is OK */ + int number_blocks; /* current value */ + int new_number_blocks; /* next value, if parameter check is OK */ + + fluid_real_t *chorusbuf; + int counter; + long phase[MAX_CHORUS]; + long modulation_period_samples; + int *lookup_tab; + fluid_real_t sample_rate; + + /* sinc lookup table */ + fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES]; +}; + +void fluid_chorus_triangle(int *buf, int len, int depth); +void fluid_chorus_sine(int *buf, int len, int depth); + +fluid_chorus_t* +new_fluid_chorus(fluid_real_t sample_rate) +{ + int i; int ii; + fluid_chorus_t* chorus; + + chorus = FLUID_NEW(fluid_chorus_t); + if (chorus == NULL) { + fluid_log(FLUID_PANIC, "chorus: Out of memory"); + return NULL; + } + + FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); + + chorus->sample_rate = sample_rate; + + /* Lookup table for the SI function (impulse response of an ideal low pass) */ + + /* i: Offset in terms of whole samples */ + for (i = 0; i < INTERPOLATION_SAMPLES; i++){ + + /* ii: Offset in terms of fractional samples ('subsamples') */ + for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){ + /* Move the origin into the center of the table */ + double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2. + + (double) ii / (double) INTERPOLATION_SUBSAMPLES); + if (fabs(i_shifted) < 0.000001) { + /* sinc(0) cannot be calculated straightforward (limit needed + for 0/0) */ + chorus->sinc_table[i][ii] = (fluid_real_t)1.; + + } else { + chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted); + /* Hamming window */ + chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES)); + }; + }; + }; + + /* allocate lookup tables */ + chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ)); + if (chorus->lookup_tab == NULL) { + fluid_log(FLUID_PANIC, "chorus: Out of memory"); + goto error_recovery; + } + + /* allocate sample buffer */ + + chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES); + if (chorus->chorusbuf == NULL) { + fluid_log(FLUID_PANIC, "chorus: Out of memory"); + goto error_recovery; + } + + if (fluid_chorus_init(chorus) != FLUID_OK){ + goto error_recovery; + }; + + return chorus; + + error_recovery: + delete_fluid_chorus(chorus); + return NULL; +} + + +int +fluid_chorus_init(fluid_chorus_t* chorus) +{ + int i; + + for (i = 0; i < MAX_SAMPLES; i++) { + chorus->chorusbuf[i] = 0.0; + } + + /* initialize the chorus with the default settings */ + fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N); + fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL); + fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED); + fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH); + fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE); + + return fluid_chorus_update(chorus); +} + +/* Purpose: + * Sets the number of stages. + * Requires call to fluid_chorus_update afterwards. + * Range checking is performed there.*/ +void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr) +{ + chorus->new_number_blocks = nr; +} + +/* Purpose: + * API function, read the current state of the chorus + */ +int fluid_chorus_get_nr(fluid_chorus_t* chorus) +{ + return chorus->number_blocks; +}; + +/* Purpose: + * Sets the mixing level of the signal from each delay line (linear). + * Requires calling fluid_chorus_update afterwards.*/ +void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level) +{ + chorus->new_level = level; +} + +/* Purpose: + * API function, read the current state of the chorus + */ +fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus) +{ + return chorus->level; +}; + +/* Purpose: + * Sets the modulation frequency. + * Requires call to fluid_chorus_update afterwards. + * Range checking is performed there.*/ +void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz) +{ + chorus->new_speed_Hz = speed_Hz; +} + +/* Purpose: + * API function, read the current state of the chorus + */ +fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus) +{ + return chorus->speed_Hz; +}; + +/* Purpose: + * Sets the modulation depth in ms. + * Requires call to fluid_chorus_update afterwards. + * Range checking is performed there.*/ +void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms) +{ + chorus->new_depth_ms=depth_ms; +} + +/* Purpose: + * API function, read the current state of the chorus + */ +fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus) +{ + return chorus->depth_ms; +}; + +/* Purpose: + * Sets the type of the modulation waveform. + * Requires call to fluid_chorus_update afterwards. + * Check for meaningful values is performed there.*/ +void fluid_chorus_set_type(fluid_chorus_t* chorus, int type) +{ + chorus->new_type=type; +} + +/* Purpose: + * API function, read the current state of the chorus + */ +int fluid_chorus_get_type(fluid_chorus_t* chorus) +{ + return chorus->type; +}; + +void +delete_fluid_chorus(fluid_chorus_t* chorus) +{ + if (chorus == NULL) { + return; + } + + if (chorus->chorusbuf != NULL) { + FLUID_FREE(chorus->chorusbuf); + } + + if (chorus->lookup_tab != NULL) { + FLUID_FREE(chorus->lookup_tab); + } + + FLUID_FREE(chorus); +} + + +/* Purpose: + * Calculates the internal chorus parameters using the settings from + * fluid_chorus_set_xxx. */ +int +fluid_chorus_update(fluid_chorus_t* chorus) +{ + int i; + int modulation_depth_samples; + + if (chorus->new_number_blocks < 0) { + fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); + chorus->new_number_blocks = 0; + } else if (chorus->new_number_blocks > MAX_CHORUS) { + fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", + MAX_CHORUS); + chorus->new_number_blocks = MAX_CHORUS; + }; + + if (chorus->new_speed_Hz < MIN_SPEED_HZ) { + fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", + (double) MIN_SPEED_HZ); + chorus->new_speed_Hz = MIN_SPEED_HZ; + } else if (chorus->new_speed_Hz > MAX_SPEED_HZ) { + fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", + (double) MAX_SPEED_HZ); + chorus->new_speed_Hz = MAX_SPEED_HZ; + } + if (chorus->new_depth_ms < 0.0) { + fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); + chorus->new_depth_ms = 0.0; + } + /* Depth: Check for too high value through modulation_depth_samples. */ + + if (chorus->new_level < 0.0) { + fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); + chorus->new_level = 0.0; + } else if (chorus->new_level > 10) { + fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " + "Setting it to 0.1."); + chorus->new_level = 0.1; + } + + /* The modulating LFO goes through a full period every x samples: */ + chorus->modulation_period_samples = chorus->sample_rate / chorus->new_speed_Hz; + + /* The variation in delay time is x: */ + modulation_depth_samples = (int) + (chorus->new_depth_ms / 1000.0 /* convert modulation depth in ms to s*/ + * chorus->sample_rate); + + if (modulation_depth_samples > MAX_SAMPLES) { + fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); + modulation_depth_samples = MAX_SAMPLES; + } + + /* initialize LFO table */ + if (chorus->type == FLUID_CHORUS_MOD_SINE) { + fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, + modulation_depth_samples); + } else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) { + fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples, + modulation_depth_samples); + } else { + fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); + chorus->type = FLUID_CHORUS_MOD_SINE; + fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, + modulation_depth_samples); + }; + + for (i = 0; i < chorus->number_blocks; i++) { + /* Set the phase of the chorus blocks equally spaced */ + chorus->phase[i] = (int) ((double) chorus->modulation_period_samples + * (double) i / (double) chorus->number_blocks); + } + + /* Start of the circular buffer */ + chorus->counter = 0; + + chorus->type = chorus->new_type; + chorus->depth_ms = chorus->new_depth_ms; + chorus->level = chorus->new_level; + chorus->speed_Hz = chorus->new_speed_Hz; + chorus->number_blocks = chorus->new_number_blocks; + return FLUID_OK; + +/* failure: */ + /* Note: This lives on the assumption, that the last chorus values were correct. + * If not, this will loop forever and a day. */ +/* fluid_log(FLUID_WARN, "chorus: Restoring last good settings"); */ +/* chorus->new_type = chorus->type; */ +/* chorus->new_depth_ms = chorus->depth_ms; */ +/* chorus->new_level = chorus->level; */ +/* chorus->new_speed_Hz = chorus->speed_Hz; */ +/* chorus->new_number_blocks = chorus->number_blocks; */ +/* return FLUID_FAILED; */ +} + + +void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_in, d_out; + + for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { + + d_in = in[sample_index]; + d_out = 0.0f; + +# if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index]=0; + right_out[sample_index]=0; +#endif + + /* Write the current sample into the circular buffer */ + chorus->chorusbuf[chorus->counter] = d_in; + + for (i = 0; i < chorus->number_blocks; i++) { + int ii; + /* Calculate the delay in subsamples for the delay line of chorus block nr. */ + + /* The value in the lookup table is so, that this expression + * will always be positive. It will always include a number of + * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to + * remain positive at all times. */ + int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter + - chorus->lookup_tab[chorus->phase[i]]); + + int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES; + + /* modulo divide by INTERPOLATION_SUBSAMPLES */ + pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; + + for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ + /* Add the delayed signal to the chorus sum d_out Note: The + * delay in the delay line moves backwards for increasing + * delay!*/ + + /* The & in chorusbuf[...] is equivalent to a division modulo + MAX_SAMPLES, only faster. */ + d_out += (chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] + * chorus->sinc_table[ii][pos_subsamples]); + + pos_samples--; + }; + /* Cycle the phase of the modulating LFO */ + chorus->phase[i]++; + chorus->phase[i] %= (chorus->modulation_period_samples); + } /* foreach chorus block */ + + d_out *= chorus->level; + + /* Add the chorus sum d_out to output */ + left_out[sample_index] += d_out; + right_out[sample_index] += d_out; + + /* Move forward in circular buffer */ + chorus->counter++; + chorus->counter %= MAX_SAMPLES; + + } /* foreach sample */ +} + +/* Duplication of code ... */ +void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_in, d_out; + + for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { + + d_in = in[sample_index]; + d_out = 0.0f; + +# if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index]=0; + right_out[sample_index]=0; +#endif + + /* Write the current sample into the circular buffer */ + chorus->chorusbuf[chorus->counter] = d_in; + + for (i = 0; i < chorus->number_blocks; i++) { + int ii; + /* Calculate the delay in subsamples for the delay line of chorus block nr. */ + + /* The value in the lookup table is so, that this expression + * will always be positive. It will always include a number of + * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to + * remain positive at all times. */ + int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter + - chorus->lookup_tab[chorus->phase[i]]); + + int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; + + /* modulo divide by INTERPOLATION_SUBSAMPLES */ + pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; + + for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ + /* Add the delayed signal to the chorus sum d_out Note: The + * delay in the delay line moves backwards for increasing + * delay!*/ + + /* The & in chorusbuf[...] is equivalent to a division modulo + MAX_SAMPLES, only faster. */ + d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] * chorus->sinc_table[ii][pos_subsamples]; + + pos_samples--; + }; + /* Cycle the phase of the modulating LFO */ + chorus->phase[i]++; + chorus->phase[i] %= (chorus->modulation_period_samples); + } /* foreach chorus block */ + + d_out *= chorus->level; + + /* Add the chorus sum d_out to output */ + left_out[sample_index] = d_out; + right_out[sample_index] = d_out; + + /* Move forward in circular buffer */ + chorus->counter++; + chorus->counter %= MAX_SAMPLES; + + } /* foreach sample */ +} + +/* Purpose: + * + * Calculates a modulation waveform (sine) Its value ( modulo + * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES. + * Its period length is len. The waveform data will be used modulo + * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform + * a couple of times here, the resulting (current position in + * buffer)-(waveform sample) will always be positive. + */ +void fluid_chorus_sine(int *buf, int len, int depth) +{ + int i; + double val; + + for (i = 0; i < len; i++) { + val = sin((double) i / (double)len * 2.0 * M_PI); + buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES); + buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; + // printf("%i %i\n",i,buf[i]); + } +} + +/* Purpose: + * Calculates a modulation waveform (triangle) + * See fluid_chorus_sine for comments. + */ +void fluid_chorus_triangle(int *buf, int len, int depth) +{ + int i=0; + int ii=len-1; + double val; + double val2; + + while (i <= ii){ + val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES; + val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; + buf[i++] = (int) val2; + buf[ii--] = (int) val2; + } +} + +void +fluid_chorus_reset(fluid_chorus_t* chorus) +{ + fluid_chorus_init(chorus); +} diff --git a/src/libs/fluidsynth/src/fluid_chorus.h b/src/libs/fluidsynth/src/fluid_chorus.h new file mode 100644 index 0000000000..1a26710b25 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_chorus.h @@ -0,0 +1,56 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_CHORUS_H +#define _FLUID_CHORUS_H + +#include "fluidsynth_priv.h" + + +typedef struct _fluid_chorus_t fluid_chorus_t; + +/* + * chorus + */ +fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate); +void delete_fluid_chorus(fluid_chorus_t* chorus); +void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); +void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +int fluid_chorus_init(fluid_chorus_t* chorus); +void fluid_chorus_reset(fluid_chorus_t* chorus); + +void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr); +void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level); +void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz); +void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms); +void fluid_chorus_set_type(fluid_chorus_t* chorus, int type); +int fluid_chorus_update(fluid_chorus_t* chorus); +int fluid_chorus_get_nr(fluid_chorus_t* chorus); +fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus); +fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus); +fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus); +int fluid_chorus_get_type(fluid_chorus_t* chorus); + + +#endif /* _FLUID_CHORUS_H */ diff --git a/src/libs/fluidsynth/src/fluid_cmd.c b/src/libs/fluidsynth/src/fluid_cmd.c new file mode 100644 index 0000000000..dd880c6fa8 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_cmd.c @@ -0,0 +1,1852 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluidsynth_priv.h" +#include "fluid_cmd.h" +#include "fluid_synth.h" +#include "fluid_strtok.h" +#include "fluid_settings.h" +#include "fluid_io.h" +#include "fluid_hash.h" +#include "fluid_sys.h" +#include "fluid_io.h" +#include "fluid_midi_router.h" +#include "fluid_sfont.h" + +#if WITH_READLINE +#include +#include +#endif + +#define MAX_TOKENS 100 /* LADSPA plugins need lots of parameters */ +#define FLUID_WORKLINELENGTH 1024 /* LADSPA plugins use long command lines */ + +void fluid_shell_settings(fluid_settings_t* settings) +{ + fluid_settings_register_str(settings, "shell.prompt", "", 0, NULL, NULL); + fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0, NULL, NULL); +} + + +/** the table of all handled commands */ + +fluid_cmd_t fluid_commands[] = { + { "help", "general", (fluid_cmd_func_t) fluid_handle_help, NULL, + "help Command summary. 'help help' for more help topics" }, + { "quit", "general", (fluid_cmd_func_t) fluid_handle_quit, NULL, + "quit Quit the synthesizer" }, + { "noteon", "event", (fluid_cmd_func_t) fluid_handle_noteon, NULL, + "noteon chan key vel Send noteon" }, + { "noteoff", "event", (fluid_cmd_func_t) fluid_handle_noteoff, NULL, + "noteoff chan key Send noteoff" }, + { "cc", "event", (fluid_cmd_func_t) fluid_handle_cc, NULL, + "cc chan ctrl value Send control-change message" }, + { "prog", "event", (fluid_cmd_func_t) fluid_handle_prog, NULL, + "prog chan num Send program-change message" }, + { "select", "event", (fluid_cmd_func_t) fluid_handle_select, NULL, + "select chan sfont bank prog Combination of bank-select and program-change" }, + { "load", "general", (fluid_cmd_func_t) fluid_handle_load, NULL, + "load file [reset] [bankofs] Load SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, + { "unload", "general", (fluid_cmd_func_t) fluid_handle_unload, NULL, + "unload id [reset] Unload SoundFont by ID (reset=0|1, default 1)"}, + { "reload", "general", (fluid_cmd_func_t) fluid_handle_reload, NULL, + "reload id Reload the SoundFont with the specified ID" }, + { "fonts", "general", (fluid_cmd_func_t) fluid_handle_fonts, NULL, + "fonts Display the list of loaded SoundFonts" }, + { "inst", "general", (fluid_cmd_func_t) fluid_handle_inst, NULL, + "inst font Print out the available instruments for the font" }, + { "channels", "general", (fluid_cmd_func_t) fluid_handle_channels, NULL, + "channels [-verbose] Print out preset of all channels" }, + { "interp", "general", (fluid_cmd_func_t) fluid_handle_interp, NULL, + "interp num Choose interpolation method for all channels" }, + { "interpc", "general", (fluid_cmd_func_t) fluid_handle_interpc, NULL, + "interpc chan num Choose interpolation method for one channel" }, + { "rev_preset", "reverb", (fluid_cmd_func_t) fluid_handle_reverbpreset, NULL, + "rev_preset num Load preset num into the reverb unit" }, + { "rev_setroomsize", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetroomsize, NULL, + "rev_setroomsize num Change reverb room size" }, + { "rev_setdamp", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetdamp, NULL, + "rev_setdamp num Change reverb damping" }, + { "rev_setwidth", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetwidth, NULL, + "rev_setwidth num Change reverb width" }, + { "rev_setlevel", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetlevel, NULL, + "rev_setlevel num Change reverb level" }, + { "reverb", "reverb", (fluid_cmd_func_t) fluid_handle_reverb, NULL, + "reverb [0|1|on|off] Turn the reverb on or off" }, + { "cho_set_nr", "chorus", (fluid_cmd_func_t) fluid_handle_chorusnr, NULL, + "cho_set_nr n Use n delay lines (default 3)" }, + { "cho_set_level", "chorus", (fluid_cmd_func_t) fluid_handle_choruslevel, NULL, + "cho_set_level num Set output level of each chorus line to num" }, + { "cho_set_speed", "chorus", (fluid_cmd_func_t) fluid_handle_chorusspeed, NULL, + "cho_set_speed num Set mod speed of chorus to num (Hz)" }, + { "cho_set_depth", "chorus", (fluid_cmd_func_t) fluid_handle_chorusdepth, NULL, + "cho_set_depth num Set chorus modulation depth to num (ms)" }, + { "chorus", "chorus", (fluid_cmd_func_t) fluid_handle_chorus, NULL, + "chorus [0|1|on|off] Turn the chorus on or off" }, + { "gain", "general", (fluid_cmd_func_t) fluid_handle_gain, NULL, + "gain value Set the master gain (0 < gain < 5)" }, + { "tuning", "tuning", (fluid_cmd_func_t) fluid_handle_tuning, NULL, + "tuning name bank prog Create a tuning with name, bank number, \n" + " and program number (0 <= bank,prog <= 127)" }, + { "tune", "tuning", (fluid_cmd_func_t) fluid_handle_tune, NULL, + "tune bank prog key pitch Tune a key" }, + { "settuning", "tuning", (fluid_cmd_func_t) fluid_handle_settuning, NULL, + "settuning chan bank prog Set the tuning for a MIDI channel" }, + { "resettuning", "tuning", (fluid_cmd_func_t) fluid_handle_resettuning, NULL, + "resettuning chan Restore the default tuning of a MIDI channel" }, + { "tunings", "tuning", (fluid_cmd_func_t) fluid_handle_tunings, NULL, + "tunings Print the list of available tunings" }, + { "dumptuning", "tuning", (fluid_cmd_func_t) fluid_handle_dumptuning, NULL, + "dumptuning bank prog Print the pitch details of the tuning" }, + { "reset", "general", (fluid_cmd_func_t) fluid_handle_reset, NULL, + "reset System reset (all notes off, reset controllers)" }, + { "set", "settings", (fluid_cmd_func_t) fluid_handle_set, NULL, + "set name value Set the value of a controller or settings" }, + { "get", "settings", (fluid_cmd_func_t) fluid_handle_get, NULL, + "get name Get the value of a controller or settings" }, + { "info", "settings", (fluid_cmd_func_t) fluid_handle_info, NULL, + "info name Get information about a controller or settings" }, + { "settings", "settings", (fluid_cmd_func_t) fluid_handle_settings, NULL, + "settings Print out all settings" }, + { "echo", "general", (fluid_cmd_func_t) fluid_handle_echo, NULL, + "echo arg Print arg" }, + /* LADSPA-related commands */ +#ifdef LADSPA + { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL, + "ladspa_clear Resets LADSPA effect unit to bypass state"}, + { "ladspa_add", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_add, NULL, + "ladspa_add lib plugin n1 <- p1 n2 -> p2 ... Loads and connects LADSPA plugin"}, + { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_start, NULL, + "ladspa_start Starts LADSPA effect unit"}, + { "ladspa_declnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_declnode, NULL, + "ladspa_declnode node value Declares control node `node' with value `value'"}, + { "ladspa_setnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_setnode, NULL, + "ladspa_setnode node value Assigns `value' to `node'"}, +#endif + { "router_clear", "router", (fluid_cmd_func_t) fluid_midi_router_handle_clear, NULL, + "router_clear Clears all routing rules from the midi router"}, + { "router_default", "router", (fluid_cmd_func_t) fluid_midi_router_handle_default, NULL, + "router_default Resets the midi router to default state"}, + { "router_begin", "router", (fluid_cmd_func_t) fluid_midi_router_handle_begin, NULL, + "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule"}, + { "router_chan", "router", (fluid_cmd_func_t) fluid_midi_router_handle_chan, NULL, + "router_chan min max mul add filters and maps midi channels on current rule"}, + { "router_par1", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par1, NULL, + "router_par1 min max mul add filters and maps parameter 1 (key/ctrl nr)"}, + { "router_par2", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par2, NULL, + "router_par2 min max mul add filters and maps parameter 2 (vel/cc val)"}, + { "router_end", "router", (fluid_cmd_func_t) fluid_midi_router_handle_end, NULL, + "router_end closes and commits the current routing rule"}, + { NULL, NULL, NULL, NULL, NULL } +}; + +int fluid_command2(fluid_strtok_t* strtok, fluid_cmd_handler_t* handler, + char* cmd, fluid_ostream_t out); + +int +fluid_command(fluid_cmd_handler_t* handler, char* cmd, fluid_ostream_t out) +{ + int ret; + + fluid_strtok_t* st = new_fluid_strtok(cmd, " \t\n\r"); + if (st == NULL) { + return -1; + } + + ret = fluid_command2(st, handler, cmd, out); + delete_fluid_strtok(st); + return ret; +} + +int +fluid_command2(fluid_strtok_t* st, fluid_cmd_handler_t* handler, char* cmd, fluid_ostream_t out) +{ + char* token[MAX_TOKENS]; + int num_tokens = 0; + + if (cmd[0] == '#') { + return 1; + } + + /* tokenize the input line */ + fluid_strtok_set(st, cmd, " \t\n\r"); + while (fluid_strtok_has_more(st)) { + token[num_tokens++] = fluid_strtok_next_token(st); + } + + if (num_tokens == 0) { + return 1; + } + + /* handle the command */ + return fluid_cmd_handler_handle(handler, num_tokens, &token[0], out); +} + +struct _fluid_shell_t { + fluid_settings_t* settings; + fluid_cmd_handler_t* handler; + fluid_thread_t* thread; + fluid_strtok_t* st; + fluid_istream_t in; + fluid_ostream_t out; +}; + +int fluid_shell_run(fluid_shell_t* shell); +void fluid_shell_init(fluid_shell_t* shell, + fluid_settings_t* settings, fluid_cmd_handler_t* handler, + fluid_istream_t in, fluid_ostream_t out); + + +fluid_shell_t* new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* handler, + fluid_istream_t in, fluid_ostream_t out, int thread) +{ + fluid_shell_t* shell = FLUID_NEW(fluid_shell_t); + if (shell == NULL) { + FLUID_LOG (FLUID_PANIC, "Out of memory"); + return NULL; + } + + + fluid_shell_init(shell, settings, handler, in, out); + + if (thread) { + shell->thread = new_fluid_thread((fluid_thread_func_t) fluid_shell_run, shell, 1); + if (shell->thread == NULL) { + delete_fluid_shell(shell); + return NULL; + } + } else { + shell->thread = NULL; + fluid_shell_run(shell); + } + + return shell; + +} + +void fluid_shell_init(fluid_shell_t* shell, + fluid_settings_t* settings, fluid_cmd_handler_t* handler, + fluid_istream_t in, fluid_ostream_t out) +{ + shell->settings = settings; + shell->handler = handler; + shell->st = new_fluid_strtok(NULL, NULL); + shell->in = in; + shell->out = out; +} + +void delete_fluid_shell(fluid_shell_t* shell) +{ + if (shell->thread != NULL) { + delete_fluid_thread(shell->thread); + } + FLUID_FREE(shell); +} + + +int fluid_shell_run(fluid_shell_t* shell) +{ + char workline[FLUID_WORKLINELENGTH]; + char* prompt = ""; + int cont = 1; + int errors = 0; + int n; + + if (shell->settings) { + fluid_settings_getstr(shell->settings, "shell.prompt", &prompt); + } + + /* handle user input */ + while (cont) { + + n = fluid_istream_readline(shell->in, prompt, workline, FLUID_WORKLINELENGTH); + + if (n < 0) { + break; + } + +#if WITH_READLINE + if (shell->in == fluid_get_stdin()) { + add_history(workline); + } +#endif + + /* handle the command */ + switch (fluid_command2(shell->st, shell->handler, workline, shell->out)) { + + case 1: /* empty line or comment */ + break; + + case -1: /* erronous command */ + errors++; + case 0: /* valid command */ + break; + + case -2: /* quit */ + cont = 0; + break; + } + + if (n == 0) { + break; + } + } + + return errors; +} + + +void +fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler) +{ + fluid_shell_t shell; + fluid_shell_init(&shell, settings, handler, fluid_get_stdin(), fluid_get_stdout()); + fluid_shell_run(&shell); +} + +int +fluid_source(fluid_cmd_handler_t* handler, char* filename) +{ + int file; + fluid_shell_t shell; + +#ifdef WIN32 + file = _open(filename, _O_RDONLY); +#else + file = open(filename, O_RDONLY); +#endif + if (file < 0) { + return file; + } + fluid_shell_init(&shell, NULL, handler, file, fluid_get_stdout()); + return fluid_shell_run(&shell); +} + + +char* +fluid_get_userconf(char* buf, int len) +{ +#if defined(WIN32) || defined(MACOS9) + return NULL; +#else + char* home = getenv("HOME"); + if (home == NULL) { + return NULL; + } else { + snprintf(buf, len, "%s/.fluidsynth", home); + return buf; + } +#endif +} + +char* +fluid_get_sysconf(char* buf, int len) +{ +#if defined(WIN32) || defined(MACOS9) + return NULL; +#else + snprintf(buf, len, "/etc/fluidsynth.conf"); + return buf; +#endif +} + + +/* + * handlers + */ +int +fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 3) { + fluid_ostream_printf(out, "noteon: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { + fluid_ostream_printf(out, "noteon: invalid argument\n"); + return -1; + } + return fluid_synth_noteon(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); +} + +int +fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 2) { + fluid_ostream_printf(out, "noteoff: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { + fluid_ostream_printf(out, "noteon: invalid argument\n"); + return -1; + } + return fluid_synth_noteoff(synth, atoi(av[0]), atoi(av[1])); +} + +int +fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 3) { + fluid_ostream_printf(out, "cc: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { + fluid_ostream_printf(out, "cc: invalid argument\n"); + return -1; + } + return fluid_synth_cc(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); +} + +int +fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 2) { + fluid_ostream_printf(out, "prog: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { + fluid_ostream_printf(out, "prog: invalid argument\n"); + return -1; + } + return fluid_synth_program_change(synth, atoi(av[0]), atoi(av[1])); +} + +int +fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int sfont_id; + int chan; + int bank; + int prog; + + if (ac < 4) { + fluid_ostream_printf(out, "preset: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) + || !fluid_is_number(av[2]) || !fluid_is_number(av[3])) { + fluid_ostream_printf(out, "preset: invalid argument\n"); + return -1; + } + + chan = atoi(av[0]); + sfont_id = atoi(av[1]); + bank = atoi(av[2]); + prog = atoi(av[3]); + + if (sfont_id != 0) { + return fluid_synth_program_select(synth, chan, sfont_id, bank, prog); + } else { + if (fluid_synth_bank_select(synth, chan, bank) == FLUID_OK) { + return fluid_synth_program_change(synth, chan, prog); + } + return FLUID_FAILED; + } +} + +int +fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int font; + fluid_sfont_t* sfont; + fluid_preset_t preset; + int offset; + + if (ac < 1) { + fluid_ostream_printf(out, "inst: too few arguments\n"); + return -1; + } + + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "inst: invalid argument\n"); + return -1; + } + + font = atoi(av[0]); + + sfont = fluid_synth_get_sfont_by_id(synth, font); + offset = fluid_synth_get_bank_offset(synth, font); + + if (sfont == NULL) { + fluid_ostream_printf(out, "inst: invalid font number\n"); + return -1; + } + + fluid_sfont_iteration_start(sfont); + + while (fluid_sfont_iteration_next(sfont, &preset)) { + fluid_ostream_printf(out, "%03d-%03d %s\n", + fluid_preset_get_banknum(&preset) + offset, + fluid_preset_get_num(&preset), + fluid_preset_get_name(&preset)); + } + + return 0; +} + + +int +fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int i; + fluid_preset_t* preset; + int verbose = 0; + + if (ac > 0 && strcmp( av[0], "-verbose") == 0) verbose = 1; + + for (i = 0; i < fluid_synth_count_midi_channels(synth); i++) { + preset = fluid_synth_get_channel_preset(synth, i); + if (preset == NULL) fluid_ostream_printf(out, "chan %d, no preset\n", i); + else if (!verbose) fluid_ostream_printf(out, "chan %d, %s\n", i, fluid_preset_get_name(preset)); + else fluid_ostream_printf(out, "chan %d, sfont %d, bank %d, preset %d, %s\n", i, + fluid_sfont_get_id( preset->sfont), + fluid_preset_get_banknum(preset), + fluid_preset_get_num(preset), + fluid_preset_get_name(preset)); + } + return 0; +} + +int +fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + char buf[1024]; + int id; + int reset = 1; + int offset = 0; + + if (ac < 1) { + fluid_ostream_printf(out, "load: too few arguments\n"); + return -1; + } + if (ac == 2) { + reset = atoi(av[1]); + } + if (ac == 3) { + offset = atoi(av[2]); + } + + /* Load the SoundFont without resetting the programs. The reset will + * be done later (if requested). */ + id = fluid_synth_sfload(synth, fluid_expand_path(av[0], buf, 1024), 0); + + if (id == -1) { + fluid_ostream_printf(out, "failed to load the SoundFont\n"); + return -1; + } else { + fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id); + } + + if (offset) { + fluid_synth_set_bank_offset(synth, id, offset); + } + + /* The reset should be done after the offset is set. */ + if (reset) { + fluid_synth_program_reset(synth); + } + + return 0; +} + +int +fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int reset = 1; + if (ac < 1) { + fluid_ostream_printf(out, "unload: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "unload: expected a number as argument\n"); + return -1; + } + if (ac == 2) { + reset = atoi(av[1]); + } + if (fluid_synth_sfunload(synth, atoi(av[0]), reset) != 0) { + fluid_ostream_printf(out, "failed to unload the SoundFont\n"); + return -1; + } + return 0; +} + +int +fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "reload: too few arguments\n"); + return -1; + } + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "reload: expected a number as argument\n"); + return -1; + } + if (fluid_synth_sfreload(synth, atoi(av[0])) == -1) { + fluid_ostream_printf(out, "failed to reload the SoundFont\n"); + return -1; + } + return 0; +} + + +int +fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int i; + fluid_sfont_t* sfont; + int num; + + num = fluid_synth_sfcount(synth); + + if (num == 0) { + fluid_ostream_printf(out, "no SoundFont loaded (try load)\n"); + return 0; + } + + fluid_ostream_printf(out, "ID Name\n"); + + for (i = 0; i < num; i++) { + sfont = fluid_synth_get_sfont(synth, i); + fluid_ostream_printf(out, "%2d %s\n", + fluid_sfont_get_id(sfont), + fluid_sfont_get_name(sfont)); + } + + return 0; +} + +int +fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ +/* fluid_ostream_printf(out, "Dvr=%s, Dev=%s\n", */ +/* fluid_midi_handler_get_driver_name(midi), */ +/* fluid_midi_handler_get_device_name(midi)); */ +/* fluid_ostream_printf(out, "Stat=%s, On=%d, Off=%d, Prog=%d, Pbend=%d, Err=%d\n", */ +/* fluid_midi_handler_get_status(midi), */ +/* fluid_midi_handler_get_event_count(midi, 0x90), */ +/* fluid_midi_handler_get_event_count(midi, 0x80), */ +/* fluid_midi_handler_get_event_count(midi, 0xc0), */ +/* fluid_midi_handler_get_event_count(midi, 0xe0), */ +/* fluid_midi_handler_get_event_count(midi, 0)); */ + fluid_ostream_printf(out, "not yet implemented\n"); + return -1; +} + +/* Purpose: + * Response to 'rev_preset' command. + * Load the values from a reverb preset into the reverb unit. */ +int +fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int reverb_preset_number; + if (ac < 1) { + fluid_ostream_printf(out, "rev_preset: too few arguments\n"); + return -1; + } + reverb_preset_number = atoi(av[0]); + if (fluid_synth_set_reverb_preset(synth, reverb_preset_number)!=FLUID_OK){ + fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n"); + return -1; + }; + return 0; +} + +/* Purpose: + * Response to 'rev_setroomsize' command. + * Load the new room size into the reverb unit. */ +int +fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t room_size; + if (ac < 1) { + fluid_ostream_printf(out, "rev_setroomsize: too few arguments.\n"); + return -1; + } + room_size = atof(av[0]); + if (room_size < 0){ + fluid_ostream_printf(out, "rev_setroomsize: Room size must be positive!\n"); + return -1; + } + if (room_size > 1.2){ + fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n"); + return -1; + } + fluid_revmodel_setroomsize(synth->reverb, room_size); + return 0; +} + +/* Purpose: + * Response to 'rev_setdamp' command. + * Load the new damp factor into the reverb unit. */ +int +fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t damp; + if (ac < 1) { + fluid_ostream_printf(out, "rev_setdamp: too few arguments.\n"); + return -1; + } + damp = atof(av[0]); + if ((damp < 0.0f) || (damp > 1)){ + fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n"); + return -1; + } + fluid_revmodel_setdamp(synth->reverb, damp); + return 0; +} + +/* Purpose: + * Response to 'rev_setwidth' command. + * Load the new width into the reverb unit. */ +int +fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t width; + if (ac < 1) { + fluid_ostream_printf(out, "rev_setwidth: too few arguments.\n"); + return -1; + } + width = atof(av[0]); + if ((width < 0) || (width > 100)){ + fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n"); + return 0; + } + fluid_revmodel_setwidth(synth->reverb, width); + return 0; +} + +/* Purpose: + * Response to 'rev_setlevel' command. + * Load the new level into the reverb unit. */ +int +fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t level; + if (ac < 1) { + fluid_ostream_printf(out, "rev_setlevel: too few arguments.\n"); + return -1; + } + level = atof(av[0]); + if (abs(level) > 30){ + fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n"); + return 0; + } + fluid_revmodel_setlevel(synth->reverb, level); + return 0; +} + +/* Purpose: + * Response to 'reverb' command. + * Change the FLUID_REVERB flag in the synth */ +int +fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "reverb: too few arguments.\n"); + return -1; + } + + if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { + fluid_synth_set_reverb_on(synth,0); + } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { + fluid_synth_set_reverb_on(synth,1); + } else { + fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]); + return -1; + } + + return 0; +} + + +/* Purpose: + * Response to 'chorus_setnr' command */ +int +fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int nr; + if (ac < 1) { + fluid_ostream_printf(out, "cho_set_nr: too few arguments.\n"); + return -1; + } + nr = atoi(av[0]); + fluid_chorus_set_nr(synth->chorus, nr); + return fluid_chorus_update(synth->chorus); +} + +/* Purpose: + * Response to 'chorus_setlevel' command */ +int +fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t level; + if (ac < 1) { + fluid_ostream_printf(out, "cho_set_level: too few arguments.\n"); + return -1; + } + level = atof(av[0]); + fluid_chorus_set_level(synth->chorus, level); + return fluid_chorus_update(synth->chorus); + +} + +/* Purpose: + * Response to 'chorus_setspeed' command */ +int +fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t speed; + if (ac < 1) { + fluid_ostream_printf(out, "cho_set_speed: too few arguments.\n"); + return -1; + } + speed = atof(av[0]); + fluid_chorus_set_speed_Hz(synth->chorus, speed); + return fluid_chorus_update(synth->chorus); +} + +/* Purpose: + * Response to 'chorus_setdepth' command */ +int +fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_real_t depth; + if (ac < 1) { + fluid_ostream_printf(out, "cho_set_depth: too few arguments.\n"); + return -1; + } + depth = atof(av[0]); + fluid_chorus_set_depth_ms(synth->chorus, depth); + return fluid_chorus_update(synth->chorus); +} + +int +fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "chorus: too few arguments\n"); + return -1; + } + + if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { + fluid_synth_set_chorus_on(synth,0); + } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { + fluid_synth_set_chorus_on(synth,1); + } else { + fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]); + return -1; + } + + return 0; +} + +/* Purpose: + * Response to the 'echo' command. + * The command itself is useful, when the synth is used via TCP/IP. + * It can signal for example, that a list of commands has been processed. + */ +int +fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "echo: too few arguments.\n"); + return -1; + } + + fluid_ostream_printf(out, "%s\n",av[0]); + + return 0; +} + +int +fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "source: too few arguments.\n"); + return -1; + } + + fluid_source(handler, av[0]); + + return 0; +} + +/* Purpose: + * Response to 'gain' command. */ +int +fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + float gain; + + if (ac < 1) { + fluid_ostream_printf(out, "gain: too few arguments.\n"); + return -1; + } + + gain = atof(av[0]); + + if ((gain < 0.0f) || (gain > 5.0f)) { + fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n"); + return -1; + }; + + fluid_synth_set_gain(synth, gain); + + return 0; +} + +/* Purpose: + * Response to 'interp' command. */ +int +fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int interp; + int chan=-1; /* -1: Set all channels */ + + if (ac < 1) { + fluid_ostream_printf(out, "interp: too few arguments.\n"); + return -1; + } + + interp = atoi(av[0]); + + if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { + fluid_ostream_printf(out, "interp: Bad value\n"); + return -1; + }; + + fluid_synth_set_interp_method(synth, chan, interp); + + return 0; +} + +/* Purpose: + * Response to 'interp' command. */ +int +fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int interp; + int chan; + + if (ac < 2) { + fluid_ostream_printf(out, "interpc: too few arguments.\n"); + return -1; + } + + chan = atoi(av[0]); + interp = atoi(av[1]); + + if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ + fluid_ostream_printf(out, "interp: Bad value for channel number.\n"); + return -1; + }; + if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { + fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n"); + return -1; + }; + + fluid_synth_set_interp_method(synth, chan, interp); + + return 0; +} + +int +fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + char *name; + int bank, prog; + + if (ac < 3) { + fluid_ostream_printf(out, "tuning: too few arguments.\n"); + return -1; + } + + name = av[0]; + + if (!fluid_is_number(av[1])) { + fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); + return -1; + } + bank = atoi(av[1]); + if ((bank < 0) || (bank >= 128)){ + fluid_ostream_printf(out, "tuning: invalid bank number.\n"); + return -1; + }; + + if (!fluid_is_number(av[2])) { + fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); + return -1; + } + prog = atoi(av[2]); + if ((prog < 0) || (prog >= 128)){ + fluid_ostream_printf(out, "tuning: invalid program number.\n"); + return -1; + }; + + fluid_synth_create_key_tuning(synth, bank, prog, name, NULL); + + return 0; +} + +int +fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int bank, prog, key; + double pitch; + + if (ac < 4) { + fluid_ostream_printf(out, "tune: too few arguments.\n"); + return -1; + } + + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); + return -1; + } + bank = atoi(av[0]); + if ((bank < 0) || (bank >= 128)){ + fluid_ostream_printf(out, "tune: invalid bank number.\n"); + return -1; + }; + + if (!fluid_is_number(av[1])) { + fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n"); + return -1; + } + prog = atoi(av[1]); + if ((prog < 0) || (prog >= 128)){ + fluid_ostream_printf(out, "tune: invalid program number.\n"); + return -1; + }; + + if (!fluid_is_number(av[2])) { + fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n"); + return -1; + } + key = atoi(av[2]); + if ((key < 0) || (key >= 128)){ + fluid_ostream_printf(out, "tune: invalid key number.\n"); + return -1; + }; + + pitch = atof(av[3]); + if (pitch < 0.0f) { + fluid_ostream_printf(out, "tune: invalid pitch.\n"); + return -1; + }; + + fluid_synth_tune_notes(synth, bank, prog, 1, &key, &pitch, 0); + + return 0; +} + +int +fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int chan, bank, prog; + + if (ac < 3) { + fluid_ostream_printf(out, "settuning: too few arguments.\n"); + return -1; + } + + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); + return -1; + } + chan = atoi(av[0]); + if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ + fluid_ostream_printf(out, "tune: invalid channel number.\n"); + return -1; + }; + + if (!fluid_is_number(av[1])) { + fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); + return -1; + } + bank = atoi(av[1]); + if ((bank < 0) || (bank >= 128)){ + fluid_ostream_printf(out, "tuning: invalid bank number.\n"); + return -1; + }; + + if (!fluid_is_number(av[2])) { + fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); + return -1; + } + prog = atoi(av[2]); + if ((prog < 0) || (prog >= 128)){ + fluid_ostream_printf(out, "tuning: invalid program number.\n"); + return -1; + }; + + fluid_synth_select_tuning(synth, chan, bank, prog); + + return 0; +} + +int +fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int chan; + + if (ac < 1) { + fluid_ostream_printf(out, "resettuning: too few arguments.\n"); + return -1; + } + + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); + return -1; + } + chan = atoi(av[0]); + if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ + fluid_ostream_printf(out, "tune: invalid channel number.\n"); + return -1; + }; + + fluid_synth_reset_tuning(synth, chan); + + return 0; +} + +int +fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int bank, prog; + char name[256]; + int count = 0; + + fluid_synth_tuning_iteration_start(synth); + + while (fluid_synth_tuning_iteration_next(synth, &bank, &prog)) { + fluid_synth_tuning_dump(synth, bank, prog, name, 256, NULL); + fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name); + count++; + } + + if (count == 0) { + fluid_ostream_printf(out, "No tunings available\n"); + } + + return 0; +} + +int +fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + int bank, prog, i; + double pitch[128]; + char name[256]; + + if (ac < 2) { + fluid_ostream_printf(out, "dumptuning: too few arguments.\n"); + return -1; + } + + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n"); + return -1; + } + bank = atoi(av[0]); + if ((bank < 0) || (bank >= 128)){ + fluid_ostream_printf(out, "dumptuning: invalid bank number.\n"); + return -1; + }; + + if (!fluid_is_number(av[1])) { + fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n"); + return -1; + } + prog = atoi(av[1]); + if ((prog < 0) || (prog >= 128)){ + fluid_ostream_printf(out, "dumptuning: invalid program number.\n"); + return -1; + }; + + fluid_synth_tuning_dump(synth, bank, prog, name, 256, pitch); + + fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name); + + for (i = 0; i < 128; i++) { + fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]); + } + + return 0; +} + +int +fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 2) { + fluid_ostream_printf(out, "set: too few arguments.\n"); + return -1; + } + + if (fluid_is_number(av[1])) { + if (FLUID_STRCHR(av[1], '.') != NULL) { + fluid_synth_setnum(synth, av[0], atof(av[1])); + } else { + fluid_synth_setint(synth, av[0], atoi(av[1])); + } + } else { + fluid_synth_setstr(synth, av[0], av[1]); + } + + return 0; +} + +int +fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + if (ac < 1) { + fluid_ostream_printf(out, "get: too few arguments.\n"); + return -1; + } + + switch (fluid_settings_get_type(fluid_synth_get_settings(synth), av[0])) { + case FLUID_NO_TYPE: + fluid_ostream_printf(out, "get: no such settings '%s'.", av[0]); + return -1; + + case FLUID_NUM_TYPE: { + double value; + fluid_synth_getnum(synth, av[0], &value); + fluid_ostream_printf(out, "%.3f", value); + break; + } + + case FLUID_INT_TYPE: { + int value; + fluid_synth_getint(synth, av[0], &value); + fluid_ostream_printf(out, "%d", value); + break; + } + + case FLUID_STR_TYPE: { + char* s; + fluid_synth_getstr(synth, av[0], &s); + fluid_ostream_printf(out, "%s", s); + break; + } + + case FLUID_SET_TYPE: + fluid_ostream_printf(out, "%s is a node", av[0]); + break; + } + + return 0; +} + +struct _fluid_handle_settings_data_t { + int len; + fluid_synth_t* synth; + fluid_ostream_t out; +}; + +static void fluid_handle_settings_iter1(void* data, char* name, int type) +{ + struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; + + int len = FLUID_STRLEN(name); + if (len > d->len) { + d->len = len; + } +} + +static void fluid_handle_settings_iter2(void* data, char* name, int type) +{ + struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; + + int len = FLUID_STRLEN(name); + fluid_ostream_printf(d->out, "%s", name); + while (len++ < d->len) { + fluid_ostream_printf(d->out, " "); + } + fluid_ostream_printf(d->out, " "); + + switch (fluid_settings_get_type(fluid_synth_get_settings(d->synth), name)) { + case FLUID_NUM_TYPE: { + double value; + fluid_synth_getnum(d->synth, name, &value); + fluid_ostream_printf(d->out, "%.3f\n", value); + break; + } + + case FLUID_INT_TYPE: { + int value; + fluid_synth_getint(d->synth, name, &value); + fluid_ostream_printf(d->out, "%d\n", value); + break; + } + + case FLUID_STR_TYPE: { + char* s; + fluid_synth_getstr(d->synth, name, &s); + fluid_ostream_printf(d->out, "%s\n", s); + break; + } + } +} + +int +fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + struct _fluid_handle_settings_data_t data; + + data.len = 0; + data.synth = synth; + data.out = out; + + fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter1); + fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter2); + return 0; +} + + +struct _fluid_handle_option_data_t { + int first; + fluid_ostream_t out; +}; + +void fluid_handle_print_option(void* data, char* name, char* option) +{ + struct _fluid_handle_option_data_t* d = (struct _fluid_handle_option_data_t*) data; + + if (d->first) { + fluid_ostream_printf(d->out, "%s", option); + d->first = 0; + } else { + fluid_ostream_printf(d->out, ", %s", option); + } +} + +int +fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_settings_t* settings = fluid_synth_get_settings(synth); + struct _fluid_handle_option_data_t data; + + if (ac < 1) { + fluid_ostream_printf(out, "info: too few arguments.\n"); + return -1; + } + + switch (fluid_settings_get_type(settings, av[0])) { + case FLUID_NO_TYPE: + fluid_ostream_printf(out, "info: no such settings '%s'.", av[0]); + return -1; + + case FLUID_NUM_TYPE: { + double value, min, max; + fluid_settings_getnum_range(settings, av[0], &min, &max); + fluid_settings_getnum(settings, av[0], &value); + fluid_ostream_printf(out, "%s:\n", av[0]); + fluid_ostream_printf(out, "Type: number\n"); + fluid_ostream_printf(out, "Value: %.3f\n", value); + fluid_ostream_printf(out, "Minimum value: %.3f\n", min); + fluid_ostream_printf(out, "Maximum value: %.3f\n", max); + fluid_ostream_printf(out, "Default value: %.3f\n", + fluid_settings_getnum_default(settings, av[0])); + fluid_ostream_printf(out, "Real-time: %s\n", + fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); + break; + } + + case FLUID_INT_TYPE: { + int value, min, max; + fluid_settings_getint_range(settings, av[0], &min, &max); + fluid_settings_getint(settings, av[0], &value); + fluid_ostream_printf(out, "%s:\n", av[0]); + fluid_ostream_printf(out, "Type: integer\n"); + fluid_ostream_printf(out, "Value: %d\n", value); + fluid_ostream_printf(out, "Minimum value: %d\n", min); + fluid_ostream_printf(out, "Maximum value: %d\n", max); + fluid_ostream_printf(out, "Default value: %d\n", + fluid_settings_getint_default(settings, av[0])); + fluid_ostream_printf(out, "Real-time: %s\n", + fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); + break; + } + + case FLUID_STR_TYPE: { + char *s; + fluid_settings_getstr(settings, av[0], &s); + fluid_ostream_printf(out, "%s:\n", av[0]); + fluid_ostream_printf(out, "Type: string\n"); + fluid_ostream_printf(out, "Value: %s\n", s); + fluid_ostream_printf(out, "Default value: %s\n", + fluid_settings_getstr_default(settings, av[0])); + + data.out = out; + data.first = 1; + fluid_ostream_printf(out, "Options: "); + fluid_settings_foreach_option(settings, av[0], &data, fluid_handle_print_option); + fluid_ostream_printf(out, "\n"); + + fluid_ostream_printf(out, "Real-time: %s\n", + fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); + break; + } + + case FLUID_SET_TYPE: + fluid_ostream_printf(out, "%s is a node", av[0]); + break; + } + + return 0; +} + +int +fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_synth_system_reset(synth); + return 0; +} + +int +fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_ostream_printf(out, "cheers!\n"); + return -2; +} + +int +fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + /* Purpose: + * Prints the help text for the command line commands. + * Can be used as follows: + * - help + * - help (topic), where (topic) is 'general', 'chorus', etc. + * - help all + * - help help + */ + + char* topic = "general"; /* default, if no topic is given */ + int count = 0; + int i; + + fluid_ostream_printf(out, "\n"); + /* 1st argument (optional): help topic */ + if (ac >= 1) { + topic = av[0]; + } + if (strcmp(topic,"help") == 0){ + /* "help help": Print a list of all topics */ + fluid_ostream_printf(out, + "*** Help topics:***\n" + "help help (prints this list)\n" + "help all (prints all topics)\n"); + for (i = 0; fluid_commands[i].name != NULL; i++) { + int listed_first_time = 1; + int ii; + for (ii = 0; ii < i; ii++){ + if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){ + listed_first_time = 0; + }; /* if topic has already been listed */ + }; /* for all topics (inner loop) */ + if (listed_first_time){ + fluid_ostream_printf(out, "help %s\n",fluid_commands[i].topic); + }; + }; /* for all topics (outer loop) */ + } else { + /* help (arbitrary topic or "all") */ + for (i = 0; fluid_commands[i].name != NULL; i++) { + fluid_cmd_t cmd = fluid_commands[i]; + if (cmd.help != NULL) { + if (strcmp(topic,"all") == 0 || strcmp(topic,cmd.topic) == 0){ + fluid_ostream_printf(out, "%s\n", fluid_commands[i].help); + count++; + }; /* if it matches the topic */ + }; /* if help text exists */ + }; /* foreach command */ + if (count == 0){ + fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n"); + }; + }; + return 0; +} + +int +fluid_is_number(char* a) +{ + while (*a != 0) { + if (((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) { + return 0; + } + a++; + } + return 1; +} + +int +fluid_is_empty(char* a) +{ + while (*a != 0) { + if ((*a != ' ') && (*a != '\t') && (*a != '\n') && (*a != '\r')) { + return 0; + } + a++; + } + return 1; +} + +char* +fluid_expand_path(char* path, char* new_path, int len) +{ +#if defined(WIN32) || defined(MACOS9) + snprintf(new_path, len - 1, "%s", path); +#else + if ((path[0] == '~') && (path[1] == '/')) { + char* home = getenv("HOME"); + if (home == NULL) { + snprintf(new_path, len - 1, "%s", path); + } else { + snprintf(new_path, len - 1, "%s%s", home, &path[1]); + } + } else { + snprintf(new_path, len - 1, "%s", path); + } +#endif + + new_path[len - 1] = 0; + return new_path; +} + + + +/* + * Command + */ + +fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd) +{ + fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t); + if (copy == NULL) { + FLUID_LOG (FLUID_PANIC, "Out of memory"); + return NULL; + } + + copy->name = FLUID_STRDUP(cmd->name); + copy->topic = FLUID_STRDUP(cmd->topic); + copy->help = FLUID_STRDUP(cmd->help); + copy->handler = cmd->handler; + copy->data = cmd->data; + return copy; +} + +void delete_fluid_cmd(fluid_cmd_t* cmd) +{ + if (cmd->name) { + FLUID_FREE(cmd->name); + } + if (cmd->topic) { + FLUID_FREE(cmd->topic); + } + if (cmd->help) { + FLUID_FREE(cmd->help); + } + FLUID_FREE(cmd); +} + +/* + * Command handler + */ + +void fluid_cmd_handler_delete(void* value, int type) +{ + delete_fluid_cmd((fluid_cmd_t*) value); +} + +fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth) +{ + int i; + fluid_cmd_handler_t* handler; + + fluid_cmd_t source = { + "source", "general", (fluid_cmd_func_t) fluid_handle_source, NULL, + "source filename Load a file and parse every line as a command" + }; + + handler = new_fluid_hashtable(fluid_cmd_handler_delete); + if (handler == NULL) { + return NULL; + } + + if (synth != NULL) { + for (i = 0; fluid_commands[i].name != NULL; i++) { + fluid_commands[i].data = synth; + fluid_cmd_handler_register(handler, &fluid_commands[i]); + fluid_commands[i].data = NULL; + } + } + + source.data = handler; + fluid_cmd_handler_register(handler, &source); + + return handler; +} + +void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler) +{ + delete_fluid_hashtable(handler); +} + +int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd) +{ + fluid_cmd_t* copy = fluid_cmd_copy(cmd); + fluid_hashtable_insert(handler, copy->name, copy, 0); + return 0; +} + +int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, char* cmd) +{ + return fluid_hashtable_remove(handler, cmd); +} + +int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) +{ + void *vp; /* use a void pointer to avoid GCC "type-punned pointer" warning */ + fluid_cmd_t* cmd; + + if (fluid_hashtable_lookup(handler, av[0], &vp, NULL) + && ((fluid_cmd_t *)vp)->handler) { + cmd = vp; + return (*cmd->handler)(cmd->data, ac - 1, av + 1, out); + } else { + fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); + return -1; + } +} + + +#if !defined(WITHOUT_SERVER) + + + +struct _fluid_server_t { + fluid_server_socket_t* socket; + fluid_settings_t* settings; + fluid_server_newclient_func_t newclient; + void* data; + fluid_list_t* clients; + fluid_mutex_t mutex; +}; + +static void fluid_server_handle_connection(fluid_server_t* server, + fluid_socket_t client_socket, + char* addr); +static void fluid_server_close(fluid_server_t* server); + +fluid_server_t* +new_fluid_server(fluid_settings_t* settings, + fluid_server_newclient_func_t newclient, + void* data) +{ + fluid_server_t* server; + int port; + + server = FLUID_NEW(fluid_server_t); + if (server == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + server->settings = settings; + server->clients = NULL; + server->newclient = newclient; + server->data = data; + + fluid_mutex_init(server->mutex); + + fluid_settings_getint(settings, "shell.port", &port); + + server->socket = new_fluid_server_socket(port, + (fluid_server_func_t) fluid_server_handle_connection, + server); + if (server->socket == NULL) { + FLUID_FREE(server); + return NULL; + } + + return server; +} + +void delete_fluid_server(fluid_server_t* server) +{ + if (server == NULL) { + return; + } + + fluid_server_close(server); + + FLUID_FREE(server); +} + +static void fluid_server_close(fluid_server_t* server) +{ + fluid_list_t* list; + fluid_list_t* clients; + fluid_client_t* client; + + if (server == NULL) { + return; + } + + fluid_mutex_lock(server->mutex); + clients = server->clients; + server->clients = NULL; + fluid_mutex_unlock(server->mutex); + + list = clients; + + while (list) { + client = fluid_list_get(list); + fluid_client_quit(client); + list = fluid_list_next(list); + } + + delete_fluid_list(clients); + + if (server->socket) { + delete_fluid_server_socket(server->socket); + server->socket = NULL; + } +} + +static void +fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr) +{ + fluid_client_t* client; + fluid_cmd_handler_t* handler; + + handler = server->newclient(server->data, addr); + if (handler == NULL) { + return; + } + + client = new_fluid_client(server, server->settings, handler, client_socket); + if (client == NULL) { + return; + } + fluid_server_add_client(server, client); +} + +void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client) +{ + fluid_mutex_lock(server->mutex); + server->clients = fluid_list_append(server->clients, client); + fluid_mutex_unlock(server->mutex); +} + +void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client) +{ + fluid_mutex_lock(server->mutex); + server->clients = fluid_list_remove(server->clients, client); + fluid_mutex_unlock(server->mutex); +} + +int fluid_server_join(fluid_server_t* server) +{ + return fluid_server_socket_join(server->socket); +} + + + + +struct _fluid_client_t { + fluid_server_t* server; + fluid_settings_t* settings; + fluid_cmd_handler_t* handler; + fluid_socket_t socket; + fluid_thread_t* thread; +}; + + + +static void fluid_client_run(fluid_client_t* client) +{ + fluid_shell_t shell; + fluid_shell_init(&shell, + client->settings, + client->handler, + fluid_socket_get_istream(client->socket), + fluid_socket_get_ostream(client->socket)); + fluid_shell_run(&shell); + fluid_server_remove_client(client->server, client); + delete_fluid_client(client); +} + + +fluid_client_t* +new_fluid_client(fluid_server_t* server, fluid_settings_t* settings, + fluid_cmd_handler_t* handler, fluid_socket_t sock) +{ + fluid_client_t* client; + + client = FLUID_NEW(fluid_client_t); + if (client == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + client->server = server; + client->socket = sock; + client->settings = settings; + client->handler = handler; + + client->thread = new_fluid_thread((fluid_thread_func_t) fluid_client_run, client, 0); + + if (client->thread == NULL) { + fluid_socket_close(sock); + FLUID_FREE(client); + return NULL; + } + + return client; +} + +void fluid_client_quit(fluid_client_t* client) +{ + if (client->socket != INVALID_SOCKET) { + fluid_socket_close(client->socket); + client->socket = INVALID_SOCKET; + } + FLUID_LOG(FLUID_DBG, "fluid_client_quit: joining"); + fluid_thread_join(client->thread); + FLUID_LOG(FLUID_DBG, "fluid_client_quit: done"); +} + +void delete_fluid_client(fluid_client_t* client) +{ + if (client->socket != INVALID_SOCKET) { + fluid_socket_close(client->socket); + client->socket = INVALID_SOCKET; + } + if (client->thread != NULL) { + delete_fluid_thread(client->thread); + client->thread = NULL; + } + FLUID_FREE(client); +} + + +#endif /* WITHOUT_SERVER */ diff --git a/src/libs/fluidsynth/src/fluid_cmd.h b/src/libs/fluidsynth/src/fluid_cmd.h new file mode 100644 index 0000000000..8734053458 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_cmd.h @@ -0,0 +1,102 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_CMD_H +#define _FLUID_CMD_H + +#include "fluidsynth_priv.h" +#include "fluid_io.h" + +void fluid_shell_settings(fluid_settings_t* settings); + + +/** some help functions */ +int fluid_is_number(char* a); +int fluid_is_empty(char* a); +char* fluid_expand_path(char* path, char* new_path, int len); + +/** the handlers for the command lines */ +int fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); +int fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); + +int fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + + +fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd); +void delete_fluid_cmd(fluid_cmd_t* cmd); + +int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, + int ac, char** av, + fluid_ostream_t out); + + + +void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client); +void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client); + + +fluid_client_t* new_fluid_client(fluid_server_t* server, + fluid_settings_t* settings, + fluid_cmd_handler_t* handler, + fluid_socket_t sock); + +void delete_fluid_client(fluid_client_t* client); +void fluid_client_quit(fluid_client_t* client); + + +#endif /* _FLUID_CMD_H */ diff --git a/src/libs/fluidsynth/src/fluid_conv.c b/src/libs/fluidsynth/src/fluid_conv.c new file mode 100644 index 0000000000..57dc47c566 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_conv.c @@ -0,0 +1,320 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_conv.h" + + +/* conversion tables */ +fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; +fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; +fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE]; +fluid_real_t fluid_posbp_tab[128]; +fluid_real_t fluid_concave_tab[128]; +fluid_real_t fluid_convex_tab[128]; +fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +void +fluid_conversion_config(void) +{ + int i; + double x; + + for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) { + fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0); + } + + /* centibels to amplitude conversion + * Note: SF2.01 section 8.1.3: Initial attenuation range is + * between 0 and 144 dB. Therefore a negative attenuation is + * not allowed. + */ + for (i = 0; i < FLUID_CB_AMP_SIZE; i++) { + fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0); + } + + /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont + * specification in regards to volume attenuation. The below calculation + * is an approx. equation for generating a table equivelant to the + * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told + * was generated from device testing. By the spec this should be centibels. + */ + for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) { + fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR); + } + + /* initialize the conversion tables (see fluid_mod.c + fluid_mod_get_value cases 4 and 8) */ + + /* concave unipolar positive transform curve */ + fluid_concave_tab[0] = 0.0; + fluid_concave_tab[127] = 1.0; + + /* convex unipolar positive transform curve */ + fluid_convex_tab[0] = 0; + fluid_convex_tab[127] = 1.0; + x = log10(128.0 / 127.0); + + /* There seems to be an error in the specs. The equations are + implemented according to the pictures on SF2.01 page 73. */ + + for (i = 1; i < 127; i++) { + x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0); + fluid_convex_tab[i] = (fluid_real_t) (1.0 - x); + fluid_concave_tab[127 - i] = (fluid_real_t) x; + } + + /* initialize the pan conversion table */ + x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0); + for (i = 0; i < FLUID_PAN_SIZE; i++) { + fluid_pan_tab[i] = (fluid_real_t) sin(i * x); + } +} + +/* + * fluid_ct2hz + */ +fluid_real_t +fluid_ct2hz_real(fluid_real_t cents) +{ + if (cents < 0) + return (fluid_real_t) 1.0; + else if (cents < 900) { + return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)]; + } else if (cents < 2100) { + return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)]; + } else if (cents < 3300) { + return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)]; + } else if (cents < 4500) { + return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)]; + } else if (cents < 5700) { + return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)]; + } else if (cents < 6900) { + return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)]; + } else if (cents < 8100) { + return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)]; + } else if (cents < 9300) { + return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)]; + } else if (cents < 10500) { + return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)]; + } else if (cents < 11700) { + return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)]; + } else if (cents < 12900) { + return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)]; + } else if (cents < 14100) { + return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)]; + } else { + return (fluid_real_t) 1.0; /* some loony trying to make you deaf */ + } +} + +/* + * fluid_ct2hz + */ +fluid_real_t +fluid_ct2hz(fluid_real_t cents) +{ + /* Filter fc limit: SF2.01 page 48 # 8 */ + if (cents >= 13500){ + cents = 13500; /* 20 kHz */ + } else if (cents < 1500){ + cents = 1500; /* 20 Hz */ + } + return fluid_ct2hz_real(cents); +} + +/* + * fluid_cb2amp + * + * in: a value between 0 and 960, 0 is no attenuation + * out: a value between 1 and 0 + */ +fluid_real_t +fluid_cb2amp(fluid_real_t cb) +{ + /* + * cb: an attenuation in 'centibels' (1/10 dB) + * SF2.01 page 49 # 48 limits it to 144 dB. + * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit. + */ + + /* minimum attenuation: 0 dB */ + if (cb < 0) { + return 1.0; + } + if (cb >= FLUID_CB_AMP_SIZE) { + return 0.0; + } + return fluid_cb2amp_tab[(int) cb]; +} + +/* + * fluid_atten2amp + * + * in: a value between 0 and 1440, 0 is no attenuation + * out: a value between 1 and 0 + * + * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't + * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp. + */ +fluid_real_t +fluid_atten2amp(fluid_real_t atten) +{ + if (atten < 0) return 1.0; + else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0; + else return fluid_atten2amp_tab[(int) atten]; +} + +/* + * fluid_tc2sec_delay + */ +fluid_real_t +fluid_tc2sec_delay(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 21, 23, 25, 33 + * SF2.01 section 8.1.3 items 21, 23, 25, 33 + * + * The most negative number indicates a delay of 0. Range is limited + * from -12000 to 5000 */ + if (tc <= -32768.0f) { + return (fluid_real_t) 0.0f; + }; + if (tc < -12000.) { + tc = (fluid_real_t) -12000.0f; + } + if (tc > 5000.0f) { + tc = (fluid_real_t) 5000.0f; + } + return (fluid_real_t) pow(2.0, (double) tc / 1200.0); +} + +/* + * fluid_tc2sec_attack + */ +fluid_real_t +fluid_tc2sec_attack(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 26, 34 + * SF2.01 section 8.1.3 items 26, 34 + * The most negative number indicates a delay of 0 + * Range is limited from -12000 to 8000 */ + if (tc<=-32768.){return (fluid_real_t) 0.0;}; + if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; + if (tc>8000.){tc=(fluid_real_t) 8000.0;}; + return (fluid_real_t) pow(2.0, (double) tc / 1200.0); +} + +/* + * fluid_tc2sec + */ +fluid_real_t +fluid_tc2sec(fluid_real_t tc) +{ + /* No range checking here! */ + return (fluid_real_t) pow(2.0, (double) tc / 1200.0); +} + +/* + * fluid_tc2sec_release + */ +fluid_real_t +fluid_tc2sec_release(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 30, 38 + * SF2.01 section 8.1.3 items 30, 38 + * No 'most negative number' rule here! + * Range is limited from -12000 to 8000 */ + if (tc<=-32768.){return (fluid_real_t) 0.0;}; + if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; + if (tc>8000.){tc=(fluid_real_t) 8000.0;}; + return (fluid_real_t) pow(2.0, (double) tc / 1200.0); +} + +/* + * fluid_act2hz + * + * Convert from absolute cents to Hertz + */ +fluid_real_t +fluid_act2hz(fluid_real_t c) +{ + return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0)); +} + +/* + * fluid_hz2ct + * + * Convert from Hertz to cents + */ +fluid_real_t +fluid_hz2ct(fluid_real_t f) +{ + return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0)); +} + +/* + * fluid_pan + */ +fluid_real_t +fluid_pan(fluid_real_t c, int left) +{ + if (left) { + c = -c; + } + if (c < -500) { + return (fluid_real_t) 0.0; + } else if (c > 500) { + return (fluid_real_t) 1.0; + } else { + return fluid_pan_tab[(int) (c + 500)]; + } +} + +/* + * fluid_concave + */ +fluid_real_t +fluid_concave(fluid_real_t val) +{ + if (val < 0) { + return 0; + } else if (val > 127) { + return 1; + } + return fluid_concave_tab[(int) val]; +} + +/* + * fluid_convex + */ +fluid_real_t +fluid_convex(fluid_real_t val) +{ + if (val < 0) { + return 0; + } else if (val > 127) { + return 1; + } + return fluid_convex_tab[(int) val]; +} diff --git a/src/libs/fluidsynth/src/fluid_conv.h b/src/libs/fluidsynth/src/fluid_conv.h new file mode 100644 index 0000000000..7044046151 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_conv.h @@ -0,0 +1,62 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_CONV_H +#define _FLUID_CONV_H + +#include "fluidsynth_priv.h" + +#define FLUID_CENTS_HZ_SIZE 1200 +#define FLUID_VEL_CB_SIZE 128 +#define FLUID_CB_AMP_SIZE 961 +#define FLUID_ATTEN_AMP_SIZE 1441 +#define FLUID_PAN_SIZE 1002 + +/* EMU 8k/10k don't follow spec in regards to volume attenuation. + * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR). + * By the standard this should be -200.0. */ +#define FLUID_ATTEN_POWER_FACTOR (-531.509) + +void fluid_conversion_config(void); + +fluid_real_t fluid_ct2hz_real(fluid_real_t cents); +fluid_real_t fluid_ct2hz(fluid_real_t cents); +fluid_real_t fluid_cb2amp(fluid_real_t cb); +fluid_real_t fluid_atten2amp(fluid_real_t atten); +fluid_real_t fluid_tc2sec(fluid_real_t tc); +fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); +fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); +fluid_real_t fluid_tc2sec_release(fluid_real_t tc); +fluid_real_t fluid_act2hz(fluid_real_t c); +fluid_real_t fluid_hz2ct(fluid_real_t c); +fluid_real_t fluid_pan(fluid_real_t c, int left); +fluid_real_t fluid_concave(fluid_real_t val); +fluid_real_t fluid_convex(fluid_real_t val); + +extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; +extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE]; +extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; +extern fluid_real_t fluid_posbp_tab[128]; +extern fluid_real_t fluid_concave_tab[128]; +extern fluid_real_t fluid_convex_tab[128]; +extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; + + +#endif /* _FLUID_CONV_H */ diff --git a/src/libs/fluidsynth/src/fluid_defsfont.c b/src/libs/fluidsynth/src/fluid_defsfont.c new file mode 100644 index 0000000000..6a042a2405 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_defsfont.c @@ -0,0 +1,3185 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_defsfont.h" +/* Todo: Get rid of that 'include' */ +#include "fluid_sys.h" + +/*************************************************************** + * + * SFONT LOADER + */ + +fluid_sfloader_t* new_fluid_defsfloader() +{ + fluid_sfloader_t* loader; + + loader = FLUID_NEW(fluid_sfloader_t); + if (loader == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + loader->data = NULL; + loader->free = delete_fluid_defsfloader; + loader->load = fluid_defsfloader_load; + + return loader; +} + +int delete_fluid_defsfloader(fluid_sfloader_t* loader) +{ + if (loader) { + FLUID_FREE(loader); + } + return FLUID_OK; +} + +fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename) +{ + fluid_defsfont_t* defsfont; + fluid_sfont_t* sfont; + + defsfont = new_fluid_defsfont(); + + if (defsfont == NULL) { + return NULL; + } + + if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) { + delete_fluid_defsfont(defsfont); + return NULL; + } + + sfont = FLUID_NEW(fluid_sfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->data = defsfont; + sfont->free = fluid_defsfont_sfont_delete; + sfont->get_name = fluid_defsfont_sfont_get_name; + sfont->get_preset = fluid_defsfont_sfont_get_preset; + sfont->iteration_start = fluid_defsfont_sfont_iteration_start; + sfont->iteration_next = fluid_defsfont_sfont_iteration_next; + + return sfont; +} + + + +/*************************************************************** + * + * PUBLIC INTERFACE + */ + +int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont) +{ + if (delete_fluid_defsfont(sfont->data) != 0) { + return -1; + } + FLUID_FREE(sfont); + return 0; +} + +char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont) +{ + return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data); +} + +fluid_preset_t* +fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) +{ + fluid_preset_t* preset; + fluid_defpreset_t* defpreset; + + defpreset = fluid_defsfont_get_preset((fluid_defsfont_t*) sfont->data, bank, prenum); + + if (defpreset == NULL) { + return NULL; + } + + preset = FLUID_NEW(fluid_preset_t); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + preset->sfont = sfont; + preset->data = defpreset; + preset->free = fluid_defpreset_preset_delete; + preset->get_name = fluid_defpreset_preset_get_name; + preset->get_banknum = fluid_defpreset_preset_get_banknum; + preset->get_num = fluid_defpreset_preset_get_num; + preset->noteon = fluid_defpreset_preset_noteon; + preset->notify = NULL; + + return preset; +} + +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont) +{ + fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data); +} + +int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) +{ + preset->free = fluid_defpreset_preset_delete; + preset->get_name = fluid_defpreset_preset_get_name; + preset->get_banknum = fluid_defpreset_preset_get_banknum; + preset->get_num = fluid_defpreset_preset_get_num; + preset->noteon = fluid_defpreset_preset_noteon; + preset->notify = NULL; + + return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset); +} + +int fluid_defpreset_preset_delete(fluid_preset_t* preset) +{ + FLUID_FREE(preset); +/* printf("TODO: free modulators\n"); */ + return 0; +} + +char* fluid_defpreset_preset_get_name(fluid_preset_t* preset) +{ + return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data); +} + +int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset) +{ + return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data); +} + +int fluid_defpreset_preset_get_num(fluid_preset_t* preset) +{ + return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data); +} + +int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, + int chan, int key, int vel) +{ + return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel); +} + + + + +/*************************************************************** + * + * SFONT + */ + +/* + * new_fluid_defsfont + */ +fluid_defsfont_t* new_fluid_defsfont() +{ + fluid_defsfont_t* sfont; + + sfont = FLUID_NEW(fluid_defsfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->filename = NULL; + sfont->samplepos = 0; + sfont->samplesize = 0; + sfont->sample = NULL; + sfont->sampledata = NULL; + sfont->preset = NULL; + + return sfont; +} + +/* + * delete_fluid_defsfont + */ +int delete_fluid_defsfont(fluid_defsfont_t* sfont) +{ + fluid_list_t *list; + fluid_defpreset_t* preset; + fluid_sample_t* sample; + + /* Check that no samples are currently used */ + for (list = sfont->sample; list; list = fluid_list_next(list)) { + sample = (fluid_sample_t*) fluid_list_get(list); + if (fluid_sample_refcount(sample) != 0) { + return -1; + } + } + + if (sfont->filename != NULL) { + FLUID_FREE(sfont->filename); + } + + for (list = sfont->sample; list; list = fluid_list_next(list)) { + delete_fluid_sample((fluid_sample_t*) fluid_list_get(list)); + } + + if (sfont->sample) { + delete_fluid_list(sfont->sample); + } + + if (sfont->sampledata != NULL) { + fluid_munlock(sfont->sampledata, sfont->samplesize); + FLUID_FREE(sfont->sampledata); + } + + preset = sfont->preset; + while (preset != NULL) { + sfont->preset = preset->next; + delete_fluid_defpreset(preset); + preset = sfont->preset; + } + + FLUID_FREE(sfont); + return FLUID_OK; +} + +/* + * fluid_defsfont_get_name + */ +char* fluid_defsfont_get_name(fluid_defsfont_t* sfont) +{ + return sfont->filename; +} + + +/* + * fluid_defsfont_load + */ +int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file) +{ + SFData* sfdata; + fluid_list_t *p; + SFPreset* sfpreset; + SFSample* sfsample; + fluid_sample_t* sample; + fluid_defpreset_t* preset; + + sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file)); + if (sfont->filename == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + FLUID_STRCPY(sfont->filename, file); + + /* The actual loading is done in the sfont and sffile files */ + sfdata = sfload_file(file); + if (sfdata == NULL) { + FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file"); + return FLUID_FAILED; + } + + /* Keep track of the position and size of the sample data because + it's loaded separately (and might be unoaded/reloaded in future) */ + sfont->samplepos = sfdata->samplepos; + sfont->samplesize = sfdata->samplesize; + + /* load sample data in one block */ + if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK) { + return FLUID_FAILED; + } + + /* Create all the sample headers */ + p = sfdata->sample; + while (p != NULL) { + sfsample = (SFSample *) p->data; + sample = new_fluid_sample(); + if (sample == NULL) { + return FLUID_FAILED; + } + if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK) { + return FLUID_FAILED; + } + fluid_defsfont_add_sample(sfont, sample); + fluid_voice_optimize_sample(sample); + p = fluid_list_next(p); + } + + /* Load all the presets */ + p = sfdata->preset; + while (p != NULL) { + sfpreset = (SFPreset *) p->data; + preset = new_fluid_defpreset(sfont); + if (preset == NULL) { + return FLUID_FAILED; + } + if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK) { + return FLUID_FAILED; + } + fluid_defsfont_add_preset(sfont, preset); + p = fluid_list_next(p); + } + sfont_free_data(sfdata); + + return FLUID_OK; +} + +/* fluid_defsfont_add_sample + * + * Add a sample to the SoundFont + */ +int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample) +{ + sfont->sample = fluid_list_append(sfont->sample, sample); + return FLUID_OK; +} + +/* fluid_defsfont_add_preset + * + * Add a preset to the SoundFont + */ +int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset) +{ + fluid_defpreset_t *cur, *prev; + if (sfont->preset == NULL) { + preset->next = NULL; + sfont->preset = preset; + } else { + /* sort them as we go along. very basic sorting trick. */ + cur = sfont->preset; + prev = NULL; + while (cur != NULL) { + if ((preset->bank < cur->bank) + || ((preset->bank == cur->bank) && (preset->num < cur->num))) { + if (prev == NULL) { + preset->next = cur; + sfont->preset = preset; + } else { + preset->next = cur; + prev->next = preset; + } + return FLUID_OK; + } + prev = cur; + cur = cur->next; + } + preset->next = NULL; + prev->next = preset; + } + return FLUID_OK; +} + +/* + * fluid_defsfont_load_sampledata + */ +int +fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont) +{ + fluid_file fd; + unsigned short endian; + fd = FLUID_FOPEN(sfont->filename, "rb"); + if (fd == NULL) { + FLUID_LOG(FLUID_ERR, "Can't open soundfont file"); + return FLUID_FAILED; + } + if (FLUID_FSEEK(fd, sfont->samplepos, SEEK_SET) == -1) { + perror("error"); + FLUID_LOG(FLUID_ERR, "Failed to seek position in data file"); + return FLUID_FAILED; + } + sfont->sampledata = (short*) FLUID_MALLOC(sfont->samplesize); + if (sfont->sampledata == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + if (FLUID_FREAD(sfont->sampledata, 1, sfont->samplesize, fd) < sfont->samplesize) { + FLUID_LOG(FLUID_ERR, "Failed to read sample data"); + return FLUID_FAILED; + } + FLUID_FCLOSE(fd); + + /* Lock the memory to disable paging. It's okay if this fails. It + probably means that the user doesn't have to required permission. */ + if (fluid_mlock(sfont->sampledata, sfont->samplesize) != 0) { + FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); + } + + + /* I'm not sure this endian test is waterproof... */ + endian = 0x0100; + + /* If this machine is big endian, the sample have to byte swapped */ + if (((char *) &endian)[0]) { + unsigned char* cbuf; + unsigned char hi, lo; + unsigned int i, j; + short s; + cbuf = (unsigned char*) sfont->sampledata; + for (i = 0, j = 0; j < sfont->samplesize; i++) { + lo = cbuf[j++]; + hi = cbuf[j++]; + s = (hi << 8) | lo; + sfont->sampledata[i] = s; + } + } + return FLUID_OK; +} + +/* + * fluid_defsfont_get_sample + */ +fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s) +{ + fluid_list_t* list; + fluid_sample_t* sample; + + for (list = sfont->sample; list; list = fluid_list_next(list)) { + + sample = (fluid_sample_t*) fluid_list_get(list); + + if (FLUID_STRCMP(sample->name, s) == 0) { + return sample; + } + } + + return NULL; +} + +/* + * fluid_defsfont_get_preset + */ +fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num) +{ + fluid_defpreset_t* preset = sfont->preset; + while (preset != NULL) { + if ((preset->bank == bank) && ((preset->num == num))) { + return preset; + } + preset = preset->next; + } + return NULL; +} + +/* + * fluid_defsfont_iteration_start + */ +void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont) +{ + sfont->iter_cur = sfont->preset; +} + +/* + * fluid_defsfont_iteration_next + */ +int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset) +{ + if (sfont->iter_cur == NULL) { + return 0; + } + + preset->data = (void*) sfont->iter_cur; + sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur); + return 1; +} + +/*************************************************************** + * + * PRESET + */ + +/* + * new_fluid_defpreset + */ +fluid_defpreset_t* +new_fluid_defpreset(fluid_defsfont_t* sfont) +{ + fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + preset->next = NULL; + preset->sfont = sfont; + preset->name[0] = 0; + preset->bank = 0; + preset->num = 0; + preset->global_zone = NULL; + preset->zone = NULL; + return preset; +} + +/* + * delete_fluid_defpreset + */ +int +delete_fluid_defpreset(fluid_defpreset_t* preset) +{ + int err = FLUID_OK; + fluid_preset_zone_t* zone; + if (preset->global_zone != NULL) { + if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) { + err = FLUID_FAILED; + } + preset->global_zone = NULL; + } + zone = preset->zone; + while (zone != NULL) { + preset->zone = zone->next; + if (delete_fluid_preset_zone(zone) != FLUID_OK) { + err = FLUID_FAILED; + } + zone = preset->zone; + } + FLUID_FREE(preset); + return err; +} + +int +fluid_defpreset_get_banknum(fluid_defpreset_t* preset) +{ + return preset->bank; +} + +int +fluid_defpreset_get_num(fluid_defpreset_t* preset) +{ + return preset->num; +} + +char* +fluid_defpreset_get_name(fluid_defpreset_t* preset) +{ + return preset->name; +} + +/* + * fluid_defpreset_next + */ +fluid_defpreset_t* +fluid_defpreset_next(fluid_defpreset_t* preset) +{ + return preset->next; +} + + +/* + * fluid_defpreset_noteon + */ +int +fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +{ + fluid_preset_zone_t *preset_zone, *global_preset_zone; + fluid_inst_t* inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone, *z; + fluid_sample_t* sample; + fluid_voice_t* voice; + fluid_mod_t * mod; + fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ + int mod_list_count; + int i; + + global_preset_zone = fluid_defpreset_get_global_zone(preset); + + /* run thru all the zones of this preset */ + preset_zone = fluid_defpreset_get_zone(preset); + while (preset_zone != NULL) { + + /* check if the note falls into the key and velocity range of this + preset */ + if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { + + inst = fluid_preset_zone_get_inst(preset_zone); + global_inst_zone = fluid_inst_get_global_zone(inst); + + /* run thru all the zones of this instrument */ + inst_zone = fluid_inst_get_zone(inst); + while (inst_zone != NULL) { + + /* make sure this instrument zone has a valid sample */ + sample = fluid_inst_zone_get_sample(inst_zone); + if (fluid_sample_in_rom(sample) || (sample == NULL)) { + inst_zone = fluid_inst_zone_next(inst_zone); + continue; + } + + /* check if the note falls into the key and velocity range of this + instrument */ + + if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { + + /* this is a good zone. allocate a new synthesis process and + initialize it */ + + voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + if (voice == NULL) { + return FLUID_FAILED; + } + + + z = inst_zone; + + /* Instrument level, generators */ + + for (i = 0; i < GEN_LAST; i++) { + + /* SF 2.01 section 9.4 'bullet' 4: + * + * A generator in a local instrument zone supersedes a + * global instrument zone generator. Both cases supersede + * the default generator -> voice_gen_set */ + + if (inst_zone->gen[i].flags){ + fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); + + } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) { + fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); + + } else { + /* The generator has not been defined in this instrument. + * Do nothing, leave it at the default. + */ + } + + } /* for all generators */ + + /* global instrument zone, modulators: Put them all into a + * list. */ + + mod_list_count = 0; + + if (global_inst_zone){ + mod = global_inst_zone->mod; + while (mod){ + mod_list[mod_list_count++] = mod; + mod = mod->next; + } + } + + /* local instrument zone, modulators. + * Replace modulators with the same definition in the list: + * SF 2.01 page 69, 'bullet' 8 + */ + mod = inst_zone->mod; + + while (mod){ + + /* 'Identical' modulators will be deleted by setting their + * list entry to NULL. The list length is known, NULL + * entries will be ignored later. SF2.01 section 9.5.1 + * page 69, 'bullet' 3 defines 'identical'. */ + + for (i = 0; i < mod_list_count; i++){ + if (fluid_mod_test_identity(mod,mod_list[i])){ + mod_list[i] = NULL; + } + } + + /* Finally add the new modulator to to the list. */ + mod_list[mod_list_count++] = mod; + mod = mod->next; + } + + /* Add instrument modulators (global / local) to the voice. */ + for (i = 0; i < mod_list_count; i++){ + + mod = mod_list[i]; + + if (mod != NULL){ /* disabled modulators CANNOT be skipped. */ + + /* Instrument modulators -supersede- existing (default) + * modulators. SF 2.01 page 69, 'bullet' 6 */ + fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); + } + } + + /* Preset level, generators */ + + for (i = 0; i < GEN_LAST; i++) { + + /* SF 2.01 section 8.5 page 58: If some generators are + * encountered at preset level, they should be ignored */ + if ((i != GEN_STARTADDROFS) + && (i != GEN_ENDADDROFS) + && (i != GEN_STARTLOOPADDROFS) + && (i != GEN_ENDLOOPADDROFS) + && (i != GEN_STARTADDRCOARSEOFS) + && (i != GEN_ENDADDRCOARSEOFS) + && (i != GEN_STARTLOOPADDRCOARSEOFS) + && (i != GEN_KEYNUM) + && (i != GEN_VELOCITY) + && (i != GEN_ENDLOOPADDRCOARSEOFS) + && (i != GEN_SAMPLEMODE) + && (i != GEN_EXCLUSIVECLASS) + && (i != GEN_OVERRIDEROOTKEY)) { + + /* SF 2.01 section 9.4 'bullet' 9: A generator in a + * local preset zone supersedes a global preset zone + * generator. The effect is -added- to the destination + * summing node -> voice_gen_incr */ + + if (preset_zone->gen[i].flags) { + fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); + } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) { + fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); + } else { + /* The generator has not been defined in this preset + * Do nothing, leave it unchanged. + */ + } + } /* if available at preset level */ + } /* for all generators */ + + + /* Global preset zone, modulators: put them all into a + * list. */ + mod_list_count = 0; + if (global_preset_zone){ + mod = global_preset_zone->mod; + while (mod){ + mod_list[mod_list_count++] = mod; + mod = mod->next; + } + } + + /* Process the modulators of the local preset zone. Kick + * out all identical modulators from the global preset zone + * (SF 2.01 page 69, second-last bullet) */ + + mod = preset_zone->mod; + while (mod){ + for (i = 0; i < mod_list_count; i++){ + if (fluid_mod_test_identity(mod,mod_list[i])){ + mod_list[i] = NULL; + } + } + + /* Finally add the new modulator to the list. */ + mod_list[mod_list_count++] = mod; + mod = mod->next; + } + + /* Add preset modulators (global / local) to the voice. */ + for (i = 0; i < mod_list_count; i++){ + mod = mod_list[i]; + if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */ + + /* Preset modulators -add- to existing instrument / + * default modulators. SF2.01 page 70 first bullet on + * page */ + fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); + } + } + + /* add the synthesis process to the synthesis loop. */ + fluid_synth_start_voice(synth, voice); + + /* Store the ID of the first voice that was created by this noteon event. + * Exclusive class may only terminate older voices. + * That avoids killing voices, which have just been created. + * (a noteon event can create several voice processes with the same exclusive + * class - for example when using stereo samples) + */ + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + } + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_set_global_zone + */ +int +fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) +{ + preset->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_defpreset_import_sfont + */ +int +fluid_defpreset_import_sfont(fluid_defpreset_t* preset, + SFPreset* sfpreset, + fluid_defsfont_t* sfont) +{ + fluid_list_t *p; + SFZone* sfzone; + fluid_preset_zone_t* zone; + int count; + char zone_name[256]; + if ((sfpreset->name != NULL) && (FLUID_STRLEN(sfpreset->name) > 0)) { + FLUID_STRCPY(preset->name, sfpreset->name); + } else { + FLUID_SPRINTF(preset->name, "Bank%d,Preset%d", sfpreset->bank, sfpreset->prenum); + } + preset->bank = sfpreset->bank; + preset->num = sfpreset->prenum; + p = sfpreset->zone; + count = 0; + while (p != NULL) { + sfzone = (SFZone *) p->data; + FLUID_SPRINTF(zone_name, "%s/%d", preset->name, count); + zone = new_fluid_preset_zone(zone_name); + if (zone == NULL) { + return FLUID_FAILED; + } + if (fluid_preset_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { + return FLUID_FAILED; + } + if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) { + fluid_defpreset_set_global_zone(preset, zone); + } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) { + return FLUID_FAILED; + } + p = fluid_list_next(p); + count++; + } + return FLUID_OK; +} + +/* + * fluid_defpreset_add_zone + */ +int +fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) +{ + if (preset->zone == NULL) { + zone->next = NULL; + preset->zone = zone; + } else { + zone->next = preset->zone; + preset->zone = zone; + } + return FLUID_OK; +} + +/* + * fluid_defpreset_get_zone + */ +fluid_preset_zone_t* +fluid_defpreset_get_zone(fluid_defpreset_t* preset) +{ + return preset->zone; +} + +/* + * fluid_defpreset_get_global_zone + */ +fluid_preset_zone_t* +fluid_defpreset_get_global_zone(fluid_defpreset_t* preset) +{ + return preset->global_zone; +} + +/* + * fluid_preset_zone_next + */ +fluid_preset_zone_t* +fluid_preset_zone_next(fluid_preset_zone_t* preset) +{ + return preset->next; +} + +/* + * new_fluid_preset_zone + */ +fluid_preset_zone_t* +new_fluid_preset_zone(char *name) +{ + int size; + fluid_preset_zone_t* zone = NULL; + zone = FLUID_NEW(fluid_preset_zone_t); + if (zone == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + zone->next = NULL; + size = 1 + FLUID_STRLEN(name); + zone->name = FLUID_MALLOC(size); + if (zone->name == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + FLUID_STRCPY(zone->name, name); + zone->inst = NULL; + zone->keylo = 0; + zone->keyhi = 128; + zone->vello = 0; + zone->velhi = 128; + + /* Flag all generators as unused (default, they will be set when they are found + * in the sound font). + * This also sets the generator values to default, but that is of no concern here.*/ + fluid_gen_set_default_values(&zone->gen[0]); + zone->mod = NULL; /* list of modulators */ + return zone; +} + +/*************************************************************** + * + * PRESET_ZONE + */ + +/* + * delete_fluid_preset_zone + */ +int +delete_fluid_preset_zone(fluid_preset_zone_t* zone) +{ + FLUID_FREE(zone); + return FLUID_OK; +} + +/* + * fluid_preset_zone_import_sfont + */ +int +fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) +{ + fluid_list_t *r; + SFGen* sfgen; + int count; + for (count = 0, r = sfzone->gen; r != NULL; count++) { + sfgen = (SFGen *) r->data; + switch (sfgen->id) { + case GEN_KEYRANGE: + zone->keylo = (int) sfgen->amount.range.lo; + zone->keyhi = (int) sfgen->amount.range.hi; + break; + case GEN_VELRANGE: + zone->vello = (int) sfgen->amount.range.lo; + zone->velhi = (int) sfgen->amount.range.hi; + break; + default: + /* FIXME: some generators have an unsigne word amount value but i don't know which ones */ + zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + zone->gen[sfgen->id].flags = GEN_SET; + break; + } + r = fluid_list_next(r); + } + if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { + zone->inst = (fluid_inst_t*) new_fluid_inst(); + if (zone->inst == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { + return FLUID_FAILED; + } + } + + /* Import the modulators (only SF2.1 and higher) */ + for (count = 0, r = sfzone->mod; r != NULL; count++) { + + SFMod* mod_src = (SFMod *)r->data; + fluid_mod_t * mod_dest = fluid_mod_new(); + int type; + + if (mod_dest == NULL){ + return FLUID_FAILED; + } + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ + + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; + + /* *** Source *** */ + mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ + mod_dest->flags1 = 0; + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + if (mod_src->src & (1<<7)){ + mod_dest->flags1 |= FLUID_MOD_CC; + } else { + mod_dest->flags1 |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if (mod_src->src & (1<<8)){ + mod_dest->flags1 |= FLUID_MOD_NEGATIVE; + } else { + mod_dest->flags1 |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if (mod_src->src & (1<<9)){ + mod_dest->flags1 |= FLUID_MOD_BIPOLAR; + } else { + mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type=(mod_src->src) >> 10; + type &= 63; /* type is a 6-bit value */ + if (type == 0){ + mod_dest->flags1 |= FLUID_MOD_LINEAR; + } else if (type == 1){ + mod_dest->flags1 |= FLUID_MOD_CONCAVE; + } else if (type == 2){ + mod_dest->flags1 |= FLUID_MOD_CONVEX; + } else if (type == 3){ + mod_dest->flags1 |= FLUID_MOD_SWITCH; + } else { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount=0; + } + + /* *** Dest *** */ + mod_dest->dest = mod_src->dest; /* index of controlled generator */ + + /* *** Amount source *** */ + mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */ + mod_dest->flags2 = 0; + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + if (mod_src->amtsrc & (1<<7)){ + mod_dest->flags2 |= FLUID_MOD_CC; + } else { + mod_dest->flags2 |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if (mod_src->amtsrc & (1<<8)){ + mod_dest->flags2 |= FLUID_MOD_NEGATIVE; + } else { + mod_dest->flags2 |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if (mod_src->amtsrc & (1<<9)){ + mod_dest->flags2 |= FLUID_MOD_BIPOLAR; + } else { + mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = (mod_src->amtsrc) >> 10; + type &= 63; /* type is a 6-bit value */ + if (type == 0){ + mod_dest->flags2 |= FLUID_MOD_LINEAR; + } else if (type == 1){ + mod_dest->flags2 |= FLUID_MOD_CONCAVE; + } else if (type == 2){ + mod_dest->flags2 |= FLUID_MOD_CONVEX; + } else if (type == 3){ + mod_dest->flags2 |= FLUID_MOD_SWITCH; + } else { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount=0; + } + + /* *** Transform *** */ + /* SF2.01 only uses the 'linear' transform (0). + * Deactivate the modulator by setting the amount to 0 in any other case. + */ + if (mod_src->trans !=0){ + mod_dest->amount = 0; + } + + /* Store the new modulator in the zone The order of modulators + * will make a difference, at least in an instrument context: The + * second modulator overwrites the first one, if they only differ + * in amount. */ + if (count == 0){ + zone->mod = mod_dest; + } else { + fluid_mod_t * last_mod = zone->mod; + + /* Find the end of the list */ + while (last_mod->next != NULL){ + last_mod=last_mod->next; + } + + last_mod->next = mod_dest; + } + + r = fluid_list_next(r); + } /* foreach modulator */ + + return FLUID_OK; +} + +/* + * fluid_preset_zone_get_inst + */ +fluid_inst_t* +fluid_preset_zone_get_inst(fluid_preset_zone_t* zone) +{ + return zone->inst; +} + +/* + * fluid_preset_zone_inside_range + */ +int +fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel) +{ + return ((zone->keylo <= key) && + (zone->keyhi >= key) && + (zone->vello <= vel) && + (zone->velhi >= vel)); +} + +/*************************************************************** + * + * INST + */ + +/* + * new_fluid_inst + */ +fluid_inst_t* +new_fluid_inst() +{ + fluid_inst_t* inst = FLUID_NEW(fluid_inst_t); + if (inst == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + inst->name[0] = 0; + inst->global_zone = NULL; + inst->zone = NULL; + return inst; +} + +/* + * delete_fluid_inst + */ +int +delete_fluid_inst(fluid_inst_t* inst) +{ + fluid_inst_zone_t* zone; + int err = FLUID_OK; + if (inst->global_zone != NULL) { + if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) { + err = FLUID_FAILED; + } + inst->global_zone = NULL; + } + zone = inst->zone; + while (zone != NULL) { + inst->zone = zone->next; + if (delete_fluid_inst_zone(zone) != FLUID_OK) { + err = FLUID_FAILED; + } + zone = inst->zone; + } + FLUID_FREE(inst); + return err; +} + +/* + * fluid_inst_set_global_zone + */ +int +fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) +{ + inst->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_inst_import_sfont + */ +int +fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) +{ + fluid_list_t *p; + SFZone* sfzone; + fluid_inst_zone_t* zone; + char zone_name[256]; + int count; + + p = sfinst->zone; + if ((sfinst->name != NULL) && (FLUID_STRLEN(sfinst->name) > 0)) { + FLUID_STRCPY(inst->name, sfinst->name); + } else { + FLUID_STRCPY(inst->name, ""); + } + + count = 0; + while (p != NULL) { + + sfzone = (SFZone *) p->data; + FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count); + + zone = new_fluid_inst_zone(zone_name); + if (zone == NULL) { + return FLUID_FAILED; + } + + if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { + return FLUID_FAILED; + } + + if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) { + fluid_inst_set_global_zone(inst, zone); + + } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) { + return FLUID_FAILED; + } + + p = fluid_list_next(p); + count++; + } + return FLUID_OK; +} + +/* + * fluid_inst_add_zone + */ +int +fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) +{ + if (inst->zone == NULL) { + zone->next = NULL; + inst->zone = zone; + } else { + zone->next = inst->zone; + inst->zone = zone; + } + return FLUID_OK; +} + +/* + * fluid_inst_get_zone + */ +fluid_inst_zone_t* +fluid_inst_get_zone(fluid_inst_t* inst) +{ + return inst->zone; +} + +/* + * fluid_inst_get_global_zone + */ +fluid_inst_zone_t* +fluid_inst_get_global_zone(fluid_inst_t* inst) +{ + return inst->global_zone; +} + +/*************************************************************** + * + * INST_ZONE + */ + +/* + * new_fluid_inst_zone + */ +fluid_inst_zone_t* +new_fluid_inst_zone(char* name) +{ + int size; + fluid_inst_zone_t* zone = NULL; + zone = FLUID_NEW(fluid_inst_zone_t); + if (zone == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + zone->next = NULL; + size = 1 + FLUID_STRLEN(name); + zone->name = FLUID_MALLOC(size); + if (zone->name == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + FLUID_STRCPY(zone->name, name); + zone->sample = NULL; + zone->keylo = 0; + zone->keyhi = 128; + zone->vello = 0; + zone->velhi = 128; + + /* Flag the generators as unused. + * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ + fluid_gen_set_default_values(&zone->gen[0]); + zone->mod=NULL; /* list of modulators */ + return zone; +} + +/* + * delete_fluid_inst_zone + */ +int +delete_fluid_inst_zone(fluid_inst_zone_t* zone) +{ + FLUID_FREE(zone); + return FLUID_OK; +} + +/* + * fluid_inst_zone_next + */ +fluid_inst_zone_t* +fluid_inst_zone_next(fluid_inst_zone_t* zone) +{ + return zone->next; +} + +/* + * fluid_inst_zone_import_sfont + */ +int +fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) +{ + fluid_list_t *r; + SFGen* sfgen; + int count; + + for (count = 0, r = sfzone->gen; r != NULL; count++) { + sfgen = (SFGen *) r->data; + switch (sfgen->id) { + case GEN_KEYRANGE: + zone->keylo = (int) sfgen->amount.range.lo; + zone->keyhi = (int) sfgen->amount.range.hi; + break; + case GEN_VELRANGE: + zone->vello = (int) sfgen->amount.range.lo; + zone->velhi = (int) sfgen->amount.range.hi; + break; + default: + /* FIXME: some generators have an unsigned word amount value but + i don't know which ones */ + zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + zone->gen[sfgen->id].flags = GEN_SET; + break; + } + r = fluid_list_next(r); + } + + /* FIXME */ +/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ +/* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ +/* } */ + + if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { + zone->sample = fluid_defsfont_get_sample(sfont, ((SFSample *) sfzone->instsamp->data)->name); + if (zone->sample == NULL) { + FLUID_LOG(FLUID_ERR, "Couldn't find sample name"); + return FLUID_FAILED; + } + } + + /* Import the modulators (only SF2.1 and higher) */ + for (count = 0, r = sfzone->mod; r != NULL; count++) { + SFMod* mod_src = (SFMod *) r->data; + int type; + fluid_mod_t* mod_dest; + + mod_dest = fluid_mod_new(); + if (mod_dest == NULL){ + return FLUID_FAILED; + } + + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ + + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; + + /* *** Source *** */ + mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ + mod_dest->flags1 = 0; + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + if (mod_src->src & (1<<7)){ + mod_dest->flags1 |= FLUID_MOD_CC; + } else { + mod_dest->flags1 |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if (mod_src->src & (1<<8)){ + mod_dest->flags1 |= FLUID_MOD_NEGATIVE; + } else { + mod_dest->flags1 |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if (mod_src->src & (1<<9)){ + mod_dest->flags1 |= FLUID_MOD_BIPOLAR; + } else { + mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = (mod_src->src) >> 10; + type &= 63; /* type is a 6-bit value */ + if (type == 0){ + mod_dest->flags1 |= FLUID_MOD_LINEAR; + } else if (type == 1){ + mod_dest->flags1 |= FLUID_MOD_CONCAVE; + } else if (type == 2){ + mod_dest->flags1 |= FLUID_MOD_CONVEX; + } else if (type == 3){ + mod_dest->flags1 |= FLUID_MOD_SWITCH; + } else { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + + /* *** Dest *** */ + mod_dest->dest=mod_src->dest; /* index of controlled generator */ + + /* *** Amount source *** */ + mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */ + mod_dest->flags2 = 0; + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + if (mod_src->amtsrc & (1<<7)){ + mod_dest->flags2 |= FLUID_MOD_CC; + } else { + mod_dest->flags2 |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if (mod_src->amtsrc & (1<<8)){ + mod_dest->flags2 |= FLUID_MOD_NEGATIVE; + } else { + mod_dest->flags2 |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if (mod_src->amtsrc & (1<<9)){ + mod_dest->flags2 |= FLUID_MOD_BIPOLAR; + } else { + mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type=(mod_src->amtsrc) >> 10; + type &= 63; /* type is a 6-bit value */ + if (type == 0){ + mod_dest->flags2 |= FLUID_MOD_LINEAR; + } else if (type == 1){ + mod_dest->flags2 |= FLUID_MOD_CONCAVE; + } else if (type == 2){ + mod_dest->flags2 |= FLUID_MOD_CONVEX; + } else if (type == 3){ + mod_dest->flags2 |= FLUID_MOD_SWITCH; + } else { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + + /* *** Transform *** */ + /* SF2.01 only uses the 'linear' transform (0). + * Deactivate the modulator by setting the amount to 0 in any other case. + */ + if (mod_src->trans !=0){ + mod_dest->amount = 0; + } + + /* Store the new modulator in the zone + * The order of modulators will make a difference, at least in an instrument context: + * The second modulator overwrites the first one, if they only differ in amount. */ + if (count == 0){ + zone->mod=mod_dest; + } else { + fluid_mod_t * last_mod=zone->mod; + /* Find the end of the list */ + while (last_mod->next != NULL){ + last_mod=last_mod->next; + } + last_mod->next=mod_dest; + } + + r = fluid_list_next(r); + } /* foreach modulator */ + return FLUID_OK; +} + +/* + * fluid_inst_zone_get_sample + */ +fluid_sample_t* +fluid_inst_zone_get_sample(fluid_inst_zone_t* zone) +{ + return zone->sample; +} + +/* + * fluid_inst_zone_inside_range + */ +int +fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) +{ + return ((zone->keylo <= key) && + (zone->keyhi >= key) && + (zone->vello <= vel) && + (zone->velhi >= vel)); +} + +/*************************************************************** + * + * SAMPLE + */ + +/* + * new_fluid_sample + */ +fluid_sample_t* +new_fluid_sample() +{ + fluid_sample_t* sample = NULL; + + sample = FLUID_NEW(fluid_sample_t); + if (sample == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + memset(sample, 0, sizeof(fluid_sample_t)); + sample->valid = 1; + + return sample; +} + +/* + * delete_fluid_sample + */ +int +delete_fluid_sample(fluid_sample_t* sample) +{ + FLUID_FREE(sample); + return FLUID_OK; +} + +/* + * fluid_sample_in_rom + */ +int +fluid_sample_in_rom(fluid_sample_t* sample) +{ + return (sample->sampletype & FLUID_SAMPLETYPE_ROM); +} + +/* + * fluid_sample_import_sfont + */ +int +fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont) +{ + FLUID_STRCPY(sample->name, sfsample->name); + sample->data = sfont->sampledata; + sample->start = sfsample->start; + sample->end = sfsample->start + sfsample->end; + sample->loopstart = sfsample->start + sfsample->loopstart; + sample->loopend = sfsample->start + sfsample->loopend; + sample->samplerate = sfsample->samplerate; + sample->origpitch = sfsample->origpitch; + sample->pitchadj = sfsample->pitchadj; + sample->sampletype = sfsample->sampletype; + + if (sample->sampletype & FLUID_SAMPLETYPE_ROM) { + sample->valid = 0; + FLUID_LOG(FLUID_WARN, "Ignoring sample %s: can't use ROM samples", sample->name); + } + if (sample->end - sample->start < 8) { + sample->valid = 0; + FLUID_LOG(FLUID_WARN, "Ignoring sample %s: too few sample data points", sample->name); + } else { +/* if (sample->loopstart < sample->start + 8) { */ +/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */ +/* sample->loopstart = sample->start + 8; */ +/* } */ +/* if (sample->loopend > sample->end - 8) { */ +/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */ +/* sample->loopend = sample->end - 8; */ +/* } */ + } + return FLUID_OK; +} + + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + + + +/*=================================sfload.c======================== + Borrowed from Smurf SoundFont Editor by Josh Green + =================================================================*/ + +/* + functions for loading data from sfont files, with appropriate byte swapping + on big endian machines. Sfont IDs are not swapped because the ID read is + equivalent to the matching ID list in memory regardless of LE/BE machine +*/ + +#ifdef WORDS_BIGENDIAN +#define READCHUNK(var,fd) G_STMT_START { \ + if (!safe_fread(var, 8, fd)) \ + return(FAIL); \ + ((SFChunk *)(var))->size = GUINT32_FROM_BE(((SFChunk *)(var))->size); \ +} G_STMT_END +#else +#define READCHUNK(var,fd) G_STMT_START { \ + if (!safe_fread(var, 8, fd)) \ + return(FAIL); \ + ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \ +} G_STMT_END +#endif +#define READID(var,fd) G_STMT_START { \ + if (!safe_fread(var, 4, fd)) \ + return(FAIL); \ +} G_STMT_END +#define READSTR(var,fd) G_STMT_START { \ + if (!safe_fread(var, 20, fd)) \ + return(FAIL); \ + (*var)[20] = '\0'; \ +} G_STMT_END +#ifdef WORDS_BIGENDIAN +#define READD(var,fd) G_STMT_START { \ + unsigned int _temp; \ + if (!safe_fread(&_temp, 4, fd)) \ + return(FAIL); \ + var = GINT32_FROM_BE(_temp); \ +} G_STMT_END +#else +#define READD(var,fd) G_STMT_START { \ + unsigned int _temp; \ + if (!safe_fread(&_temp, 4, fd)) \ + return(FAIL); \ + var = GINT32_FROM_LE(_temp); \ +} G_STMT_END +#endif +#ifdef WORDS_BIGENDIAN + #define READW(var,fd) G_STMT_START { \ + unsigned short _temp; \ + if (!safe_fread(&_temp, 2, fd)) \ + return(FAIL); \ +var = GINT16_FROM_BE(_temp); \ +} G_STMT_END +#else +#define READW(var,fd) G_STMT_START { \ + unsigned short _temp; \ + if (!safe_fread(&_temp, 2, fd)) \ + return(FAIL); \ + var = GINT16_FROM_LE(_temp); \ +} G_STMT_END +#endif +#define READB(var,fd) G_STMT_START { \ + if (!safe_fread(&var, 1, fd)) \ + return(FAIL); \ +} G_STMT_END +#define FSKIP(size,fd) G_STMT_START { \ + if (!safe_fseek(fd, size, SEEK_CUR)) \ + return(FAIL); \ +} G_STMT_END +#define FSKIPW(fd) G_STMT_START { \ + if (!safe_fseek(fd, 2, SEEK_CUR)) \ + return(FAIL); \ +} G_STMT_END + +/* removes and advances a fluid_list_t pointer */ +#define SLADVREM(list, item) G_STMT_START { \ + fluid_list_t *_temp = item; \ + item = fluid_list_next(item); \ + list = fluid_list_remove_link(list, _temp); \ + delete1_fluid_list(_temp); \ +} G_STMT_END + +static int chunkid (unsigned int id); +static int load_body (unsigned int size, SFData * sf, FILE * fd); +static int read_listchunk (SFChunk * chunk, FILE * fd); +static int process_info (int size, SFData * sf, FILE * fd); +static int process_sdta (int size, SFData * sf, FILE * fd); +static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, + int * size, FILE * fd); +static int process_pdta (int size, SFData * sf, FILE * fd); +static int load_phdr (int size, SFData * sf, FILE * fd); +static int load_pbag (int size, SFData * sf, FILE * fd); +static int load_pmod (int size, SFData * sf, FILE * fd); +static int load_pgen (int size, SFData * sf, FILE * fd); +static int load_ihdr (int size, SFData * sf, FILE * fd); +static int load_ibag (int size, SFData * sf, FILE * fd); +static int load_imod (int size, SFData * sf, FILE * fd); +static int load_igen (int size, SFData * sf, FILE * fd); +static int load_shdr (unsigned int size, SFData * sf, FILE * fd); +static int fixup_pgen (SFData * sf); +static int fixup_igen (SFData * sf); +static int fixup_sample (SFData * sf); + +char idlist[] = { + "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" + "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr" +}; + +static unsigned int sdtachunk_size; + +/* sound font file load functions */ +static int +chunkid (unsigned int id) +{ + unsigned int i; + unsigned int *p; + + p = (unsigned int *) & idlist; + for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1) + if (*p == id) + return (i + 1); + + return (UNKN_ID); +} + +SFData * +sfload_file (const char * fname) +{ + SFData *sf = NULL; + FILE *fd; + int fsize = 0; + int err = FALSE; + + if (!(fd = fopen (fname, "rb"))) + { + FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname); + return (NULL); + } + + if (!(sf = safe_malloc (sizeof (SFData)))) + err = TRUE; + if (!err) + { + memset (sf, 0, sizeof (SFData)); /* zero sfdata */ + sf->fname = FLUID_STRDUP (fname); /* copy file name */ + sf->sffd = fd; + } + + /* get size of file */ + if (!err && fseek (fd, 0L, SEEK_END) == -1) + { /* seek to end of file */ + err = TRUE; + FLUID_LOG (FLUID_ERR, _("Seek to end of file failed")); + } + if (!err && (fsize = ftell (fd)) == -1) + { /* position = size */ + err = TRUE; + FLUID_LOG (FLUID_ERR, _("Get end of file position failed")); + } + if (!err) + rewind (fd); + + if (!err && !load_body (fsize, sf, fd)) + err = TRUE; /* load the sfont */ + + if (err) + { + if (sf) + sfont_close (sf); + return (NULL); + } + + return (sf); +} + +static int +load_body (unsigned int size, SFData * sf, FILE * fd) +{ + SFChunk chunk; + + READCHUNK (&chunk, fd); /* load RIFF chunk */ + if (chunkid (chunk.id) != RIFF_ID) { /* error if not RIFF */ + FLUID_LOG (FLUID_ERR, _("Not a RIFF file")); + return (FAIL); + } + + READID (&chunk.id, fd); /* load file ID */ + if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */ + FLUID_LOG (FLUID_ERR, _("Not a sound font file")); + return (FAIL); + } + + if (chunk.size != size - 8) { + gerr (ErrCorr, _("Sound font file size mismatch")); + return (FAIL); + } + + /* Process INFO block */ + if (!read_listchunk (&chunk, fd)) + return (FAIL); + if (chunkid (chunk.id) != INFO_ID) + return (gerr (ErrCorr, _("Invalid ID found when expecting INFO chunk"))); + if (!process_info (chunk.size, sf, fd)) + return (FAIL); + + /* Process sample chunk */ + if (!read_listchunk (&chunk, fd)) + return (FAIL); + if (chunkid (chunk.id) != SDTA_ID) + return (gerr (ErrCorr, + _("Invalid ID found when expecting SAMPLE chunk"))); + if (!process_sdta (chunk.size, sf, fd)) + return (FAIL); + + /* process HYDRA chunk */ + if (!read_listchunk (&chunk, fd)) + return (FAIL); + if (chunkid (chunk.id) != PDTA_ID) + return (gerr (ErrCorr, _("Invalid ID found when expecting HYDRA chunk"))); + if (!process_pdta (chunk.size, sf, fd)) + return (FAIL); + + if (!fixup_pgen (sf)) + return (FAIL); + if (!fixup_igen (sf)) + return (FAIL); + if (!fixup_sample (sf)) + return (FAIL); + + /* sort preset list by bank, preset # */ + sf->preset = fluid_list_sort (sf->preset, + (fluid_compare_func_t) sfont_preset_compare_func); + + return (OK); +} + +static int +read_listchunk (SFChunk * chunk, FILE * fd) +{ + READCHUNK (chunk, fd); /* read list chunk */ + if (chunkid (chunk->id) != LIST_ID) /* error if ! list chunk */ + return (gerr (ErrCorr, _("Invalid chunk id in level 0 parse"))); + READID (&chunk->id, fd); /* read id string */ + chunk->size -= 4; + return (OK); +} + +static int +process_info (int size, SFData * sf, FILE * fd) +{ + SFChunk chunk; + unsigned char id; + char *item; + unsigned short ver; + + while (size > 0) + { + READCHUNK (&chunk, fd); + size -= 8; + + id = chunkid (chunk.id); + + if (id == IFIL_ID) + { /* sound font version chunk? */ + if (chunk.size != 4) + return (gerr (ErrCorr, + _("Sound font version info chunk has invalid size"))); + + READW (ver, fd); + sf->version.major = ver; + READW (ver, fd); + sf->version.minor = ver; + + if (sf->version.major < 2) { + FLUID_LOG (FLUID_ERR, + _("Sound font version is %d.%d which is not" + " supported, convert to version 2.0x"), + sf->version.major, + sf->version.minor); + return (FAIL); + } + + if (sf->version.major > 2) { + FLUID_LOG (FLUID_WARN, + _("Sound font version is %d.%d which is newer than" + " what this version of FLUID Synth was designed for (v2.0x)"), + sf->version.major, + sf->version.minor); + return (FAIL); + } + } + else if (id == IVER_ID) + { /* ROM version chunk? */ + if (chunk.size != 4) + return (gerr (ErrCorr, + _("ROM version info chunk has invalid size"))); + + READW (ver, fd); + sf->romver.major = ver; + READW (ver, fd); + sf->romver.minor = ver; + } + else if (id != UNKN_ID) + { + if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) + || (chunk.size % 2)) + return (gerr (ErrCorr, + _("INFO sub chunk %.4s has invalid chunk size" + " of %d bytes"), &chunk.id, chunk.size)); + + /* alloc for chunk id and da chunk */ + if (!(item = safe_malloc (chunk.size + 1))) + return (FAIL); + + /* attach to INFO list, sfont_close will cleanup if FAIL occurs */ + sf->info = fluid_list_append (sf->info, item); + + *(unsigned char *) item = id; + if (!safe_fread (&item[1], chunk.size, fd)) + return (FAIL); + + /* force terminate info item (don't forget uint8 info ID) */ + *(item + chunk.size) = '\0'; + } + else + return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk"))); + size -= chunk.size; + } + + if (size < 0) + return (gerr (ErrCorr, _("INFO chunk size mismatch"))); + + return (OK); +} + +static int +process_sdta (int size, SFData * sf, FILE * fd) +{ + SFChunk chunk; + + if (size == 0) + return (OK); /* no sample data? */ + + /* read sub chunk */ + READCHUNK (&chunk, fd); + size -= 8; + + if (chunkid (chunk.id) != SMPL_ID) + return (gerr (ErrCorr, + _("Expected SMPL chunk found invalid id instead"))); + + if ((size - chunk.size) != 0) + return (gerr (ErrCorr, _("SDTA chunk size mismatch"))); + + /* sample data follows */ + sf->samplepos = ftell (fd); + + /* used in fixup_sample() to check validity of sample headers */ + sdtachunk_size = chunk.size; + sf->samplesize = chunk.size; + + FSKIP (chunk.size, fd); + + return (OK); +} + +static int +pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, + int * size, FILE * fd) +{ + unsigned int id; + char *expstr; + + expstr = CHNKIDSTR (expid); /* in case we need it */ + + READCHUNK (chunk, fd); + *size -= 8; + + if ((id = chunkid (chunk->id)) != expid) + return (gerr (ErrCorr, _("Expected" + " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr)); + + if (chunk->size % reclen) /* valid chunk size? */ + return (gerr (ErrCorr, + _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr, + reclen)); + if ((*size -= chunk->size) < 0) + return (gerr (ErrCorr, + _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr)); + return (OK); +} + +static int +process_pdta (int size, SFData * sf, FILE * fd) +{ + SFChunk chunk; + + if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_phdr (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_pbag (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_pmod (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_pgen (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_ihdr (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_ibag (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_imod (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_igen (chunk.size, sf, fd)) + return (FAIL); + + if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd)) + return (FAIL); + if (!load_shdr (chunk.size, sf, fd)) + return (FAIL); + + return (OK); +} + +/* preset header loader */ +static int +load_phdr (int size, SFData * sf, FILE * fd) +{ + int i, i2; + SFPreset *p, *pr = NULL; /* ptr to current & previous preset */ + unsigned short zndx, pzndx = 0; + + if (size % SFPHDRSIZE || size == 0) + return (gerr (ErrCorr, _("Preset header chunk size is invalid"))); + + i = size / SFPHDRSIZE - 1; + if (i == 0) + { /* at least one preset + term record */ + FLUID_LOG (FLUID_WARN, _("File contains no presets")); + FSKIP (SFPHDRSIZE, fd); + return (OK); + } + + for (; i > 0; i--) + { /* load all preset headers */ + p = FLUID_NEW (SFPreset); + sf->preset = fluid_list_append (sf->preset, p); + p->zone = NULL; /* In case of failure, sfont_close can cleanup */ + READSTR (&p->name, fd); /* possible read failure ^ */ + READW (p->prenum, fd); + READW (p->bank, fd); + READW (zndx, fd); + READD (p->libr, fd); + READD (p->genre, fd); + READD (p->morph, fd); + + if (pr) + { /* not first preset? */ + if (zndx < pzndx) + return (gerr (ErrCorr, _("Preset header indices not monotonic"))); + i2 = zndx - pzndx; + while (i2--) + { + pr->zone = fluid_list_prepend (pr->zone, NULL); + } + } + else if (zndx > 0) /* 1st preset, warn if ofs >0 */ + FLUID_LOG (FLUID_WARN, _("%d preset zones not referenced, discarding"), zndx); + pr = p; /* update preset ptr */ + pzndx = zndx; + } + + FSKIP (24, fd); + READW (zndx, fd); /* Read terminal generator index */ + FSKIP (12, fd); + + if (zndx < pzndx) + return (gerr (ErrCorr, _("Preset header indices not monotonic"))); + i2 = zndx - pzndx; + while (i2--) + { + pr->zone = fluid_list_prepend (pr->zone, NULL); + } + + return (OK); +} + +/* preset bag loader */ +static int +load_pbag (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx; + unsigned short pgenndx = 0, pmodndx = 0; + unsigned short i; + + if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ + return (gerr (ErrCorr, _("Preset bag chunk size is invalid"))); + + p = sf->preset; + while (p) + { /* traverse through presets */ + p2 = ((SFPreset *) (p->data))->zone; + while (p2) + { /* traverse preset's zones */ + if ((size -= SFBAGSIZE) < 0) + return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); + z = FLUID_NEW (SFZone); + p2->data = z; + z->gen = NULL; /* Init gen and mod before possible failure, */ + z->mod = NULL; /* to ensure proper cleanup (sfont_close) */ + READW (genndx, fd); /* possible read failure ^ */ + READW (modndx, fd); + z->instsamp = NULL; + + if (pz) + { /* if not first zone */ + if (genndx < pgenndx) + return (gerr (ErrCorr, + _("Preset bag generator indices not monotonic"))); + if (modndx < pmodndx) + return (gerr (ErrCorr, + _("Preset bag modulator indices not monotonic"))); + i = genndx - pgenndx; + while (i--) + pz->gen = fluid_list_prepend (pz->gen, NULL); + i = modndx - pmodndx; + while (i--) + pz->mod = fluid_list_prepend (pz->mod, NULL); + } + pz = z; /* update previous zone ptr */ + pgenndx = genndx; /* update previous zone gen index */ + pmodndx = modndx; /* update previous zone mod index */ + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + size -= SFBAGSIZE; + if (size != 0) + return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); + + READW (genndx, fd); + READW (modndx, fd); + + if (!pz) + { + if (genndx > 0) + FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0")); + if (modndx > 0) + FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0")); + return (OK); + } + + if (genndx < pgenndx) + return (gerr (ErrCorr, _("Preset bag generator indices not monotonic"))); + if (modndx < pmodndx) + return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic"))); + i = genndx - pgenndx; + while (i--) + pz->gen = fluid_list_prepend (pz->gen, NULL); + i = modndx - pmodndx; + while (i--) + pz->mod = fluid_list_prepend (pz->mod, NULL); + + return (OK); +} + +/* preset modulator loader */ +static int +load_pmod (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2, *p3; + SFMod *m; + + p = sf->preset; + while (p) + { /* traverse through all presets */ + p2 = ((SFPreset *) (p->data))->zone; + while (p2) + { /* traverse this preset's zones */ + p3 = ((SFZone *) (p2->data))->mod; + while (p3) + { /* load zone's modulators */ + if ((size -= SFMODSIZE) < 0) + return (gerr (ErrCorr, + _("Preset modulator chunk size mismatch"))); + m = FLUID_NEW (SFMod); + p3->data = m; + READW (m->src, fd); + READW (m->dest, fd); + READW (m->amount, fd); + READW (m->amtsrc, fd); + READW (m->trans, fd); + p3 = fluid_list_next (p3); + } + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if (size == 0) + return (OK); + + size -= SFMODSIZE; + if (size != 0) + return (gerr (ErrCorr, _("Preset modulator chunk size mismatch"))); + FSKIP (SFMODSIZE, fd); /* terminal mod */ + + return (OK); +} + +/* ------------------------------------------------------------------- + * preset generator loader + * generator (per preset) loading rules: + * Zones with no generators or modulators shall be annihilated + * Global zone must be 1st zone, discard additional ones (instrumentless zones) + * + * generator (per zone) loading rules (in order of decreasing precedence): + * KeyRange is 1st in list (if exists), else discard + * if a VelRange exists only preceded by a KeyRange, else discard + * if a generator follows an instrument discard it + * if a duplicate generator exists replace previous one + * ------------------------------------------------------------------- */ +static int +load_pgen (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; + SFZone *z; + SFGen *g; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, gzone, discarded; + + p = sf->preset; + while (p) + { /* traverse through all presets */ + gzone = FALSE; + discarded = FALSE; + p2 = ((SFPreset *) (p->data))->zone; + if (p2) + hz = &p2; + while (p2) + { /* traverse preset's zones */ + level = 0; + z = (SFZone *) (p2->data); + p3 = z->gen; + while (p3) + { /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + if ((size -= SFGENSIZE) < 0) + return (gerr (ErrCorr, + _("Preset generator chunk size mismatch"))); + + READW (genid, fd); + + if (genid == Gen_KeyRange) + { /* nothing precedes */ + if (level == 0) + { + level = 1; + READB (genval.range.lo, fd); + READB (genval.range.hi, fd); + } + else + skip = TRUE; + } + else if (genid == Gen_VelRange) + { /* only KeyRange precedes */ + if (level <= 1) + { + level = 2; + READB (genval.range.lo, fd); + READB (genval.range.hi, fd); + } + else + skip = TRUE; + } + else if (genid == Gen_Instrument) + { /* inst is last gen */ + level = 3; + READW (genval.uword, fd); + ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); + break; /* break out of generator loop */ + } + else + { + level = 2; + if (gen_validp (genid)) + { /* generator valid? */ + READW (genval.sword, fd); + dup = gen_inlist (genid, z->gen); + } + else + skip = TRUE; + } + + if (!skip) + { + if (!dup) + { /* if gen ! dup alloc new */ + g = FLUID_NEW (SFGen); + p3->data = g; + g->id = genid; + } + else + { + g = (SFGen *) (dup->data); /* ptr to orig gen */ + drop = TRUE; + } + g->amount = genval; + } + else + { /* Skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW (fd); + } + + if (!drop) + p3 = fluid_list_next (p3); /* next gen */ + else + SLADVREM (z->gen, p3); /* drop place holder */ + + } /* generator loop */ + + if (level == 3) + SLADVREM (z->gen, p3); /* zone has inst? */ + else + { /* congratulations its a global zone */ + if (!gzone) + { /* Prior global zones? */ + gzone = TRUE; + + /* if global zone is not 1st zone, relocate */ + if (*hz != p2) + { + void* save = p2->data; + FLUID_LOG (FLUID_WARN, + _("Preset \"%s\": Global zone is not first zone"), + ((SFPreset *) (p->data))->name); + SLADVREM (*hz, p2); + *hz = fluid_list_prepend (*hz, save); + continue; + } + } + else + { /* previous global zone exists, discard */ + FLUID_LOG (FLUID_WARN, + _("Preset \"%s\": Discarding invalid global zone"), + ((SFPreset *) (p->data))->name); + sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); + } + } + + while (p3) + { /* Kill any zones following an instrument */ + discarded = TRUE; + if ((size -= SFGENSIZE) < 0) + return (gerr (ErrCorr, + _("Preset generator chunk size mismatch"))); + FSKIP (SFGENSIZE, fd); + SLADVREM (z->gen, p3); + } + + p2 = fluid_list_next (p2); /* next zone */ + } + if (discarded) + FLUID_LOG(FLUID_WARN, + _("Preset \"%s\": Some invalid generators were discarded"), + ((SFPreset *) (p->data))->name); + p = fluid_list_next (p); + } + + /* in case there isn't a terminal record */ + if (size == 0) + return (OK); + + size -= SFGENSIZE; + if (size != 0) + return (gerr (ErrCorr, _("Preset generator chunk size mismatch"))); + FSKIP (SFGENSIZE, fd); /* terminal gen */ + + return (OK); +} + +/* instrument header loader */ +static int +load_ihdr (int size, SFData * sf, FILE * fd) +{ + int i, i2; + SFInst *p, *pr = NULL; /* ptr to current & previous instrument */ + unsigned short zndx, pzndx = 0; + + if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */ + return (gerr (ErrCorr, _("Instrument header has invalid size"))); + + size = size / SFIHDRSIZE - 1; + if (size == 0) + { /* at least one preset + term record */ + FLUID_LOG (FLUID_WARN, _("File contains no instruments")); + FSKIP (SFIHDRSIZE, fd); + return (OK); + } + + for (i = 0; i < size; i++) + { /* load all instrument headers */ + p = FLUID_NEW (SFInst); + sf->inst = fluid_list_append (sf->inst, p); + p->zone = NULL; /* For proper cleanup if fail (sfont_close) */ + READSTR (&p->name, fd); /* Possible read failure ^ */ + READW (zndx, fd); + + if (pr) + { /* not first instrument? */ + if (zndx < pzndx) + return (gerr (ErrCorr, + _("Instrument header indices not monotonic"))); + i2 = zndx - pzndx; + while (i2--) + pr->zone = fluid_list_prepend (pr->zone, NULL); + } + else if (zndx > 0) /* 1st inst, warn if ofs >0 */ + FLUID_LOG (FLUID_WARN, _("%d instrument zones not referenced, discarding"), + zndx); + pzndx = zndx; + pr = p; /* update instrument ptr */ + } + + FSKIP (20, fd); + READW (zndx, fd); + + if (zndx < pzndx) + return (gerr (ErrCorr, _("Instrument header indices not monotonic"))); + i2 = zndx - pzndx; + while (i2--) + pr->zone = fluid_list_prepend (pr->zone, NULL); + + return (OK); +} + +/* instrument bag loader */ +static int +load_ibag (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; + int i; + + if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ + return (gerr (ErrCorr, _("Instrument bag chunk size is invalid"))); + + p = sf->inst; + while (p) + { /* traverse through inst */ + p2 = ((SFInst *) (p->data))->zone; + while (p2) + { /* load this inst's zones */ + if ((size -= SFBAGSIZE) < 0) + return (gerr (ErrCorr, _("Instrument bag chunk size mismatch"))); + z = FLUID_NEW (SFZone); + p2->data = z; + z->gen = NULL; /* In case of failure, */ + z->mod = NULL; /* sfont_close can clean up */ + READW (genndx, fd); /* READW = possible read failure */ + READW (modndx, fd); + z->instsamp = NULL; + + if (pz) + { /* if not first zone */ + if (genndx < pgenndx) + return (gerr (ErrCorr, + _("Instrument generator indices not monotonic"))); + if (modndx < pmodndx) + return (gerr (ErrCorr, + _("Instrument modulator indices not monotonic"))); + i = genndx - pgenndx; + while (i--) + pz->gen = fluid_list_prepend (pz->gen, NULL); + i = modndx - pmodndx; + while (i--) + pz->mod = fluid_list_prepend (pz->mod, NULL); + } + pz = z; /* update previous zone ptr */ + pgenndx = genndx; + pmodndx = modndx; + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + size -= SFBAGSIZE; + if (size != 0) + return (gerr (ErrCorr, _("Instrument chunk size mismatch"))); + + READW (genndx, fd); + READW (modndx, fd); + + if (!pz) + { /* in case that all are no zoners */ + if (genndx > 0) + FLUID_LOG (FLUID_WARN, + _("No instrument generators and terminal index not 0")); + if (modndx > 0) + FLUID_LOG (FLUID_WARN, + _("No instrument modulators and terminal index not 0")); + return (OK); + } + + if (genndx < pgenndx) + return (gerr (ErrCorr, _("Instrument generator indices not monotonic"))); + if (modndx < pmodndx) + return (gerr (ErrCorr, _("Instrument modulator indices not monotonic"))); + i = genndx - pgenndx; + while (i--) + pz->gen = fluid_list_prepend (pz->gen, NULL); + i = modndx - pmodndx; + while (i--) + pz->mod = fluid_list_prepend (pz->mod, NULL); + + return (OK); +} + +/* instrument modulator loader */ +static int +load_imod (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2, *p3; + SFMod *m; + + p = sf->inst; + while (p) + { /* traverse through all inst */ + p2 = ((SFInst *) (p->data))->zone; + while (p2) + { /* traverse this inst's zones */ + p3 = ((SFZone *) (p2->data))->mod; + while (p3) + { /* load zone's modulators */ + if ((size -= SFMODSIZE) < 0) + return (gerr (ErrCorr, + _("Instrument modulator chunk size mismatch"))); + m = FLUID_NEW (SFMod); + p3->data = m; + READW (m->src, fd); + READW (m->dest, fd); + READW (m->amount, fd); + READW (m->amtsrc, fd); + READW (m->trans, fd); + p3 = fluid_list_next (p3); + } + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if (size == 0) + return (OK); + + size -= SFMODSIZE; + if (size != 0) + return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch"))); + FSKIP (SFMODSIZE, fd); /* terminal mod */ + + return (OK); +} + +/* load instrument generators (see load_pgen for loading rules) */ +static int +load_igen (int size, SFData * sf, FILE * fd) +{ + fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; + SFZone *z; + SFGen *g; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, gzone, discarded; + + p = sf->inst; + while (p) + { /* traverse through all instruments */ + gzone = FALSE; + discarded = FALSE; + p2 = ((SFInst *) (p->data))->zone; + if (p2) + hz = &p2; + while (p2) + { /* traverse this instrument's zones */ + level = 0; + z = (SFZone *) (p2->data); + p3 = z->gen; + while (p3) + { /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + if ((size -= SFGENSIZE) < 0) + return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); + + READW (genid, fd); + + if (genid == Gen_KeyRange) + { /* nothing precedes */ + if (level == 0) + { + level = 1; + READB (genval.range.lo, fd); + READB (genval.range.hi, fd); + } + else + skip = TRUE; + } + else if (genid == Gen_VelRange) + { /* only KeyRange precedes */ + if (level <= 1) + { + level = 2; + READB (genval.range.lo, fd); + READB (genval.range.hi, fd); + } + else + skip = TRUE; + } + else if (genid == Gen_SampleId) + { /* sample is last gen */ + level = 3; + READW (genval.uword, fd); + ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); + break; /* break out of generator loop */ + } + else + { + level = 2; + if (gen_valid (genid)) + { /* gen valid? */ + READW (genval.sword, fd); + dup = gen_inlist (genid, z->gen); + } + else + skip = TRUE; + } + + if (!skip) + { + if (!dup) + { /* if gen ! dup alloc new */ + g = FLUID_NEW (SFGen); + p3->data = g; + g->id = genid; + } + else + { + g = (SFGen *) (dup->data); + drop = TRUE; + } + g->amount = genval; + } + else + { /* skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW (fd); + } + + if (!drop) + p3 = fluid_list_next (p3); /* next gen */ + else + SLADVREM (z->gen, p3); + + } /* generator loop */ + + if (level == 3) + SLADVREM (z->gen, p3); /* zone has sample? */ + else + { /* its a global zone */ + if (!gzone) + { + gzone = TRUE; + + /* if global zone is not 1st zone, relocate */ + if (*hz != p2) + { + void* save = p2->data; + FLUID_LOG (FLUID_WARN, + _("Instrument \"%s\": Global zone is not first zone"), + ((SFPreset *) (p->data))->name); + SLADVREM (*hz, p2); + *hz = fluid_list_prepend (*hz, save); + continue; + } + } + else + { /* previous global zone exists, discard */ + FLUID_LOG (FLUID_WARN, + _("Instrument \"%s\": Discarding invalid global zone"), + ((SFInst *) (p->data))->name); + sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); + } + } + + while (p3) + { /* Kill any zones following a sample */ + discarded = TRUE; + if ((size -= SFGENSIZE) < 0) + return (gerr (ErrCorr, + _("Instrument generator chunk size mismatch"))); + FSKIP (SFGENSIZE, fd); + SLADVREM (z->gen, p3); + } + + p2 = fluid_list_next (p2); /* next zone */ + } + if (discarded) + FLUID_LOG(FLUID_WARN, + _("Instrument \"%s\": Some invalid generators were discarded"), + ((SFInst *) (p->data))->name); + p = fluid_list_next (p); + } + + /* for those non-terminal record cases, grr! */ + if (size == 0) + return (OK); + + size -= SFGENSIZE; + if (size != 0) + return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); + FSKIP (SFGENSIZE, fd); /* terminal gen */ + + return (OK); +} + +/* sample header loader */ +static int +load_shdr (unsigned int size, SFData * sf, FILE * fd) +{ + unsigned int i; + SFSample *p; + + if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */ + return (gerr (ErrCorr, _("Sample header has invalid size"))); + + size = size / SFSHDRSIZE - 1; + if (size == 0) + { /* at least one sample + term record? */ + FLUID_LOG (FLUID_WARN, _("File contains no samples")); + FSKIP (SFSHDRSIZE, fd); + return (OK); + } + + /* load all sample headers */ + for (i = 0; i < size; i++) + { + p = FLUID_NEW (SFSample); + sf->sample = fluid_list_append (sf->sample, p); + READSTR (&p->name, fd); + READD (p->start, fd); + READD (p->end, fd); /* - end, loopstart and loopend */ + READD (p->loopstart, fd); /* - will be checked and turned into */ + READD (p->loopend, fd); /* - offsets in fixup_sample() */ + READD (p->samplerate, fd); + READB (p->origpitch, fd); + READB (p->pitchadj, fd); + FSKIPW (fd); /* skip sample link */ + READW (p->sampletype, fd); + p->samfile = 0; + } + + FSKIP (SFSHDRSIZE, fd); /* skip terminal shdr */ + + return (OK); +} + +/* "fixup" (inst # -> inst ptr) instrument references in preset list */ +static int +fixup_pgen (SFData * sf) +{ + fluid_list_t *p, *p2, *p3; + SFZone *z; + int i; + + p = sf->preset; + while (p) + { + p2 = ((SFPreset *) (p->data))->zone; + while (p2) + { /* traverse this preset's zones */ + z = (SFZone *) (p2->data); + if ((i = GPOINTER_TO_INT (z->instsamp))) + { /* load instrument # */ + p3 = fluid_list_nth (sf->inst, i - 1); + if (!p3) + return (gerr (ErrCorr, + _("Preset %03d %03d: Invalid instrument reference"), + ((SFPreset *) (p->data))->bank, + ((SFPreset *) (p->data))->prenum)); + z->instsamp = p3; + } + else + z->instsamp = NULL; + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + return (OK); +} + +/* "fixup" (sample # -> sample ptr) sample references in instrument list */ +static int +fixup_igen (SFData * sf) +{ + fluid_list_t *p, *p2, *p3; + SFZone *z; + int i; + + p = sf->inst; + while (p) + { + p2 = ((SFInst *) (p->data))->zone; + while (p2) + { /* traverse instrument's zones */ + z = (SFZone *) (p2->data); + if ((i = GPOINTER_TO_INT (z->instsamp))) + { /* load sample # */ + p3 = fluid_list_nth (sf->sample, i - 1); + if (!p3) + return (gerr (ErrCorr, + _("Instrument \"%s\": Invalid sample reference"), + ((SFInst *) (p->data))->name)); + z->instsamp = p3; + } + p2 = fluid_list_next (p2); + } + p = fluid_list_next (p); + } + + return (OK); +} + +/* convert sample end, loopstart and loopend to offsets and check if valid */ +static int +fixup_sample (SFData * sf) +{ + fluid_list_t *p; + SFSample *sam; + + p = sf->sample; + while (p) + { + sam = (SFSample *) (p->data); + + /* if sample is not a ROM sample and end is over the sample data chunk + or sam start is greater than 4 less than the end (at least 4 samples) */ + if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM) + && sam->end > sdtachunk_size) || sam->start > (sam->end - 4)) + { + FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid," + " disabling and will not be saved"), sam->name); + + /* disable sample by setting all sample markers to 0 */ + sam->start = sam->end = sam->loopstart = sam->loopend = 0; + + return (OK); + } + else if (sam->loopend > sam->end || sam->loopstart >= sam->loopend + || sam->loopstart <= sam->start) + { /* loop is fowled?? (cluck cluck :) */ + /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */ + if ((sam->end - sam->start) >= 20) + { + sam->loopstart = sam->start + 8; + sam->loopend = sam->end - 8; + } + else + { /* loop is fowled, sample is tiny (can't pad 8 samples) */ + sam->loopstart = sam->start + 1; + sam->loopend = sam->end - 1; + } + } + + /* convert sample end, loopstart, loopend to offsets from sam->start */ + sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */ + sam->loopstart -= sam->start; + sam->loopend -= sam->start; + + p = fluid_list_next (p); + } + + return (OK); +} + +/*=================================sfont.c======================== + Smurf SoundFont Editor + ================================================================*/ + + +/* optimum chunk area sizes (could be more optimum) */ +#define PRESET_CHUNK_OPTIMUM_AREA 256 +#define INST_CHUNK_OPTIMUM_AREA 256 +#define SAMPLE_CHUNK_OPTIMUM_AREA 256 +#define ZONE_CHUNK_OPTIMUM_AREA 256 +#define MOD_CHUNK_OPTIMUM_AREA 256 +#define GEN_CHUNK_OPTIMUM_AREA 256 + +unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4, + Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0 +}; + +unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, + Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs, + Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, + Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass, + Gen_OverrideRootKey, 0 +}; + +void +sfont_close (SFData * sf) +{ + if (sf->sffd) + fclose (sf->sffd); + + sfont_free_data (sf); +} + +/* delete a sound font structure */ +void +sfont_free_data (SFData * sf) +{ + fluid_list_t *p, *p2; + + if (sf->fname) + free (sf->fname); + + p = sf->info; + while (p) + { + free (p->data); + p = fluid_list_next (p); + } + delete_fluid_list(sf->info); + sf->info = NULL; + + p = sf->preset; + while (p) + { /* loop over presets */ + p2 = ((SFPreset *) (p->data))->zone; + while (p2) + { /* loop over preset's zones */ + sfont_free_zone (p2->data); + p2 = fluid_list_next (p2); + } /* free preset's zone list */ + delete_fluid_list (((SFPreset *) (p->data))->zone); + FLUID_FREE (p->data); /* free preset chunk */ + p = fluid_list_next (p); + } + delete_fluid_list (sf->preset); + sf->preset = NULL; + + p = sf->inst; + while (p) + { /* loop over instruments */ + p2 = ((SFInst *) (p->data))->zone; + while (p2) + { /* loop over inst's zones */ + sfont_free_zone (p2->data); + p2 = fluid_list_next (p2); + } /* free inst's zone list */ + delete_fluid_list (((SFInst *) (p->data))->zone); + FLUID_FREE (p->data); + p = fluid_list_next (p); + } + delete_fluid_list (sf->sample); + sf->sample = NULL; +} + +/* free all elements of a zone (Preset or Instrument) */ +void +sfont_free_zone (SFZone * zone) +{ + fluid_list_t *p; + + if (!zone) + return; + + p = zone->gen; + while (p) + { /* Free gen chunks for this zone */ + if (p->data) + FLUID_FREE (p->data); + p = fluid_list_next (p); + } + delete_fluid_list (zone->gen); /* free genlist */ + + p = zone->mod; + while (p) + { /* Free mod chunks for this zone */ + if (p->data) + FLUID_FREE (p->data); + p = fluid_list_next (p); + } + delete_fluid_list (zone->mod); /* free modlist */ + + FLUID_FREE (zone); /* free zone chunk */ +} + +/* preset sort function, first by bank, then by preset # */ +int +sfont_preset_compare_func (void* a, void* b) +{ + int aval, bval; + + aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum; + bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum; + + return (aval - bval); +} + +/* delete zone from zone list */ +void +sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone) +{ + *zlist = fluid_list_remove (*zlist, (void*) zone); + sfont_free_zone (zone); +} + +/* Find generator in gen list */ +fluid_list_t * +gen_inlist (int gen, fluid_list_t * genlist) +{ /* is generator in gen list? */ + fluid_list_t *p; + + p = genlist; + while (p) + { + if (p->data == NULL) + return (NULL); + if (gen == ((SFGen *) p->data)->id) + break; + p = fluid_list_next (p); + } + return (p); +} + +/* check validity of instrument generator */ +int +gen_valid (int gen) +{ /* is generator id valid? */ + int i = 0; + + if (gen > Gen_MaxValid) + return (FALSE); + while (badgen[i] && badgen[i] != gen) + i++; + return (badgen[i] == 0); +} + +/* check validity of preset generator */ +int +gen_validp (int gen) +{ /* is preset generator valid? */ + int i = 0; + + if (!gen_valid (gen)) + return (FALSE); + while (badpgen[i] && badpgen[i] != (unsigned short) gen) + i++; + return (badpgen[i] == 0); +} + +/*================================util.c===========================*/ + +/* Logging function, returns FAIL to use as a return value in calling funcs */ +int +gerr (int ev, char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vprintf(fmt, args); + va_end (args); + + printf("\n"); + + return (FAIL); +} + +int +safe_fread (void *buf, int count, FILE * fd) +{ + if (fread (buf, count, 1, fd) != 1) + { /* size_t = count, nmemb = 1 */ + if (feof (fd)) + gerr (ErrEof, _("EOF while attemping to read %d bytes"), count); + else + FLUID_LOG (FLUID_ERR, _("File read failed")); + return (FAIL); + } + return (OK); +} + +int +safe_fseek (FILE * fd, long ofs, int whence) +{ + if (fseek (fd, ofs, whence) == -1) { + FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence); + return (FAIL); + } + return (OK); +} + +void * +safe_malloc (size_t size) +{ + void *ptr; + + if (!(ptr = malloc (size))) + FLUID_LOG (FLUID_ERR, _("Attempted to allocate %d bytes"), (int) size); + return (ptr); +} diff --git a/src/libs/fluidsynth/src/fluid_defsfont.h b/src/libs/fluidsynth/src/fluid_defsfont.h new file mode 100644 index 0000000000..844347fc88 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_defsfont.h @@ -0,0 +1,608 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_DEFSFONT_H +#define _FLUID_DEFSFONT_H + + +#include "fluidsynth.h" +#include "fluidsynth_priv.h" +#include "fluid_list.h" + + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +/*-----------------------------------sfont.h----------------------------*/ + +#define SF_SAMPMODES_LOOP 1 +#define SF_SAMPMODES_UNROLL 2 + +#define SF_MIN_SAMPLERATE 400 +#define SF_MAX_SAMPLERATE 50000 + +#define SF_MIN_SAMPLE_LENGTH 32 + +/* Sound Font structure defines */ + +typedef struct _SFVersion +{ /* version structure */ + unsigned short major; + unsigned short minor; +} +SFVersion; + +typedef struct _SFMod +{ /* Modulator structure */ + unsigned short src; /* source modulator */ + unsigned short dest; /* destination generator */ + signed short amount; /* signed, degree of modulation */ + unsigned short amtsrc; /* second source controls amnt of first */ + unsigned short trans; /* transform applied to source */ +} +SFMod; + +typedef union _SFGenAmount +{ /* Generator amount structure */ + signed short sword; /* signed 16 bit value */ + unsigned short uword; /* unsigned 16 bit value */ + struct + { + unsigned char lo; /* low value for ranges */ + unsigned char hi; /* high value for ranges */ + } + range; +} +SFGenAmount; + +typedef struct _SFGen +{ /* Generator structure */ + unsigned short id; /* generator ID */ + SFGenAmount amount; /* generator value */ +} +SFGen; + +typedef struct _SFZone +{ /* Sample/instrument zone structure */ + fluid_list_t *instsamp; /* instrument/sample pointer for zone */ + fluid_list_t *gen; /* list of generators */ + fluid_list_t *mod; /* list of modulators */ +} +SFZone; + +typedef struct _SFSample +{ /* Sample structure */ + char name[21]; /* Name of sample */ + unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */ + unsigned int start; /* Offset in sample area to start of sample */ + unsigned int end; /* Offset from start to end of sample, + this is the last point of the + sample, the SF spec has this as the + 1st point after, corrected on + load/save */ + unsigned int loopstart; /* Offset from start to start of loop */ + unsigned int loopend; /* Offset from start to end of loop, + marks the first point after loop, + whose sample value is ideally + equivalent to loopstart */ + unsigned int samplerate; /* Sample rate recorded at */ + unsigned char origpitch; /* root midi key number */ + signed char pitchadj; /* pitch correction in cents */ + unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ +} +SFSample; + +typedef struct _SFInst +{ /* Instrument structure */ + char name[21]; /* Name of instrument */ + fluid_list_t *zone; /* list of instrument zones */ +} +SFInst; + +typedef struct _SFPreset +{ /* Preset structure */ + char name[21]; /* preset name */ + unsigned short prenum; /* preset number */ + unsigned short bank; /* bank number */ + unsigned int libr; /* Not used (preserved) */ + unsigned int genre; /* Not used (preserved) */ + unsigned int morph; /* Not used (preserved) */ + fluid_list_t *zone; /* list of preset zones */ +} +SFPreset; + +/* NOTE: sffd is also used to determine if sound font is new (NULL) */ +typedef struct _SFData +{ /* Sound font data structure */ + SFVersion version; /* sound font version */ + SFVersion romver; /* ROM version */ + unsigned int samplepos; /* position within sffd of the sample chunk */ + unsigned int samplesize; /* length within sffd of the sample chunk */ + char *fname; /* file name */ + FILE *sffd; /* loaded sfont file descriptor */ + fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ + fluid_list_t *preset; /* linked list of preset info */ + fluid_list_t *inst; /* linked list of instrument info */ + fluid_list_t *sample; /* linked list of sample info */ +} +SFData; + +/* sf file chunk IDs */ +enum +{ UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, + INFO_ID, SDTA_ID, PDTA_ID, /* info/sample/preset */ + + IFIL_ID, ISNG_ID, INAM_ID, IROM_ID, /* info ids (1st byte of info strings) */ + IVER_ID, ICRD_ID, IENG_ID, IPRD_ID, /* more info ids */ + ICOP_ID, ICMT_ID, ISFT_ID, /* and yet more info ids */ + + SNAM_ID, SMPL_ID, /* sample ids */ + PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, /* preset ids */ + IHDR_ID, IBAG_ID, IMOD_ID, IGEN_ID, /* instrument ids */ + SHDR_ID /* sample info */ +}; + +/* generator types */ +typedef enum +{ Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, + Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch, + Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ, + Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs, + Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan, + Gen_Unused2, Gen_Unused3, Gen_Unused4, + Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq, + Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay, + Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold, + Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack, + Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease, + Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument, + Gen_Reserved1, Gen_KeyRange, Gen_VelRange, + Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, + Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs, + Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes, + Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey, + Gen_Dummy +} +Gen_Type; + +#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */ +#define Gen_Count Gen_Dummy /* count of generators */ +#define GenArrSize sizeof(SFGenAmount)*Gen_Count /* gen array size */ + +/* generator unit type */ +typedef enum +{ + None, /* No unit type */ + Unit_Smpls, /* in samples */ + Unit_32kSmpls, /* in 32k samples */ + Unit_Cent, /* in cents (1/100th of a semitone) */ + Unit_HzCent, /* in Hz Cents */ + Unit_TCent, /* in Time Cents */ + Unit_cB, /* in centibels (1/100th of a decibel) */ + Unit_Percent, /* in percentage */ + Unit_Semitone, /* in semitones */ + Unit_Range /* a range of values */ +} +Gen_Unit; + +/* global data */ + +extern unsigned short badgen[]; /* list of bad generators */ +extern unsigned short badpgen[]; /* list of bad preset generators */ + +/* functions */ +void sfont_init_chunks (void); + +void sfont_close (SFData * sf); +void sfont_free_data (SFData * sf); +void sfont_free_zone (SFZone * zone); +int sfont_preset_compare_func (void* a, void* b); + +void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone); + +fluid_list_t *gen_inlist (int gen, fluid_list_t * genlist); +int gen_valid (int gen); +int gen_validp (int gen); + + +/*-----------------------------------sffile.h----------------------------*/ +/* + File structures and routines (used to be in sffile.h) +*/ + +#define CHNKIDSTR(id) &idlist[(id - 1) * 4] + +/* sfont file chunk sizes */ +#define SFPHDRSIZE 38 +#define SFBAGSIZE 4 +#define SFMODSIZE 10 +#define SFGENSIZE 4 +#define SFIHDRSIZE 22 +#define SFSHDRSIZE 46 + +/* sfont file data structures */ +typedef struct _SFChunk +{ /* RIFF file chunk structure */ + unsigned int id; /* chunk id */ + unsigned int size; /* size of the following chunk */ +} +SFChunk; + +typedef struct _SFPhdr +{ + unsigned char name[20]; /* preset name */ + unsigned short preset; /* preset number */ + unsigned short bank; /* bank number */ + unsigned short pbagndx; /* index into preset bag */ + unsigned int library; /* just for preserving them */ + unsigned int genre; /* Not used */ + unsigned int morphology; /* Not used */ +} +SFPhdr; + +typedef struct _SFBag +{ + unsigned short genndx; /* index into generator list */ + unsigned short modndx; /* index into modulator list */ +} +SFBag; + +typedef struct _SFIhdr +{ + char name[20]; /* Name of instrument */ + unsigned short ibagndx; /* Instrument bag index */ +} +SFIhdr; + +typedef struct _SFShdr +{ /* Sample header loading struct */ + char name[20]; /* Sample name */ + unsigned int start; /* Offset to start of sample */ + unsigned int end; /* Offset to end of sample */ + unsigned int loopstart; /* Offset to start of loop */ + unsigned int loopend; /* Offset to end of loop */ + unsigned int samplerate; /* Sample rate recorded at */ + unsigned char origpitch; /* root midi key number */ + signed char pitchadj; /* pitch correction in cents */ + unsigned short samplelink; /* Not used */ + unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ +} +SFShdr; + +/* data */ +extern char idlist[]; + +/* functions */ +SFData *sfload_file (const char * fname); + + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +/* Provide definitions for some commonly used macros. + * Some of them are only provided if they haven't already + * been defined. It is assumed that if they are already + * defined then the current definition is correct. + */ +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define GPOINTER_TO_INT(p) ((int) (p)) +#define GINT_TO_POINTER(i) ((void *) (i)) + +char* g_strdup (const char *str); + + + + + +/* Provide simple macro statement wrappers (adapted from Perl): + * G_STMT_START { statements; } G_STMT_END; + * can be used as a single statement, as in + * if (x) G_STMT_START { ... } G_STMT_END; else ... + * + * For gcc we will wrap the statements within `({' and `})' braces. + * For SunOS they will be wrapped within `if (1)' and `else (void) 0', + * and otherwise within `do' and `while (0)'. + */ +#if !(defined (G_STMT_START) && defined (G_STMT_END)) +# if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus) +# define G_STMT_START (void)( +# define G_STMT_END ) +# else +# if (defined (sun) || defined (__sun__)) +# define G_STMT_START if (1) +# define G_STMT_END else (void)0 +# else +# define G_STMT_START do +# define G_STMT_END while (0) +# endif +# endif +#endif + + +/* Basic bit swapping functions + */ +#define GUINT16_SWAP_LE_BE_CONSTANT(val) ((unsigned short) ( \ + (((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | \ + (((unsigned short) (val) & (unsigned short) 0xff00U) >> 8))) +#define GUINT32_SWAP_LE_BE_CONSTANT(val) ((unsigned int) ( \ + (((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \ + (((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \ + (((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \ + (((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24))) + +#define GUINT16_SWAP_LE_BE(val) (GUINT16_SWAP_LE_BE_CONSTANT (val)) +#define GUINT32_SWAP_LE_BE(val) (GUINT32_SWAP_LE_BE_CONSTANT (val)) + +#define GINT16_TO_LE(val) ((signed short) (val)) +#define GUINT16_TO_LE(val) ((unsigned short) (val)) +#define GINT16_TO_BE(val) ((signed short) GUINT16_SWAP_LE_BE (val)) +#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val)) +#define GINT32_TO_LE(val) ((signed int) (val)) +#define GUINT32_TO_LE(val) ((unsigned int) (val)) +#define GINT32_TO_BE(val) ((signed int) GUINT32_SWAP_LE_BE (val)) +#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val)) + +/* The G*_TO_?E() macros are defined in glibconfig.h. + * The transformation is symmetric, so the FROM just maps to the TO. + */ +#define GINT16_FROM_LE(val) (GINT16_TO_LE (val)) +#define GUINT16_FROM_LE(val) (GUINT16_TO_LE (val)) +#define GINT16_FROM_BE(val) (GINT16_TO_BE (val)) +#define GUINT16_FROM_BE(val) (GUINT16_TO_BE (val)) +#define GINT32_FROM_LE(val) (GINT32_TO_LE (val)) +#define GUINT32_FROM_LE(val) (GUINT32_TO_LE (val)) +#define GINT32_FROM_BE(val) (GINT32_TO_BE (val)) +#define GUINT32_FROM_BE(val) (GUINT32_TO_BE (val)) + + +/*-----------------------------------util.h----------------------------*/ +/* + Utility functions (formerly in util.h) + */ +#define FAIL 0 +#define OK 1 + +enum +{ ErrWarn, ErrFatal, ErrStatus, ErrCorr, ErrEof, ErrMem, Errno, + ErrRead, ErrWrite +}; + +#define ErrMax ErrWrite +#define ErrnoStart Errno +#define ErrnoEnd ErrWrite + +int gerr (int ev, char * fmt, ...); +void *safe_malloc (size_t size); +int safe_fread (void *buf, int count, FILE * fd); +int safe_fwrite (void *buf, int count, FILE * fd); +int safe_fseek (FILE * fd, long ofs, int whence); + + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + + + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_defsfont_t fluid_defsfont_t; +typedef struct _fluid_defpreset_t fluid_defpreset_t; +typedef struct _fluid_preset_zone_t fluid_preset_zone_t; +typedef struct _fluid_inst_t fluid_inst_t; +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; + +/* + + Public interface + + */ + +fluid_sfloader_t* new_fluid_defsfloader(void); +int delete_fluid_defsfloader(fluid_sfloader_t* loader); +fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename); + + +int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont); +char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont); +fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont); +int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); + + +int fluid_defpreset_preset_delete(fluid_preset_t* preset); +char* fluid_defpreset_preset_get_name(fluid_preset_t* preset); +int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset); +int fluid_defpreset_preset_get_num(fluid_preset_t* preset); +int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + +/* + * fluid_defsfont_t + */ +struct _fluid_defsfont_t +{ + char* filename; /* the filename of this soundfont */ + unsigned int samplepos; /* the position in the file at which the sample data starts */ + unsigned int samplesize; /* the size of the sample data */ + short* sampledata; /* the sample data, loaded in ram */ + fluid_list_t* sample; /* the samples in this soundfont */ + fluid_defpreset_t* preset; /* the presets of this soundfont */ + + fluid_preset_t iter_preset; /* preset interface used in the iteration */ + fluid_defpreset_t* iter_cur; /* the current preset in the iteration */ +}; + + +fluid_defsfont_t* new_fluid_defsfont(void); +int delete_fluid_defsfont(fluid_defsfont_t* sfont); +int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file); +char* fluid_defsfont_get_name(fluid_defsfont_t* sfont); +fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont); +int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset); +int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont); +int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample); +int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset); +fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s); + + +/* + * fluid_preset_t + */ +struct _fluid_defpreset_t +{ + fluid_defpreset_t* next; + fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */ + char name[21]; /* the name of the preset */ + unsigned int bank; /* the bank number */ + unsigned int num; /* the preset number */ + fluid_preset_zone_t* global_zone; /* the global zone of the preset */ + fluid_preset_zone_t* zone; /* the chained list of preset zones */ +}; + +fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont); +int delete_fluid_defpreset(fluid_defpreset_t* preset); +fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset); +int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont); +int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); +int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); +fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset); +fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset); +int fluid_defpreset_get_banknum(fluid_defpreset_t* preset); +int fluid_defpreset_get_num(fluid_defpreset_t* preset); +char* fluid_defpreset_get_name(fluid_defpreset_t* preset); +int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + +/* + * fluid_preset_zone + */ +struct _fluid_preset_zone_t +{ + fluid_preset_zone_t* next; + char* name; + fluid_inst_t* inst; + int keylo; + int keyhi; + int vello; + int velhi; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t * mod; /* List of modulators */ +}; + +fluid_preset_zone_t* new_fluid_preset_zone(char* name); +int delete_fluid_preset_zone(fluid_preset_zone_t* zone); +fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset); +int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont); +int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel); +fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone); + +/* + * fluid_inst_t + */ +struct _fluid_inst_t +{ + char name[21]; + fluid_inst_zone_t* global_zone; + fluid_inst_zone_t* zone; +}; + +fluid_inst_t* new_fluid_inst(void); +int delete_fluid_inst(fluid_inst_t* inst); +int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont); +int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); +int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); +fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst); +fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst); + +/* + * fluid_inst_zone_t + */ +struct _fluid_inst_zone_t +{ + fluid_inst_zone_t* next; + char* name; + fluid_sample_t* sample; + int keylo; + int keyhi; + int vello; + int velhi; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t * mod; /* List of modulators */ +}; + +fluid_inst_zone_t* new_fluid_inst_zone(char* name); +int delete_fluid_inst_zone(fluid_inst_zone_t* zone); +fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone); +int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); +int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel); +fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); + + + +fluid_sample_t* new_fluid_sample(void); +int delete_fluid_sample(fluid_sample_t* sample); +int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont); +int fluid_sample_in_rom(fluid_sample_t* sample); + + +#endif /* _FLUID_SFONT_H */ diff --git a/src/libs/fluidsynth/src/fluid_dsp_core.c b/src/libs/fluidsynth/src/fluid_dsp_core.c new file mode 100644 index 0000000000..4e09047eda --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_dsp_core.c @@ -0,0 +1,26 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifdef ENABLE_SSE +#include "fluid_dsp_sse.c" +#else +/* #include "fluid_dsp_simple.c" */ +#include "fluid_dsp_float.c" +#endif diff --git a/src/libs/fluidsynth/src/fluid_dsp_float.c b/src/libs/fluidsynth/src/fluid_dsp_float.c new file mode 100644 index 0000000000..e251603f7f --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_dsp_float.c @@ -0,0 +1,281 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* Purpose: + * Low-level voice processing: + * + * - interpolates (obtains values between the samples of the original waveform data) + * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) + * - mixes the processed sample to left and right output using the pan setting + * - sends the processed sample to chorus and reverb + * + * + * This file does -not- generate an object file. + * Instead, it is #included in several places in fluid_voice.c. + * The motivation for this is + * - Calling it as a subroutine may be time consuming, especially with optimization off + * - The previous implementation as a macro was clumsy to handle + * + * + * Fluid_voice.c sets a couple of variables before #including this: + * - dsp_data: Pointer to the original waveform data + * - dsp_left_buf: The generated signal goes here, left channel + * - dsp_right_buf: right channel + * - dsp_reverb_buf: Send to reverb unit + * - dsp_chorus_buf: Send to chorus unit + * - dsp_start: Start processing at this output buffer index + * - dsp_end: End processing just before this output buffer index + * - dsp_a1: Coefficient for the filter + * - dsp_a2: same + * - dsp_b0: same + * - dsp_b1: same + * - dsp_b2: same + * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use + * the filter at all. If it is left at its default setting + * of roughly 20 kHz, there is no need to apply filterling.) + * - dsp_interp_method: Which interpolation method to use. + * - voice holds the voice structure + * + * Some variables are set and modified: + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_phase_fractional: The fractional part of dsp_phase + * - dsp_coeff: A table of four coefficients, depending on the fractional phase. + * Used to interpolate between samples. + * - dsp_process_buffer: Holds the processed signal between stages + * - dsp_centernode: delay line for the IIR filter + * - dsp_hist1: same + * - dsp_hist2: same + * + */ + +/* Purpose: + * zap_almost_zero will return a number, as long as its + * absolute value is over a certain threshold. Otherwise 0. See + * fluid_rev.c for documentation (denormal numbers) + */ + +# if defined(WITH_FLOAT) +# define zap_almost_zero(_sample) \ + ((((*(unsigned int*)&(_sample))&0x7f800000) < 0x08000000)? 0.0f : (_sample)) +# else +/* 1e-20 was chosen as an arbitrary (small) threshold. */ +#define zap_almost_zero(_sample) ((abs(_sample) < 1e-20)? 0.0f : (_sample)) +#endif + +/* Interpolation (find a value between two samples of the original waveform) */ + +if ((fluid_phase_fract(dsp_phase) == 0) + && (fluid_phase_fract(dsp_phase_incr) == 0) + && (fluid_phase_index(dsp_phase_incr) == 1)) { + + /* Check for a special case: The current phase falls directly on an + * original sample. Also, the stepsize per output sample is exactly + * one sample, no fractional part. In other words: The sample is + * played back at normal phase and root pitch. => No interpolation + * needed. + */ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* Mix to the buffer and advance the phase by one sample */ + dsp_buf[dsp_i] = dsp_amp * dsp_data[fluid_phase_index_plusplus(dsp_phase)]; + dsp_amp += dsp_amp_incr; + } + +} else { + + /* wave table interpolation: Choose the interpolation method */ + + switch(dsp_interp_method){ + case FLUID_INTERP_NONE: + /* No interpolation. Just take the sample, which is closest to + * the playback pointer. Questionable quality, but very + * efficient. */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + }; + break; + + case FLUID_INTERP_LINEAR: + /* Straight line interpolation. */ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_coeff = &interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_buf[dsp_i] = (dsp_amp * + (dsp_coeff->a0 * dsp_data[dsp_phase_index] + + dsp_coeff->a1 * dsp_data[dsp_phase_index+1])); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + }; + break; + + case FLUID_INTERP_4THORDER: + default: + /* Default interpolation loop using floats */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_buf[dsp_i] = (dsp_amp * + (dsp_coeff->a0 * dsp_data[dsp_phase_index] + + dsp_coeff->a1 * dsp_data[dsp_phase_index+1] + + dsp_coeff->a2 * dsp_data[dsp_phase_index+2] + + dsp_coeff->a3 * dsp_data[dsp_phase_index+3])); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + } + break; + + + case FLUID_INTERP_7THORDER: + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + int fract = fluid_phase_fract_to_tablerow(dsp_phase); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_buf[dsp_i] = (dsp_amp * + (sinc_table7[0][fract] * (fluid_real_t) dsp_data[dsp_phase_index] + + sinc_table7[1][fract] * (fluid_real_t) dsp_data[dsp_phase_index+1] + + sinc_table7[2][fract] * (fluid_real_t) dsp_data[dsp_phase_index+2] + + sinc_table7[3][fract] * (fluid_real_t) dsp_data[dsp_phase_index+3] + + sinc_table7[4][fract] * (fluid_real_t) dsp_data[dsp_phase_index+4] + + sinc_table7[5][fract] * (fluid_real_t) dsp_data[dsp_phase_index+5] + + sinc_table7[6][fract] * (fluid_real_t) dsp_data[dsp_phase_index+6])); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + } + break; + + } /* switch interpolation method */ +} /* If interpolation is needed */ + +/* filter (implement the voice filter according to Soundfont standard) */ +if (dsp_use_filter_flag) { + + /* Check for denormal number (too close to zero) once in a + * while. This is not a big concern here - why would someone play a + * sample with an empty tail? */ + dsp_hist1 = zap_almost_zero(dsp_hist1); + + /* Two versions of the filter loop. One, while the filter is + * changing towards its new setting. The other, if the filter + * doesn't change. + */ + + if (dsp_filter_coeff_incr_count > 0) { + /* The increment is added to each filter coefficient + filter_coeff_incr_count times. */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + if (dsp_filter_coeff_incr_count-- > 0){ + dsp_a1 += dsp_a1_incr; + dsp_a2 += dsp_a2_incr; + dsp_b02 += dsp_b02_incr; + dsp_b1 += dsp_b1_incr; + } + } /* for dsp_i */ + + } else { + + /* The filter parameters are constant. This is duplicated to save + * time. */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + } + } /* if filter is fixed */ +} /* if filter is enabled */ + + + +/* pan (Copy the signal to the left and right output buffer) The voice + * panning generator has a range of -500 .. 500. If it is centered, + * it's close to 0. voice->amp_left and voice->amp_right are then the + * same, and we can save one multiplication per voice and sample. + */ +if ((-0.5 < voice->pan) && (voice->pan < 0.5)) { + + /* The voice is centered. Use voice->amp_left twice. */ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + float v = voice->amp_left * dsp_buf[dsp_i]; + dsp_left_buf[dsp_i] += v; + dsp_right_buf[dsp_i] += v; + } + +} else { + + /* The voice is not centered. For stereo samples, one of the + * amplitudes will be zero. */ + if (voice->amp_left != 0.0){ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i]; + } + } + if (voice->amp_right != 0.0){ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i]; + } + } +} + +/* reverb send. Buffer may be NULL. */ +if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0)) { + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i]; + } +} + +/* chorus send. Buffer may be NULL. */ +if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0)) { + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i]; + } +} diff --git a/src/libs/fluidsynth/src/fluid_dsp_simple.c b/src/libs/fluidsynth/src/fluid_dsp_simple.c new file mode 100644 index 0000000000..6aa3cdf432 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_dsp_simple.c @@ -0,0 +1,120 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* Purpose: + * Low-level voice processing: + * + * - interpolates (obtains values between the samples of the original waveform data) + * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) + * - mixes the processed sample to left and right output using the pan setting + * - sends the processed sample to chorus and reverb + * + * + * This file does -not- generate an object file. + * Instead, it is #included in several places in fluid_voice.c. + * The motivation for this is + * - Calling it as a subroutine may be time consuming, especially with optimization off + * - The previous implementation as a macro was clumsy to handle + * + * + * Fluid_voice.c sets a couple of variables before #including this: + * - dsp_data: Pointer to the original waveform data + * - dsp_left_buf: The generated signal goes here, left channel + * - dsp_right_buf: right channel + * - dsp_reverb_buf: Send to reverb unit + * - dsp_chorus_buf: Send to chorus unit + * - dsp_start: Start processing at this output buffer index + * - dsp_end: End processing just before this output buffer index + * - dsp_a1: Coefficient for the filter + * - dsp_a2: same + * - dsp_b0: same + * - dsp_b1: same + * - dsp_b2: same + * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use + * the filter at all. If it is left at its default setting + * of roughly 20 kHz, there is no need to apply filterling.) + * - dsp_interp_method: Which interpolation method to use. + * - voice holds the voice structure + * + * Some variables are set and modified: + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_phase_fractional: The fractional part of dsp_phase + * - dsp_coeff: A table of four coefficients, depending on the fractional phase. + * Used to interpolate between samples. + * - dsp_process_buffer: Holds the processed signal between stages + * - dsp_centernode: delay line for the IIR filter + * - dsp_hist1: same + * - dsp_hist2: same + * + */ + + +/* Nonoptimized DSP loop */ +#warning "This code is meant for experiments only."; + +/* wave table interpolation */ +for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + + dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_sample = (dsp_amp * + (dsp_coeff->a0 * dsp_data[dsp_phase_index] + + dsp_coeff->a1 * dsp_data[dsp_phase_index+1] + + dsp_coeff->a2 * dsp_data[dsp_phase_index+2] + + dsp_coeff->a3 * dsp_data[dsp_phase_index+3])); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_amp += dsp_amp_incr; + + /* filter */ + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + /* pan */ + dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample; + dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample; + + /* reverb */ + if (dsp_reverb_buf){ + dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample; + } + + /* chorus */ + if (dsp_chorus_buf){ + dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample; + } +} + diff --git a/src/libs/fluidsynth/src/fluid_dsp_sse.c b/src/libs/fluidsynth/src/fluid_dsp_sse.c new file mode 100644 index 0000000000..f2b8fdeb77 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_dsp_sse.c @@ -0,0 +1,387 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* Purpose: + * Low-level voice processing: + * + * - interpolates (obtains values between the samples of the original waveform data) + * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) + * - mixes the processed sample to left and right output using the pan setting + * - sends the processed sample to chorus and reverb + * + * + * This file does -not- generate an object file. + * Instead, it is #included in several places in fluid_voice.c. + * The motivation for this is + * - Calling it as a subroutine may be time consuming, especially with optimization off + * - The previous implementation as a macro was clumsy to handle + * + * + * Fluid_voice.c sets a couple of variables before #including this: + * - dsp_data: Pointer to the original waveform data + * - dsp_left_buf: The generated signal goes here, left channel + * - dsp_right_buf: right channel + * - dsp_reverb_buf: Send to reverb unit + * - dsp_chorus_buf: Send to chorus unit + * - dsp_start: Start processing at this output buffer index + * - dsp_end: End processing just before this output buffer index + * - dsp_a1: Coefficient for the filter + * - dsp_a2: same + * - dsp_b0: same + * - dsp_b1: same + * - dsp_b2: same + * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use + * the filter at all. If it is left at its default setting + * of roughly 20 kHz, there is no need to apply filterling.) + * - dsp_interp_method: Which interpolation method to use. + * - voice holds the voice structure + * + * Some variables are set and modified: + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_phase_fractional: The fractional part of dsp_phase + * - dsp_coeff: A table of four coefficients, depending on the fractional phase. + * Used to interpolate between samples. + * - dsp_process_buffer: Holds the processed signal between stages + * - dsp_centernode: delay line for the IIR filter + * - dsp_hist1: same + * - dsp_hist2: same + * + */ + +/* Purpose: + * zap_almost_zero will return a number, as long as its + * absolute value is over a certain threshold. Otherwise 0. See + * fluid_rev.c for documentation (denormal numbers) + */ + +# if defined(WITH_FLOAT) +# define zap_almost_zero(_sample) \ + ((((*(unsigned int*)&(_sample))&0x7f800000) < 0x08000000)? 0.0f : (_sample)) +# else +/* 1e-20 was chosen as an arbitrary (small) threshold. */ +#define zap_almost_zero(_sample) ((abs(_sample) < 1e-20)? 0.0f : (_sample)) +#endif + +/* Interpolation (find a value between two samples of the original waveform) */ + +if ((fluid_phase_fract(dsp_phase) == 0) + && (fluid_phase_fract(dsp_phase_incr) == 0) + && (fluid_phase_index(dsp_phase_incr) == 1)) { + + /* Check for a special case: The current phase falls directly on an + * original sample. Also, the stepsize per output sample is exactly + * one sample, no fractional part. In other words: The sample is + * played back at normal phase and root pitch. => No interpolation + * needed. + */ + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* Mix to the buffer and advance the phase by one sample */ + dsp_buf[dsp_i] = dsp_amp * dsp_data[fluid_phase_index_plusplus(dsp_phase)]; + dsp_amp += dsp_amp_incr; + } + +} else { + + /* wave table interpolation: Choose the interpolation method */ + /* !!! SSE interpolation is less efficient that normal interpolation. */ + + + /* Initialize amplitude increase */ + sse_b->sf[0] = sse_b->sf[1] = sse_b->sf[2] = sse_b->sf[3] = 4.*dsp_amp_incr; + + /* Initialize amplitude => xmm7 + * The amplitude is kept in xmm7 throughout the whole process + */ + + sse_a->sf[0]=sse_a->sf[1]=sse_a->sf[2]=sse_a->sf[3]=dsp_amp; + sse_a->sf[1] += dsp_amp_incr; + sse_a->sf[2] += 2.* dsp_amp_incr; + sse_a->sf[3] += 3.* dsp_amp_incr; + movaps_m2r(*sse_a,xmm7); + + /* Where to store the result */ + sse_dest=(sse_t*)&dsp_buf[0]; + for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i += 4) { + /* Note / fixme: The coefficients are first copied to + * sse_c, then to the xmm register. Can't get it through + * the compiler differently... */ + + /* Load the four source samples for the 1st output sample */ + dsp_phase_index = fluid_phase_index(dsp_phase); + sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index]; + sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1]; + sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2]; + sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3]; + movaps_m2r(*sse_a,xmm0); + *sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)]; + mulps_m2r(*sse_c,xmm0); + + fluid_phase_incr(dsp_phase, dsp_phase_incr); + + /* Load the four source samples for the 2nd output sample */ + dsp_phase_index = fluid_phase_index(dsp_phase); + sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index]; + sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1]; + sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2]; + sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3]; + movaps_m2r(*sse_a,xmm1); + *sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)]; + mulps_m2r(*sse_c,xmm1); + + fluid_phase_incr(dsp_phase, dsp_phase_incr); + + /* Load the four source samples for the 3rd output sample */ + dsp_phase_index = fluid_phase_index(dsp_phase); + sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index]; + sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1]; + sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2]; + sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3]; + movaps_m2r(*sse_a,xmm2); + *sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)]; + mulps_m2r(*sse_c,xmm2); + + fluid_phase_incr(dsp_phase, dsp_phase_incr); + + /* Load the four source samples for the 4th output sample */ + dsp_phase_index = fluid_phase_index(dsp_phase); + sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index]; + sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1]; + sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2]; + sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3]; + movaps_m2r(*sse_a,xmm3); + *sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)]; + mulps_m2r(*sse_c,xmm3); + + fluid_phase_incr(dsp_phase, dsp_phase_incr); + +#if 0 + /*Testcase for horizontal add */ + sse_a->sf[0]=0.1;sse_a->sf[1]=0.01;sse_a->sf[2]=0.001;sse_a->sf[3]=0.0001; + movaps_m2r(*sse_a,xmm0); + sse_a->sf[0]=0.2;sse_a->sf[1]=0.02;sse_a->sf[2]=0.002;sse_a->sf[3]=0.0002; + movaps_m2r(*sse_a,xmm1); + sse_a->sf[0]=0.3;sse_a->sf[1]=0.03;sse_a->sf[2]=0.003;sse_a->sf[3]=0.0003; + movaps_m2r(*sse_a,xmm2); + sse_a->sf[0]=0.4;sse_a->sf[1]=0.04;sse_a->sf[2]=0.004;sse_a->sf[3]=0.0004; + movaps_m2r(*sse_a,xmm3); +#endif /* #if 0 */ + +#if 1 + /* Horizontal add + * xmm4[0]:=xmm0[0]+xmm1[0]+xmm2[0]+xmm3[0] + * xmm4[1]:=xmm0[1]+xmm1[1]+xmm2[1]+xmm3[1] + * etc. + * The only register, which is unused, is xmm7. + */ + movaps_r2r(xmm0,xmm5); + movaps_r2r(xmm2,xmm6); + movlhps_r2r(xmm1,xmm5); + movlhps_r2r(xmm3,xmm6); + movhlps_r2r(xmm0,xmm1); + movhlps_r2r(xmm2,xmm3); + addps_r2r(xmm1,xmm5); + addps_r2r(xmm3,xmm6); + movaps_r2r(xmm5,xmm4); + shufps_r2r(xmm6,xmm5,0xDD); + shufps_r2r(xmm6,xmm4,0x88); + addps_r2r(xmm5,xmm4); + +/* movaps_r2m(xmm4,*sse_a); */ +/* printf("xmm4 (Result): %f %f %f %f\n", */ +/* sse_a->sf[0], sse_a->sf[1], */ +/* sse_a->sf[2], sse_a->sf[3]); */ +#else + /* Add using normal FPU */ + movaps_r2m(xmm0,*sse_a); + sse_c->sf[0]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3]; + movaps_r2m(xmm1,*sse_a); + sse_c->sf[1]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3]; + movaps_r2m(xmm2,*sse_a); + sse_c->sf[2]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3]; + movaps_r2m(xmm3,*sse_a); + sse_c->sf[3]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3]; + movaps_m2r(*sse_c,xmm4); +#endif /* #if 1 */ + /* end horizontal add. Result in xmm6. */ + + + /* Multiply xmm4 with amplitude */ + mulps_r2r(xmm7,xmm4); + + /* Store the result */ + movaps_r2m(xmm4,*sse_dest); // ++ + + /* Advance the position in the output buffer */ + sse_dest++; + + /* Change the amplitude */ + addps_m2r(*sse_b,xmm7); + + } /* for dsp_i in steps of four */ + + movaps_r2m(xmm7,*sse_a); + + /* Retrieve the last amplitude value. */ + dsp_amp=sse_a->sf[3]; + + +} /* If interpolation is needed */ + +/* filter (implement the voice filter according to Soundfont standard) */ +if (dsp_use_filter_flag) { + + /* Check for denormal number (too close to zero) once in a + * while. This is not a big concern here - why would someone play a + * sample with an empty tail? */ + dsp_hist1 = zap_almost_zero(dsp_hist1); + + /* Two versions of the filter loop. One, while the filter is + * changing towards its new setting. The other, if the filter + * doesn't change. + */ + + if (dsp_filter_coeff_incr_count > 0) { + /* The increment is added to each filter coefficient + filter_coeff_incr_count times. */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + if (dsp_filter_coeff_incr_count-- > 0){ + dsp_a1 += dsp_a1_incr; + dsp_a2 += dsp_a2_incr; + dsp_b02 += dsp_b02_incr; + dsp_b1 += dsp_b1_incr; + } + } /* for dsp_i */ + + } else { + + /* The filter parameters are constant. This is duplicated to save + * time. */ + + for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + } + } /* if filter is fixed */ +} /* if filter is enabled */ + + +/* The following optimization will process a whole buffer using the + * SSE extension of the Pentium processor. + */ + +if (voice->amp_left != 0.0) { + sse_a->sf[0]=voice->amp_left; + sse_a->sf[1]=voice->amp_left; + sse_a->sf[2]=voice->amp_left; + sse_a->sf[3]=voice->amp_left; + movaps_m2r(*sse_a,xmm0); + + sse_src=(sse_t*)dsp_buf; + sse_dest=(sse_t*)&dsp_left_buf[0]; + for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) { + movaps_m2r(*sse_src,xmm4); /* Load original sample */ + mulps_r2r(xmm0,xmm4); /* Gain */ + sse_src++; + addps_m2r(*sse_dest,xmm4); /* Mix with buf */ + movaps_r2m(xmm4,*sse_dest); /* Store in buf */ + sse_dest++; + } +} + +if (voice->amp_right != 0.0){ + sse_a->sf[0]=voice->amp_right; + sse_a->sf[1]=voice->amp_right; + sse_a->sf[2]=voice->amp_right; + sse_a->sf[3]=voice->amp_right; + movaps_m2r(*sse_a,xmm0); + + sse_src=(sse_t*)dsp_buf; + sse_dest=(sse_t*)&dsp_right_buf[0]; + for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) { + movaps_m2r(*sse_src,xmm4); /* Load original sample */ + sse_src++; + mulps_r2r(xmm0,xmm4); /* Gain */ + addps_m2r(*sse_dest,xmm4); /* Mix with buf */ + movaps_r2m(xmm4,*sse_dest); /* Store in buf */ + sse_dest++; + } +} + +/* reverb send. Buffer may be NULL. */ +if (dsp_reverb_buf && voice->amp_reverb != 0.0){ + sse_a->sf[0]=voice->amp_reverb; + sse_a->sf[1]=voice->amp_reverb; + sse_a->sf[2]=voice->amp_reverb; + sse_a->sf[3]=voice->amp_reverb; + movaps_m2r(*sse_a,xmm0); + + sse_src=(sse_t*)dsp_buf; + sse_dest=(sse_t*)&dsp_reverb_buf[0]; + for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) { + movaps_m2r(*sse_src,xmm4); /* Load original sample */ + sse_src++; + mulps_r2r(xmm0,xmm4); /* Gain */ + addps_m2r(*sse_dest,xmm4); /* Mix with buf */ + movaps_r2m(xmm4,*sse_dest); /* Store in buf */ + sse_dest++; + } +} + +/* chorus send. Buffer may be NULL. */ +if (dsp_chorus_buf && voice->amp_chorus != 0){ + sse_a->sf[0]=voice->amp_chorus; + sse_a->sf[1]=voice->amp_chorus; + sse_a->sf[2]=voice->amp_chorus; + sse_a->sf[3]=voice->amp_chorus; + movaps_m2r(*sse_a,xmm0); + + sse_src=(sse_t*)dsp_buf; + sse_dest=(sse_t*)&dsp_chorus_buf[0]; + for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) { + movaps_m2r(*sse_src,xmm4); /* Load original sample */ + sse_src++; + mulps_r2r(xmm0,xmm4); /* Gain */ + addps_m2r(*sse_dest,xmm4); /* Mix with buf */ + movaps_r2m(xmm4,*sse_dest); /* Store in buf */ + sse_dest++; + } +} diff --git a/src/libs/fluidsynth/src/fluid_event.c b/src/libs/fluidsynth/src/fluid_event.c new file mode 100644 index 0000000000..c8d8e84e38 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_event.c @@ -0,0 +1,495 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ + Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free. +*/ + + +#include "fluid_event_priv.h" +#include "fluidsynth_priv.h" + +/*************************************************************** + * + * SEQUENCER EVENTS + */ + +/* Event alloc/free */ + +fluid_event_t* +new_fluid_event() +{ + fluid_event_t* evt; + + evt = FLUID_NEW(fluid_event_t); + if (evt == NULL) { + fluid_log(FLUID_PANIC, "event: Out of memory\n"); + return NULL; + } + + FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); + + // by default, no type + evt->dest = -1; + evt->src = -1; + evt->type = -1; + + return(evt); +} + +void +delete_fluid_event(fluid_event_t* evt) +{ + + if (evt == NULL) { + return; + } + + FLUID_FREE(evt); +} + +/* Initializing events */ +void +fluid_event_set_time(fluid_event_t* evt, unsigned int time) +{ + evt->time = time; +} + +void +fluid_event_set_source(fluid_event_t* evt, short src) +{ + evt->src = src; +} + +void +fluid_event_set_dest(fluid_event_t* evt, short dest) +{ + evt->dest = dest; +} + +/* Timer events */ +void +fluid_event_timer(fluid_event_t* evt, void* data) +{ + evt->type = FLUID_SEQ_TIMER; + evt->data = data; +} + + +/* Note events */ +void +fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel) +{ + evt->type = FLUID_SEQ_NOTEON; + evt->channel = channel; + evt->key = key; + evt->vel = vel; +} + +void +fluid_event_noteoff(fluid_event_t* evt, int channel, short key) +{ + evt->type = FLUID_SEQ_NOTEOFF; + evt->channel = channel; + evt->key = key; +} + +void +fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned int duration) +{ + evt->type = FLUID_SEQ_NOTE; + evt->channel = channel; + evt->key = key; + evt->vel = vel; + evt->duration = duration; +} + +void +fluid_event_all_sounds_off(fluid_event_t* evt, int channel) +{ + evt->type = FLUID_SEQ_ALLSOUNDSOFF; + evt->channel = channel; +} + +void +fluid_event_all_notes_off(fluid_event_t* evt, int channel) +{ + evt->type = FLUID_SEQ_ALLNOTESOFF; + evt->channel = channel; +} + +void +fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num) +{ + evt->type = FLUID_SEQ_BANKSELECT; + evt->channel = channel; + evt->control = bank_num; +} + +void +fluid_event_program_change(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_PROGRAMCHANGE; + evt->channel = channel; + evt->value = val; +} + +void +fluid_event_program_select(fluid_event_t* evt, int channel, + unsigned int sfont_id, short bank_num, short preset_num) +{ + evt->type = FLUID_SEQ_PROGRAMSELECT; + evt->channel = channel; + evt->duration = sfont_id; + evt->value = preset_num; + evt->control = bank_num; +} + +void +fluid_event_any_control_change(fluid_event_t* evt, int channel) +{ + evt->type = FLUID_SEQ_ANYCONTROLCHANGE; + evt->channel = channel; +} + +void +fluid_event_pitch_bend(fluid_event_t* evt, int channel, int pitch) +{ + evt->type = FLUID_SEQ_PITCHBEND; + evt->channel = channel; + if (pitch < 0) pitch = 0; + if (pitch > 16383) pitch = 16383; + evt->pitch = pitch; +} + +void +fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value) +{ + evt->type = FLUID_SEQ_PITCHWHHELSENS; + evt->channel = channel; + evt->value = value; +} + +void +fluid_event_modulation(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_MODULATION; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +void +fluid_event_sustain(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_SUSTAIN; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +void +fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val) +{ + evt->type = FLUID_SEQ_CONTROLCHANGE; + evt->channel = channel; + evt->control = control; + evt->value = val; +} + +void +fluid_event_pan(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_PAN; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +void +fluid_event_volume(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_VOLUME; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +void +fluid_event_reverb_send(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_REVERBSEND; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +void +fluid_event_chorus_send(fluid_event_t* evt, int channel, short val) +{ + evt->type = FLUID_SEQ_CHORUSSEND; + evt->channel = channel; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->value = val; +} + +/* Accessing event data */ +int fluid_event_get_type(fluid_event_t* evt) +{ + return evt->type; +} + +unsigned int fluid_event_get_time(fluid_event_t* evt) +{ + return evt->time; +} + +short fluid_event_get_source(fluid_event_t* evt) +{ + return evt->src; +} + +short fluid_event_get_dest(fluid_event_t* evt) +{ + return evt->dest; +} + +int fluid_event_get_channel(fluid_event_t* evt) +{ + return evt->channel; +} + +short fluid_event_get_key(fluid_event_t* evt) +{ + return evt->key; +} + +short fluid_event_get_velocity(fluid_event_t* evt) + +{ + return evt->vel; +} + +short fluid_event_get_control(fluid_event_t* evt) +{ + return evt->control; +} + +short fluid_event_get_value(fluid_event_t* evt) +{ + return evt->value; +} + +void* fluid_event_get_data(fluid_event_t* evt) +{ + return evt->data; +} + +unsigned int fluid_event_get_duration(fluid_event_t* evt) +{ + return evt->duration; +} +short fluid_event_get_bank(fluid_event_t* evt) +{ + return evt->control; +} +int fluid_event_get_pitch(fluid_event_t* evt) +{ + return evt->pitch; +} +short +fluid_event_get_program(fluid_event_t* evt) +{ + return evt->value; +} +unsigned int +fluid_event_get_sfont_id(fluid_event_t* evt) +{ + return evt->duration; +} + + + +/********************/ +/* heap management */ +/********************/ + +fluid_evt_heap_t* +_fluid_evt_heap_init(int nbEvents) +{ +#ifdef HEAP_WITH_DYNALLOC + + int i; + fluid_evt_heap_t* heap; + fluid_evt_entry *tmp; + + heap = FLUID_NEW(fluid_evt_heap_t); + if (heap == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + + heap->freelist = NULL; + fluid_mutex_init(heap->mutex); + + /* LOCK */ + fluid_mutex_lock(heap->mutex); + + /* Allocate the event entries */ + for (i = 0; i < nbEvents; i++) { + tmp = FLUID_NEW(fluid_evt_entry); + tmp->next = heap->freelist; + heap->freelist = tmp; + } + + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); + + +#else + int i; + fluid_evt_heap_t* heap; + int siz = 2*sizeof(fluid_evt_entry *) + sizeof(fluid_evt_entry)*nbEvents; + + heap = (fluid_evt_heap_t *)FLUID_MALLOC(siz); + if (heap == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + FLUID_MEMSET(heap, 0, siz); + + /* link all heap events */ + { + fluid_evt_entry *tmp = &(heap->pool); + for (i = 0 ; i < nbEvents - 1 ; i++) + tmp[i].next = &(tmp[i+1]); + tmp[nbEvents-1].next = NULL; + + /* set head & tail */ + heap->tail = &(tmp[nbEvents-1]); + heap->head = &(heap->pool); + } +#endif + return (heap); +} + +void +_fluid_evt_heap_free(fluid_evt_heap_t* heap) +{ +#ifdef HEAP_WITH_DYNALLOC + fluid_evt_entry *tmp, *next; + + /* LOCK */ + fluid_mutex_lock(heap->mutex); + + tmp = heap->freelist; + while (tmp) { + next = tmp->next; + FLUID_FREE(tmp); + tmp = next; + } + + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); + fluid_mutex_destroy(heap->mutex); + + FLUID_FREE(heap); + +#else + FLUID_FREE(heap); +#endif +} + +fluid_evt_entry* +_fluid_seq_heap_get_free(fluid_evt_heap_t* heap) +{ +#ifdef HEAP_WITH_DYNALLOC + fluid_evt_entry* evt = NULL; + + /* LOCK */ + fluid_mutex_lock(heap->mutex); + +#if !defined(MACOS9) + if (heap->freelist == NULL) { + heap->freelist = FLUID_NEW(fluid_evt_entry); + if (heap->freelist != NULL) { + heap->freelist->next = NULL; + } + } +#endif + + evt = heap->freelist; + + if (evt != NULL) { + heap->freelist = heap->freelist->next; + evt->next = NULL; + } + + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); + + return evt; + +#else + fluid_evt_entry* evt; + if (heap->head == NULL) return NULL; + + /* take from head of the heap */ + /* critical - should threadlock ? */ + evt = heap->head; + heap->head = heap->head->next; + + return evt; +#endif +} + +void +_fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt) +{ +#ifdef HEAP_WITH_DYNALLOC + + /* LOCK */ + fluid_mutex_lock(heap->mutex); + + evt->next = heap->freelist; + heap->freelist = evt; + + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); + +#else + /* append to the end of the heap */ + /* critical - should threadlock ? */ + heap->tail->next = evt; + heap->tail = evt; + evt->next = NULL; +#endif +} diff --git a/src/libs/fluidsynth/src/fluid_event_priv.h b/src/libs/fluidsynth/src/fluid_event_priv.h new file mode 100644 index 0000000000..3fc1a61a7c --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_event_priv.h @@ -0,0 +1,81 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_EVENT_PRIV_H +#define _FLUID_EVENT_PRIV_H + +#include "fluidsynth.h" +#include "fluid_sys.h" + +/* Private data for event */ +/* ?? should be optimized in size, using unions */ +struct _fluid_event_t { + unsigned int time; + int type; + short src; + short dest; + int channel; + short key; + short vel; + short control; + short value; + short id; //?? unused ? + int pitch; + unsigned int duration; + void* data; +}; + +unsigned int fluid_event_get_time(fluid_event_t* evt); +void fluid_event_set_time(fluid_event_t* evt, unsigned int time); + +/* private data for sorter + heap */ +enum fluid_evt_entry_type { + FLUID_EVT_ENTRY_INSERT = 0, + FLUID_EVT_ENTRY_REMOVE +}; + +typedef struct _fluid_evt_entry fluid_evt_entry; +struct _fluid_evt_entry { + fluid_evt_entry *next; + short entryType; + fluid_event_t evt; +}; + +#define HEAP_WITH_DYNALLOC 1 +/* #undef HEAP_WITH_DYNALLOC */ + +typedef struct _fluid_evt_heap_t { +#ifdef HEAP_WITH_DYNALLOC + fluid_evt_entry* freelist; + fluid_mutex_t mutex; +#else + fluid_evt_entry* head; + fluid_evt_entry* tail; + fluid_evt_entry pool; +#endif +} fluid_evt_heap_t; + +fluid_evt_heap_t* _fluid_evt_heap_init(int nbEvents); +void _fluid_evt_heap_free(fluid_evt_heap_t* heap); +fluid_evt_entry* _fluid_seq_heap_get_free(fluid_evt_heap_t* heap); +void _fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt); + +#endif /* _FLUID_EVENT_PRIV_H */ diff --git a/src/libs/fluidsynth/src/fluid_gen.c b/src/libs/fluidsynth/src/fluid_gen.c new file mode 100644 index 0000000000..24c4cc3232 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_gen.c @@ -0,0 +1,148 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_gen.h" +#include "fluid_chan.h" + + +/* See SFSpec21 $8.1.3 */ +fluid_gen_info_t fluid_gen_info[] = { + /* number/name init scale min max def */ + { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f }, + { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, + { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, + { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, + { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f }, + { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f }, + { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }, + { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f }, + { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f }, + { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f }, + { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, + { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, + { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f }, + { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, + { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, + { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, + { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f }, + { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f }, + { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f }, + { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, + { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f }, + { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f }, + { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f }, + { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f }, + { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f } +}; + + +/* fluid_gen_set_default_values + * + * Set an array of generators to their initial value + */ +int +fluid_gen_set_default_values(fluid_gen_t* gen) +{ + int i; + + for (i = 0; i < GEN_LAST; i++) { + gen[i].flags = GEN_UNUSED; + gen[i].mod = 0.0; + gen[i].nrpn = 0.0; + gen[i].val = fluid_gen_info[i].def; + } + + return FLUID_OK; +} + + +/* fluid_gen_init + * + * Set an array of generators to their initial value + */ +int +fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel) +{ + int i; + + fluid_gen_set_default_values(gen); + + for (i = 0; i < GEN_LAST; i++) { + gen[i].nrpn = fluid_channel_get_gen(channel, i); + + /* This is an extension to the SoundFont standard. More + * documentation is available at the fluid_synth_set_gen2() + * function. */ + if (fluid_channel_get_gen_abs(channel, i)) { + gen[i].flags = GEN_ABS_NRPN; + } + } + + return FLUID_OK; +} + +fluid_real_t fluid_gen_scale(int gen, float value) +{ + return (fluid_gen_info[gen].min + + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); +} + +fluid_real_t fluid_gen_scale_nrpn(int gen, int data) +{ + fluid_real_t value = (float) data - 8192.0f; + fluid_clip(value, -8192, 8192); + return value * (float) fluid_gen_info[gen].nrpn_scale; +} diff --git a/src/libs/fluidsynth/src/fluid_gen.h b/src/libs/fluidsynth/src/fluid_gen.h new file mode 100644 index 0000000000..de446c857c --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_gen.h @@ -0,0 +1,44 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_GEN_H +#define _FLUID_GEN_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_gen_info_t { + char num; /* Generator number */ + char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */ + char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ + float min; /* The minimum value */ + float max; /* The maximum value */ + float def; /* The default value (cfr. fluid_gen_set_default_values()) */ +} fluid_gen_info_t; + +#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } +#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } + +fluid_real_t fluid_gen_scale(int gen, float value); +fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); +int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel); + + +#endif /* _FLUID_GEN_H */ diff --git a/src/libs/fluidsynth/src/fluid_hash.c b/src/libs/fluidsynth/src/fluid_hash.c new file mode 100644 index 0000000000..3062b7b67f --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_hash.c @@ -0,0 +1,388 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * MT safe + */ + +#include "fluidsynth_priv.h" +#include "fluid_hash.h" + + +#define HASH_TABLE_MIN_SIZE 7 +#define HASH_TABLE_MAX_SIZE 13845163 + + +typedef struct _fluid_hashnode_t fluid_hashnode_t; + +struct _fluid_hashnode_t { + char* key; + void* value; + int type; + fluid_hashnode_t *next; +}; + +static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type); +static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); +static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); + +struct _fluid_hashtable_t { + unsigned int size; + unsigned int nnodes; + fluid_hashnode_t **nodes; + fluid_hash_delete_t del; +}; + +#define FLUID_HASHTABLE_RESIZE(hash_table) \ + if ((3 * hash_table->size <= hash_table->nnodes) \ + && (hash_table->size < HASH_TABLE_MAX_SIZE)) { \ + fluid_hashtable_resize(hash_table); \ + } + +static void fluid_hashtable_resize(fluid_hashtable_t *hash_table); +static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key); + +/** + * new_fluid_hashtable: + * + * Creates a new #fluid_hashtable_t. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t* +new_fluid_hashtable(fluid_hash_delete_t del) +{ + fluid_hashtable_t *hash_table; + unsigned int i; + + hash_table = FLUID_NEW(fluid_hashtable_t); + hash_table->size = HASH_TABLE_MIN_SIZE; + hash_table->nnodes = 0; + hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size); + hash_table->del = del; + + for (i = 0; i < hash_table->size; i++) { + hash_table->nodes[i] = NULL; + } + + return hash_table; +} + +/** + * delete_fluid_hashtable: + * @hash_table: a #fluid_hashtable_t. + * + * Destroys the #fluid_hashtable_t. If keys and/or values are dynamically + * allocated, you should either free them first or create the #fluid_hashtable_t + * using fluid_hashtable_new_full(). In the latter case the destroy functions + * you supplied will be called on all keys and values before destroying + * the #fluid_hashtable_t. + **/ +void +delete_fluid_hashtable(fluid_hashtable_t *hash_table) +{ + unsigned int i; + + if (hash_table == NULL) { + return; + } + + for (i = 0; i < hash_table->size; i++) { + delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del); + } + + FLUID_FREE(hash_table->nodes); + FLUID_FREE(hash_table); +} + + +static /*inline*/ fluid_hashnode_t** +fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key) +{ + fluid_hashnode_t **node; + + node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size]; + + while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) { + node = &(*node)->next; + } + + return node; +} + +/** + * fluid_hashtable_lookup: + * @hash_table: a #fluid_hashtable_t. + * @key: the key to look up. + * + * Looks up a key in a #fluid_hashtable_t. + * + * Return value: the associated value, or %NULL if the key is not found. + **/ +int +fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type) +{ + fluid_hashnode_t *node; + + node = *fluid_hashtable_lookup_node(hash_table, key); + + if (node) { + if (value) { + *value = node->value; + } + if (type) { + *type = node->type; + } + return 1; + } else { + return 0; + } +} + +/** + * fluid_hashtable_insert: + * @hash_table: a #fluid_hashtable_t. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #fluid_hashtable_t. + * + * If the key already exists in the #fluid_hashtable_t its current value is replaced + * with the new value. If you supplied a @value_destroy_func when creating the + * #fluid_hashtable_t, the old value is freed using that function. If you supplied + * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed + * using that function. + **/ +void +fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type) +{ + fluid_hashnode_t **node; + + node = fluid_hashtable_lookup_node(hash_table, key); + + if (*node) { + (*node)->value = value; + (*node)->type = type; + } else { + *node = new_fluid_hashnode(key, value, type); + hash_table->nnodes++; + FLUID_HASHTABLE_RESIZE(hash_table); + } +} + + +/** + * fluid_hashtable_replace: + * @hash_table: a #GHashTable. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #GHashTable similar to + * fluid_hashtable_insert(). The difference is that if the key already exists + * in the #GHashTable, it gets replaced by the new key. If you supplied a + * @value_destroy_func when creating the #GHashTable, the old value is freed + * using that function. If you supplied a @key_destroy_func when creating the + * #GHashTable, the old key is freed using that function. + **/ +void +fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type) +{ + fluid_hashnode_t **node; + + node = fluid_hashtable_lookup_node(hash_table, key); + + if (*node) { + if (hash_table->del) { + hash_table->del((*node)->value, (*node)->type); + } + (*node)->value = value; + + } else { + *node = new_fluid_hashnode(key, value, type); + hash_table->nnodes++; + FLUID_HASHTABLE_RESIZE(hash_table); + } +} + +/** + * fluid_hashtable_remove: + * @hash_table: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key) +{ + fluid_hashnode_t **node, *dest; + + node = fluid_hashtable_lookup_node(hash_table, key); + if (*node) { + dest = *node; + (*node) = dest->next; + delete_fluid_hashnode(dest, hash_table->del); + hash_table->nnodes--; + + FLUID_HASHTABLE_RESIZE (hash_table); + + return 1; + } + + return 0; +} + +/** + * fluid_hashtable_foreach: + * @hash_table: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each of the key/value pairs in the + * #fluid_hashtable_t. The function is passed the key and value of each + * pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove + * items). To remove all items matching a predicate, use + * fluid_hashtable_remove(). + **/ +void +fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data) +{ + fluid_hashnode_t *node = NULL; + unsigned int i; + + for (i = 0; i < hash_table->size; i++) { + for (node = hash_table->nodes[i]; node != NULL; node = node->next) { + (*func)(node->key, node->value, node->type, data); + } + } +} + +/** + * fluid_hashtable_size: + * @hash_table: a #fluid_hashtable_t. + * + * Returns the number of elements contained in the #fluid_hashtable_t. + * + * Return value: the number of key/value pairs in the #fluid_hashtable_t. + **/ +unsigned int +fluid_hashtable_size(fluid_hashtable_t *hash_table) +{ + return hash_table->nnodes; +} + +static void +fluid_hashtable_resize(fluid_hashtable_t *hash_table) +{ + fluid_hashnode_t **new_nodes; + fluid_hashnode_t *node; + fluid_hashnode_t *next; + unsigned int hash_val; + int new_size; + unsigned int i; + + new_size = 3 * hash_table->size + 1; + new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size; + +/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */ + + new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size); + FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*)); + + for (i = 0; i < hash_table->size; i++) { + for (node = hash_table->nodes[i]; node; node = next) { + next = node->next; + hash_val = fluid_str_hash(node->key) % new_size; + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + } + + FLUID_FREE(hash_table->nodes); + hash_table->nodes = new_nodes; + hash_table->size = new_size; +} + +static fluid_hashnode_t* +new_fluid_hashnode(char* key, void* value, int type) +{ + fluid_hashnode_t *hash_node; + + hash_node = FLUID_NEW(fluid_hashnode_t); + + hash_node->key = FLUID_STRDUP(key); + hash_node->value = value; + hash_node->type = type; + hash_node->next = NULL; + + return hash_node; +} + +static void +delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) +{ + if (del) { + (*del)(hash_node->value, hash_node->type); + } + if (hash_node->key) { + FLUID_FREE(hash_node->key); + } + FLUID_FREE(hash_node); +} + +static void +delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) +{ + while (hash_node) { + fluid_hashnode_t *next = hash_node->next; + delete_fluid_hashnode(hash_node, del); + hash_node = next; + } +} + + +/* 31 bit hash function */ +unsigned int +fluid_str_hash(char* key) +{ + char *p = key; + unsigned int h = *p; + + if (h) { + for (p += 1; *p != '\0'; p++) { + h = (h << 5) - h + *p; + } + } + + return h; +} diff --git a/src/libs/fluidsynth/src/fluid_hash.h b/src/libs/fluidsynth/src/fluid_hash.h new file mode 100644 index 0000000000..9cb21b52bf --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_hash.h @@ -0,0 +1,64 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * Demolished by Peter Hanappe [December 2002] + * + * - only string as key + * - stores additional type info + * - removed use of GLib types (gpointer, gint, ...) + * - reduced the number of API functions + * - changed names to fluid_hashtable_... + */ + +#ifndef _FLUID_HASH_H +#define _FLUID_HASH_H + + +typedef int (*fluid_hash_iter_t)(char* key, void* value, int type, void* data); +typedef void (*fluid_hash_delete_t)(void* value, int type); + +fluid_hashtable_t* new_fluid_hashtable(fluid_hash_delete_t delete); +void delete_fluid_hashtable(fluid_hashtable_t *hash_table); + +void fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type); + +void fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type); + +/* Returns non-zero if found, 0 if not found */ +int fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type); + +/* Returns non-zero if removed, 0 if not removed */ +int fluid_hashtable_remove(fluid_hashtable_t *hash_table, char* key); + +void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hash_iter_t fun, void* data); + +unsigned int fluid_hashtable_size(fluid_hashtable_t *hash_table); + +unsigned int fluid_str_hash(char* v); + +#endif /* _FLUID_HASH_H */ + diff --git a/src/libs/fluidsynth/src/fluid_io.c b/src/libs/fluidsynth/src/fluid_io.c new file mode 100644 index 0000000000..e00251b2d4 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_io.c @@ -0,0 +1,121 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluidsynth_priv.h" +#include "fluid_io.h" + +#if WITH_READLINE +#include +#include +#endif + +int fluid_istream_gets(fluid_istream_t in, char* buf, int len); + +fluid_istream_t fluid_get_stdin() +{ +#ifdef MACOS9 + return 0; /* to be tested - Antoine 8/3/3 */ +#else + return STDIN_FILENO; +#endif +} + +fluid_ostream_t fluid_get_stdout() +{ +#ifdef MACOS9 + return 1; /* to be tested - Antoine 8/3/3 */ +#else + return STDOUT_FILENO; +#endif +} + +int fluid_istream_readline(fluid_istream_t in, char* prompt, char* buf, int len) +{ +#if WITH_READLINE + if (in == fluid_get_stdin()) { + char* line; + + line = readline(prompt); + if (line == NULL) { + return -1; + } + + snprintf(buf, len, "%s", line); + buf[len - 1] = 0; + + free(line); + return 1; + } else { + return fluid_istream_gets(in, buf, len); + } +#else + return fluid_istream_gets(in, buf, len); +#endif +} + +/* FIXME */ +int fluid_istream_gets(fluid_istream_t in, char* buf, int len) +{ + char c; + int n; + + buf[len - 1] = 0; + + while (--len > 0) { + n = read(in, &c, 1); + if (n == 0) { + *buf++ = 0; + return 0; + } + if (n < 0) { + return n; + } + if ((c == '\n') || (c == '\r')) { + *buf++ = 0; + return 1; + } + *buf++ = c; + } + + return -1; +} + + +int fluid_ostream_printf(fluid_ostream_t out, char* format, ...) +{ + char buf[4096]; + va_list args; + int len; + + va_start(args, format); + len = vsnprintf(buf, 4095, format, args); + va_end(args); + + if (len <= 0) { + printf("fluid_ostream_printf: buffer overflow"); + return -1; + } + + buf[4095] = 0; + +/* return write(out, buf, len); */ + return write(out, buf, strlen(buf)); +} diff --git a/src/libs/fluidsynth/src/fluid_io.h b/src/libs/fluidsynth/src/fluid_io.h new file mode 100644 index 0000000000..733ac0c05e --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_io.h @@ -0,0 +1,42 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_IO_H +#define _FLUID_IO_H + + +/** Read a line from the input stream. + + \returns 0 if end-of-stream, -1 if error, non zero otherwise +*/ +int fluid_istream_readline(fluid_istream_t in, char* prompt, char* buf, int len); + + +/** Read a line from the input stream. + + \returns The number of bytes written. If an error occured, -1 is + returned. +*/ +int fluid_ostream_printf(fluid_ostream_t out, char* format, ...); + + + +#endif /* _FLUID_IO_H */ diff --git a/src/libs/fluidsynth/src/fluid_ladspa.c b/src/libs/fluidsynth/src/fluid_ladspa.c new file mode 100644 index 0000000000..153dfb9225 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_ladspa.c @@ -0,0 +1,1118 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* This module: 3/2002 + * Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + +#define PrintErrorMessage -1 + +#include "fluidsynth_priv.h" + +#ifdef LADSPA +#include + +/* Dynamic library functions */ +#include + +#include "fluid_ladspa.h" +#include "fluid_synth.h" +#include "fluid_io.h" + +/* Logging to stdout. */ +//#define L(x) x;printf("\n"); +#define L(x); + +fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth){ + fluid_LADSPA_FxUnit_t* FxUnit=FLUID_NEW(fluid_LADSPA_FxUnit_t); + assert(FxUnit); + assert(synth); + /* The default state is 'bypassed'. The Fx unit has to be turned on explicitly by the user. */ + /* Those settings have to be done in order to allow fluid_LADSPA_clean. */ + FxUnit->Bypass=fluid_LADSPA_Bypassed; + FxUnit->NumberNodes=0; + FxUnit->NumberPlugins=0; + FxUnit->NumberLibs=0; + FxUnit->NumberCommands=0; + FxUnit->NumberUserControlNodes=0; + FxUnit->synth=synth; + pthread_cond_init(&FxUnit->cond,NULL); + return FxUnit; +}; + +/* Purpose: + * Creates the system nodes to get data into and out of the Fx unit. + */ +void fluid_LADSPA_CreateSystemNodes(fluid_LADSPA_FxUnit_t* FxUnit){ + char str[99]; + int nr_input_nodes; + int nr_fx_input_nodes; + int nr_output_nodes; + int temp; + int i; + + /* Retrieve the number of synth / audio out / Fx send nodes */ + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-groups", &temp)); + nr_input_nodes=(int) temp; + printf("%i audio groups\n", nr_input_nodes); + + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-channels", &temp)); + nr_output_nodes=temp; + + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.effects-channels", &temp)); + nr_fx_input_nodes=temp; + + /* Create regular input nodes (associated with audio groups) */ + for (i=0; i < nr_input_nodes; i++){ + sprintf(str, "in%i_L",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); + sprintf(str, "in%i_R",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); + }; + + /* Create effects send nodes (for example reverb, chorus send) */ + for (i=0; i < nr_fx_input_nodes; i++){ + sprintf(str, "send%i_L",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); + sprintf(str, "send%i_R",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); + }; + + /* Create output nodes (usually towards the sound card) */ + for (i=0; i < nr_input_nodes; i++){ + sprintf(str, "out%i_L",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); + sprintf(str, "out%i_R",(i+1)); + fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); + }; +}; + +/* Purpose: + * Creates predeclared nodes for control of the Fx unit during operation. + */ +void fluid_LADSPA_CreateUserControlNodes(fluid_LADSPA_FxUnit_t* FxUnit){ + int i; + fluid_LADSPA_Node_t* CurrentNode; + + for (i=0; iNumberUserControlNodes; i++){ + CurrentNode=fluid_LADSPA_CreateNode(FxUnit,FxUnit->UserControlNodeNames[i],fluid_LADSPA_node_is_control); + assert(CurrentNode); + CurrentNode->buf[0]=FxUnit->UserControlNodeValues[i]; + CurrentNode->InCount++; /* The constant counts as input */ + CurrentNode->flags=fluid_LADSPA_node_is_source | fluid_LADSPA_node_is_user_ctrl; /* It is a user control node */ + }; +}; + +/* Purpose: + * Returns the pointer to the shared library loaded using 'LibraryFilename' (if it has been loaded). + * Return NULL otherwise. + * If not: Clear Fx and abort. + */ +void * fluid_LADSPA_RetrieveSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ + void * CurrentLib=NULL; + int LibCount; + for (LibCount=0; LibCountNumberLibs; LibCount++){ + assert(FxUnit->ppvPluginLibNames[LibCount]); + if (FLUID_STRCMP(FxUnit->ppvPluginLibNames[LibCount],LibraryFilename)==0){ + CurrentLib=FxUnit->ppvPluginLibs[LibCount]; + }; + }; + return CurrentLib; +}; + +/* Purpose: + * Loads a shared LADSPA library. + * Return NULL, if failed. + * TODO: use LADSPA_PATH + */ +void * fluid_LADSPA_LoadSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ + void * LoadedLib; + assert(LibraryFilename); + LoadedLib=dlopen(LibraryFilename,RTLD_NOW); + if (!LoadedLib){ + return NULL; + }; + FxUnit->ppvPluginLibs[FxUnit->NumberLibs]=LoadedLib; + FxUnit->ppvPluginLibNames[FxUnit->NumberLibs]=FLUID_STRDUP(LibraryFilename); + FxUnit->NumberLibs++; + return LoadedLib; +}; + +/* Purpose: + * Retrieves a descriptor to the plugin labeled 'PluginLabel' from the shared library. + */ +const LADSPA_Descriptor * fluid_LADSPA_Retrieve_Plugin_Descriptor(void * CurrentLib, char * PluginLabel){ + LADSPA_Descriptor_Function pfDescriptorFunction; + unsigned long lPluginIndex=0; + const LADSPA_Descriptor * psDescriptor; + pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); + while (pfDescriptorFunction(lPluginIndex)){ + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (FLUID_STRCMP(psDescriptor->Label, PluginLabel) == 0){ + return psDescriptor; + }; + lPluginIndex++; + }; + return NULL; +}; + +/* Purpose: + * Finds out, if 'PortName' starts with 'PluginPort'. + * Spaces and underscore mean the same. + * The comparison is not case sensitive. + * The result distinguishes between a full match (strings are equal), and a partial match (PortName starts with Plugin_Port, but is longer). + */ + +fluid_LADSPA_Stringmatch_t fluid_LADSPA_Check_SubString_Match(const char * Plugin_Port, const char * PortName){ + unsigned int CharCount; + char a; + char b; + for(CharCount=0; CharCount='a' && a <='z'){a-=32;} + if (b>='a' && b <='z'){b-=32;} + if (a == ' '){a='_';}; + if (b == ' '){b='_';}; + if ( a != b){ + return fluid_LADSPA_NoMatch; + }; + }; + if (FLUID_STRLEN(Plugin_Port) == FLUID_STRLEN(PortName)){ + return fluid_LADSPA_FullMatch; + }; + return fluid_LADSPA_PartialMatch; + +}; + +/* Purpose: + * Load the plugins added with 'ladspa_add' and then start the Fx unit. + */ + +int +fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ + fluid_LADSPA_FxUnit_t* FxUnit; + int CommandLineCount; + char * LibraryFilename; + char * PluginLabel; + char ** TokenSequence; + void * CurrentLib; + int NodeCount; //x + int IngoingSignalCount; // Count signals going into LADSPA Fx section + int OutgoingSignalCount; // Count signals going out of LADSPA Fx section + int ReturnVal=FLUID_OK; /* If warnings occur, this is set to -1. */ + char * LADSPA_Path = getenv("LADSPA_PATH"); + + L(fluid_ostream_printf(out,"ladspa_start: starting...")); + assert(synth); + FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); + + /* When calling fluid_ladspastart, the Fx unit must be 'cleared' (no plugins, no libs, no nodes). Verify this here. */ + if (FxUnit->NumberPlugins || FxUnit->NumberLibs){ + fluid_ostream_printf(out, "***Error006***\n" + "Fx unit is currently in use!\n" + "Please run the ladspa_clear command before attempting to use ladspa_add!\n"); + /* In this case do _not_ clear the Fx unit. */ + return(PrintErrorMessage); + }; + if (!FxUnit->NumberCommands){ + fluid_ostream_printf(out, "***Error007***\n" + "Refusing to start the Fx unit without any plugin.\n" + "Use ladspa_add first!\n"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* Create predefined nodes */ + L(fluid_ostream_printf(out,"ladspa_start: creating predefined nodes...")); + fluid_LADSPA_CreateSystemNodes(FxUnit); + + /* Create predeclared nodes, that will allow to control the Fx unit during operation */ + L(fluid_ostream_printf(out,"ladspa_start: creating user control nodes...")); + fluid_LADSPA_CreateUserControlNodes(FxUnit); + + L(fluid_ostream_printf(out,"ladspa_start: Processing command lines...")); + for (CommandLineCount=0; CommandLineCountNumberCommands; CommandLineCount++){ + int CurrentPlugin_PortConnected[FLUID_LADSPA_MaxTokens/3]; /* For example: 100 tokens corresponds to roughly 30 ports. */ + char LibFullPath[FLUID_LADSPA_MaxPathLength]; + const LADSPA_Descriptor * CurrentPluginDescriptor; + LADSPA_Handle CurrentPlugin; + unsigned long PortCount; + int TokenCount=0; + char * Search; + int HasASlash=0; + + if (FxUnit->NumberPlugins>=FLUID_LADSPA_MaxPlugins){ + fluid_ostream_printf(out, "***Error003***\n" + "Too many plugins at the same time (%i).\n" + "Change FLUID_LADSPA_MaxPlugins!\n", + FxUnit->NumberPlugins); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + L(fluid_ostream_printf(out,"Processing plugin nr. %i",FxUnit->NumberPlugins)); + + TokenSequence=FxUnit->LADSPA_Command_Sequence[CommandLineCount]; + assert(TokenSequence); + + /* Check, if the library is already loaded. If not, load. */ + LibraryFilename=TokenSequence[TokenCount++]; assert(LibraryFilename); + + L(fluid_ostream_printf(out,"Library name from ladspa_add: %s",LibraryFilename)); + /* A slash-free filename refers to the LADSPA_PATH directory. Add that path, if needed. */ + + + + /* Determine, if the library filename contains a slash. + * If no, try to retrieve the environment variable LADSPA_PATH. + * If that fails, signal error. + * Otherwise leave the filename as it is. + */; + + /* Determine, if the library name is just the filename, or a path (including a slash) */ + Search=LibraryFilename; + while (*Search != '\0') { + if ((*Search)== '/'){ + HasASlash=1; + }; + Search++; + }; + + if (!HasASlash){ + if (!LADSPA_Path){ + fluid_ostream_printf(out, "***Error018***\n" + "The library file name %s does not include a path.\n" + "The environment variable LADSPA_PATH is not set.\n" + "- Use an absolute path (i.e. /home/myself/mylib.so)\n" + "- For the current directory use ./mylib.so\n" + "- set the environment variable LADSPA_PATH (export LADSPA_PATH=/usr/lib/ladspa)\n" + "- depending on your shell, try 'setenv' instead of 'export'\n", + LibraryFilename); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; /* if no LADSPA_PATH */ + snprintf(LibFullPath,FLUID_LADSPA_MaxPathLength,"%s/%s",LADSPA_Path,LibraryFilename); + /* If no slash in filename */ + } else { + snprintf(LibFullPath,FLUID_LADSPA_MaxPathLength,"%s",LibraryFilename); + }; + + L(fluid_ostream_printf(out,"Full Library path name: %s",LibFullPath)); + + CurrentLib=fluid_LADSPA_RetrieveSharedLibrary(FxUnit, LibFullPath); + if (!CurrentLib){ + LADSPA_Descriptor_Function pfDescriptorFunction; + + L(fluid_ostream_printf(out,"Library %s not yet loaded. Loading.",LibFullPath)); + + if (FxUnit->NumberLibs>=FLUID_LADSPA_MaxLibs){ + fluid_ostream_printf(out, "***Error004***\n" + "Too many libraries open (%i)\n" + "Change FLUID_LADSPA_MaxLibs",FxUnit->NumberPlugins); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* Load a LADSPA plugin library and store it for future use.*/ + CurrentLib=fluid_LADSPA_LoadSharedLibrary(FxUnit, LibFullPath); + + if (!CurrentLib){ + fluid_ostream_printf(out, "***Error008***\n" + "Failed to load plugin library %s.", + LibraryFilename); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + + dlerror(); + pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); + if (!pfDescriptorFunction) { + const char * pcError = dlerror(); + if (!pcError) {pcError="Huh?! No error from lib!";}; + fluid_ostream_printf(out, "***Error015***\n" + "Unable to find ladspa_descriptor() function in plugin library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + LibraryFilename, + pcError); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + L(fluid_ostream_printf(out,"Library loaded.")); + }; + + PluginLabel=TokenSequence[TokenCount++]; assert(PluginLabel); + L(fluid_ostream_printf(out,"Plugin Label from ladspa_add: %s",PluginLabel)); + /* Retrieve a 'plugin descriptor' from the library. */ + L(fluid_ostream_printf(out,"Looking for the plugin labeled %s",PluginLabel)); + + CurrentPluginDescriptor=fluid_LADSPA_Retrieve_Plugin_Descriptor(CurrentLib, PluginLabel); + + + /* Check, that the plugin was actually found.*/ + if (CurrentPluginDescriptor==NULL){ + fluid_ostream_printf(out, "***Error016***\n" + "Unable to find the plugin labeled \"%s\" in plugin library file \"%s\".\n" + "Hint: run analyzeplugin %s from the command line to get a list of valid plugin labels.\n", + PluginLabel, + LibraryFilename, + LibraryFilename); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* Create an instance of the plugin type from the descriptor */ + L(fluid_ostream_printf(out,"instantiating plugin %s",PluginLabel)); + CurrentPlugin=CurrentPluginDescriptor + ->instantiate(CurrentPluginDescriptor,44100); /* Sample rate hardcoded */ + assert(CurrentPlugin); + + /* The descriptor ("type of plugin") and the instance are stored for each plugin instantiation. + If one plugin type is instantiated several times, they will have the same descriptor, only + different instances.*/ + FxUnit->PluginDescriptorTable[FxUnit->NumberPlugins]=CurrentPluginDescriptor; + FxUnit->PluginInstanceTable[FxUnit->NumberPlugins]=CurrentPlugin; + + /* + * + * Wire up the inputs and outputs + * + */ + + /* List for checking, that each plugin port is exactly connected once */ + for (PortCount=0; PortCountPortCount; PortCount++){ + CurrentPlugin_PortConnected[PortCount]=0; + }; + + /* Note: There are three NULL tokens at the end. The last condition may be evaluated even if the first one already detects the end. */ + while (TokenSequence[TokenCount] && TokenSequence[TokenCount+1] && TokenSequence[TokenCount+2]){ + int CurrentPort_StringMatchCount; + LADSPA_PortDescriptor CurrentPort_Descriptor; + char * Plugin_Port=TokenSequence[TokenCount++]; + char * Direction=TokenSequence[TokenCount++]; + char * FLUID_Node=TokenSequence[TokenCount++]; + fluid_LADSPA_Node_t* Current_Node; + const char * PortName=NULL; + int CurrentPort_Index=-1; + fluid_LADSPA_Stringmatch_t StringMatchType=fluid_LADSPA_NoMatch; + fluid_LADSPA_Stringmatch_t CurrentPort_StringMatchType=fluid_LADSPA_NoMatch; + CurrentPort_StringMatchCount=0; + + L(fluid_ostream_printf(out,"Wiring %s %s %s",Plugin_Port, Direction, FLUID_Node)); + + /* Find the port number on the plugin, that belongs to "Plugin_Port". + * Match the identifier specified by the user against the first characters + * of the port name. + * + * If the given identifier matches several port names only partly, then the input is ambiguous, an error + * message results. + * + * Example: cmt.so, limit_peak: This plugin uses the labels + * - Output Envelope Attack (s) + * - Output Envelope Decay (s) + * - Output + * + * The user input 'Output' matches the first two labels partly, the third fully. This will be accepted. + * The user input 'Out' matches all three only partly, this results in an error message. + */ + + for (PortCount=0; PortCountPortCount; PortCount++){ + PortName=CurrentPluginDescriptor->PortNames[PortCount]; + + StringMatchType=fluid_LADSPA_Check_SubString_Match(Plugin_Port, PortName); + /* If a full-string match has been found earlier, reject all partial matches. */ + if (StringMatchType==fluid_LADSPA_FullMatch || + (StringMatchType==fluid_LADSPA_PartialMatch && CurrentPort_StringMatchType != fluid_LADSPA_FullMatch)){ + + if(StringMatchType==fluid_LADSPA_FullMatch && CurrentPort_StringMatchType==fluid_LADSPA_FullMatch){ + fluid_ostream_printf(out, "***Error027***\n" + "While processing plugin %s: The port label %s appears more than once!\n" + "This is an error in the plugin itself. Please correct it or use another plugin.",PluginLabel, Plugin_Port); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + CurrentPort_Index=PortCount; + CurrentPort_StringMatchType=StringMatchType; + CurrentPort_StringMatchCount++; + + }; /* if suitable match */ + }; /* For port count */ + + /* Several partial matches? Then the identifier is not unique. */ + if (CurrentPort_StringMatchCount > 1 && CurrentPort_StringMatchType == fluid_LADSPA_PartialMatch){ + fluid_ostream_printf(out, "***Error019***\n" + "While processing plugin %s: The identifier %s matches more than one plugin port.\n" + "Please use more letters for the port name. If needed, replace spaces with underscores (_).\n" + "This error will not occur, if you use the full name of a port.\n",PluginLabel, Plugin_Port); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + if (CurrentPort_Index<0){ + fluid_ostream_printf(out, "***Error017***\n" + "Unable to find port '%s' on plugin %s\n" + "Port names are:\n",Plugin_Port,PluginLabel); + for (PortCount=0; PortCountPortCount; PortCount++){ + printf("- `%s'\n",CurrentPluginDescriptor->PortNames[PortCount]); + }; + + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + CurrentPort_Descriptor=CurrentPluginDescriptor->PortDescriptors[CurrentPort_Index]; + assert(CurrentPort_Descriptor); + + /* Retrieve the node with the right name. */ + Current_Node=fluid_LADSPA_RetrieveNode(FxUnit,FLUID_Node); +#define PortIsAudio LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor)) +#define PortIsControl LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor)) + if (!Current_Node){ + /* Doesn't exist? Then create it. */ + if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ + fluid_ostream_printf(out, "***Error005***\n" + "Too many nodes (%i)\n" + "Change FLUID_LADSPA_MaxNodes",FxUnit->NumberNodes); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + if (PortIsAudio){ + Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_audio); + } else if (PortIsControl){ + Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_control); + } else { + fluid_ostream_printf(out, "***Error025***\n" + "Plugin port number %i is neither input nor output!\n" + "This is an error in the plugin.\n" + "Please check plugin sourcecode.\n", + CurrentPort_Index); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + }; + assert(Current_Node); + + /* + * + * Check flowgraph for some possible errors. + * + */ + + if (FLUID_STRCMP(Direction,"->")==0){ + /* Data from plugin to FLUID, into node + * + * *** Rule: **** + * A node may not have more than one data source.*/ + if (Current_Node->InCount !=0){ + fluid_ostream_printf(out, "***Error009***\n" + "Plugin %s tries to feed data from output %s into node %s, which is already connected to a data source.\n",PluginLabel,Plugin_Port,FLUID_Node); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + Current_Node->InCount++; + } else if (FLUID_STRCMP(Direction,"<-")==0){ + /* Data from FLUID to plugin, out of node + * + * This check verifies the integrity of the flow graph: + * *** Rule *** + * The execution order of the plugins is the order, in which they are programmed. + * The plugins must be ordered so, that the input of a plugin is already computed at the time of its execution. + * If the user tries to read data out of a node that has not yet an input, then something is wrong.*/ + assert(Current_Node->InCount<=1); + if (Current_Node->InCount !=1){ + fluid_ostream_printf(out, "***Error010***\n" + "Plugin %s tries to read data through input %s from node %s.\n" + "But at this point there is no valid data at that node.\n" + "Please check the flowgraph and especially the execution order!\n",PluginLabel,Plugin_Port,FLUID_Node); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + Current_Node->OutCount++; + } else { + fluid_ostream_printf(out, "***Error024***\n" + "Syntax error: Illegal `arrow' `%s', expecting -> or <-\n", + Direction); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* In any case, there must be a valid data source for the port at this time. */ + assert(Current_Node->InCount==1); + + /* Keep track on the number of connections to each port. + * This error occurs only, if an attempt is made to connect one port twice (i.e. ladspa_add libname pluginname port <- nodex port <- nodey) */ + if (CurrentPlugin_PortConnected[CurrentPort_Index]){ + fluid_ostream_printf(out, "***Error011***\n" + "Refusing to connect twice to port %s on plugin %s.\n",CurrentPluginDescriptor->PortNames[CurrentPort_Index], PluginLabel); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* + * + * Connect the port + * + */ + + L(fluid_ostream_printf(out,"Connecting %i",CurrentPort_Index)); + CurrentPluginDescriptor->connect_port + (CurrentPlugin, + CurrentPort_Index, + Current_Node->buf + ); + CurrentPlugin_PortConnected[CurrentPort_Index]++; + + }; /* While Tokensequence (more connections) */ + + /* + * + * Check for left-over tokens + * + */ + + if (TokenSequence[TokenCount]){ + char * T1="";char * T2="";char * T3=""; + if (TokenSequence[TokenCount]){T1=TokenSequence[TokenCount];}; + if (TokenSequence[TokenCount+1]){T2=TokenSequence[TokenCount+1];}; + if (TokenSequence[TokenCount+2]){T3=TokenSequence[TokenCount+2];}; + fluid_ostream_printf(out, "***Error012***\n" + "Leftover tokens: %s %s %s...\n",T1,T2,T3); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* + * + * Check, that all plugin ports are connected + * + */ + L(fluid_ostream_printf(out,"Checking left-over ports")); + assert(CurrentPluginDescriptor); + for (PortCount=0; PortCountPortCount; PortCount++){ + assert(CurrentPlugin_PortConnected[PortCount] <=1); + if (CurrentPlugin_PortConnected[PortCount] !=1){ + fluid_ostream_printf(out, "***Error013***\nPlugin: %s. Port %s is unconnected!\n",PluginLabel, CurrentPluginDescriptor->PortNames[PortCount]); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + }; + + /* + * + *Run activate function on plugin, where possible + * + */ + + if (CurrentPluginDescriptor->activate !=NULL){ + CurrentPluginDescriptor->activate(CurrentPlugin); + }; + + FxUnit->NumberPlugins++; + }; /* For CommandLineCount: once for each new command (i.e. plugin instantiation request from user */ + + /* + * + * Further flow graph checks + * + */ + + L(fluid_ostream_printf(out,"Checking flow graph")); + + IngoingSignalCount=0; + OutgoingSignalCount=0; + + for (NodeCount=0; NodeCountNumberNodes; NodeCount++){ + fluid_LADSPA_Node_t* Current_Node; + Current_Node=FxUnit->Nodelist[NodeCount]; + assert(Current_Node); + if (Current_Node->flags & fluid_LADSPA_node_is_source){ + IngoingSignalCount+=Current_Node->OutCount; + } else if (Current_Node->flags & fluid_LADSPA_node_is_sink){ + OutgoingSignalCount+=Current_Node->InCount; + } else { + + /* A node without any input doesn't make sense. + * The flow graph check aborts with an error. This case cannot happen. + */ + if (Current_Node->InCount==0 && !Current_Node->flags & fluid_LADSPA_node_is_dummy){ /* There can only be one warning at a time. */ + fluid_ostream_printf(out, "***Warning020***" + "No input into node %s.\n" + "Use '_' as first char in nodename to suppress this warning.\n" + "Hint: Check for typos in the node name.\n",Current_Node->Name); + /* A warning can also be printed as an error message (check fluid_cmd.c). The only difference between + the return values -1 and 0 is, that -1 prints the result. */ + }; + ReturnVal=PrintErrorMessage; + }; + + /* A node without any output doesn't make sense. */ + if (Current_Node->OutCount==0 && !Current_Node->flags & fluid_LADSPA_node_is_dummy){ + fluid_ostream_printf(out, "***Warning021***\n" + "No output from node %s.\n" + "Use '_' as first char in nodename to suppress this warning.\n" + "Hint: Check for typos in the node name.\n",Current_Node->Name); + ReturnVal=PrintErrorMessage; + }; + /* A free-flying node simply cannot happen. */ + assert(Current_Node->OutCount+Current_Node->InCount); + }; /* Foreach node */ + + /* Issue a warning, if no signal goes into the Fx section. */ + if (IngoingSignalCount==0){ + fluid_ostream_printf(out, "***Warning022***\n" + "You have not connected anything to the synthesizer section (in1_L, in1_R).\n"); + ReturnVal=PrintErrorMessage; + }; + + /* Issue a warning, if no signal leaves the Fx section. */ + if (OutgoingSignalCount==0){ + fluid_ostream_printf(out, "***Warning023***\n" + "You have not connected anything to the output (out1_L, out1_R).\n"); + }; + + /* Finally turn on the Fx unit. */ + FxUnit->Bypass=fluid_LADSPA_Active; + L(fluid_ostream_printf(out,"LADSPA Init OK")); + return(ReturnVal); +}; + +void +fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* FxUnit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]){ + int i; + int ii; + + + int nr_audio_channels; + int nr_fx_sends; + int nr_groups; + int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); + char str[99]; + fluid_LADSPA_Node_t* n; + int temp; + + /* Retrieve the number of synth / audio out / Fx send nodes */ + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-groups", &temp)); + nr_groups=(int) temp; + + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-channels", &temp)); + nr_audio_channels=temp; + + assert(fluid_settings_getint(FxUnit->synth->settings, "synth.effects-channels", &temp)); + nr_fx_sends=temp; + + /* Fixme: Retrieving nodes via names is inefficient + * (but not that bad, because the interesting nodes are always at the start of the list). + */ + + /* Input and output are processed via the same buffers. Therefore the effect is bypassed by just skipping everything else. */ + if (FxUnit->Bypass==fluid_LADSPA_Bypassed){ + return; + }; + + if (FxUnit->Bypass==fluid_LADSPA_BypassRequest){ + FxUnit->Bypass=fluid_LADSPA_Bypassed; + pthread_mutex_lock(&FxUnit->mutex); + pthread_cond_broadcast(&FxUnit->cond); + pthread_mutex_unlock(&FxUnit->mutex); + L(printf("LADSPA_Run: Command line asked for bypass of Fx unit. Acknowledged.")); + return; + }; + + assert(FxUnit); + + /* Clear the output buffers, if they are not connected anywhere */ + for (ii=0; ii < nr_audio_channels; ii++){ + + sprintf(str, "out%i_L",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + if (n->InCount == 0){ +/* printf("Output node %s is not connected -> clear buffer\n", str); */ + FLUID_MEMSET(n->buf, 0, byte_size); + }; + + sprintf(str, "out%i_R",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + if (n->InCount == 0){ +/* printf("Output node %s is not connected -> clear buffer\n", str); */ + FLUID_MEMSET(n->buf, 0, byte_size); + }; + }; + + /* Prepare the incoming data: + * Convert fluid_real_t data type to LADSPA_Data type */ + for (ii=0; ii < nr_groups; ii++){ + fluid_real_t* src_buf=left_buf[ii]; + sprintf(str, "in%i_L",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + + assert(FLUID_BUFSIZE % 2 == 0); + + /* Add a very small high frequency signal. This avoids denormal number problems. */ + for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); + i++; + n->buf[i]=(LADSPA_Data)(src_buf[i]); + i++; + }; + + src_buf=right_buf[ii]; + sprintf(str, "in%i_R",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + + /* Add a very small high frequency signal. This avoids denormal number problems. */ + for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); + i++; + n->buf[i]=(LADSPA_Data)(src_buf[i]); + i++; + }; + }; + + /* Effect send paths */ + for (ii=0; ii < nr_fx_sends; ii++){ + sprintf(str, "send%i_L",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + for (i=0; ibuf[i]=(LADSPA_Data)(fx_left_buf[ii][i]); + }; + + sprintf(str, "send%i_R",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + for (i=0; ibuf[i]=(LADSPA_Data)(fx_right_buf[ii][i]); + }; + }; + + /* Run each plugin on a block of data. + * The execution order has been checked during setup.*/ + for (i=0; iNumberPlugins; i++){ + FxUnit->PluginDescriptorTable[i]->run(FxUnit->PluginInstanceTable[i],FLUID_BUFSIZE); + }; + + /* Copy the data from the output nodes back to the synth. */ + for (ii=0; ii < nr_audio_channels; ii++){ + fluid_real_t* dest_buf=left_buf[ii]; + sprintf(str, "out%i_L",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + for (i=0; ibuf[i]; + }; + + dest_buf=right_buf[ii]; + sprintf(str, "out%i_R",(ii+1)); + n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); + for (i=0; ibuf[i]; + }; + }; +}; + +fluid_LADSPA_Node_t* +fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name){ + int i=0; + assert(FxUnit);assert(Name); + for (i=0; iNumberNodes; i++){ + assert(FxUnit->Nodelist[i]); + if (FLUID_STRCMP(FxUnit->Nodelist[i]->Name,Name)==0){ + return FxUnit->Nodelist[i]; + }; + }; + return NULL; +}; + + +/* Purpose: + * Creates a new node from the node name given by the user. + */ +fluid_LADSPA_Node_t* +fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags){ + int Dummy=0; + fluid_LADSPA_Node_t* NewNode; + assert(FxUnit); + assert(Name); +// printf("Flags is %i\n",flags); + L(printf("Create node: %s",Name)); + if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ + printf( "***Error014***\n" + "Too many nodes (%i)\n" + "Change FLUID_LADSPA_MaxNodes.\n",FxUnit->NumberNodes); + fluid_LADSPA_clear(FxUnit); + return NULL; + }; + + /* Don't allow node names, which start with -, 0..9 */ + if (Name[0] == '-' || (Name[0]>='0' && Name[0]<='9')){ + printf( "***Error026***\n" + "The node name %s starts with a digit / minus sign!\n" + "Please use a letter to start a node name.\n" + "A constant node is created by using `#' as first character,\n" + "for example #-2.5.\n", + Name); + fluid_LADSPA_clear(FxUnit); + return NULL; + }; + + /* A nodename starting with "_" is a possible dummy node, which may (but need not) act as a data sink or source (dummy node). */ + if (Name[0] == ' '){ /* ??? Should be '_' ??? */ + Dummy=1; + }; + NewNode=FLUID_NEW(fluid_LADSPA_Node_t);assert(NewNode); + if (flags && fluid_LADSPA_node_is_audio){ + /* Audio node contains buffer. */ + NewNode->buf=FLUID_ARRAY(LADSPA_Data, (FLUID_BUFSIZE));assert(NewNode->buf); + /* It is permitted to use a dummy node without input. Therefore clear all node buffers at startup. */ + FLUID_MEMSET(NewNode->buf, 0, (FLUID_BUFSIZE*sizeof(LADSPA_Data))); + } else if (flags & fluid_LADSPA_node_is_control){ + /* Control node contains single value. */ + NewNode->buf=FLUID_ARRAY(LADSPA_Data, 1);assert(NewNode->buf); + } else { + assert(0); + }; + NewNode->Name=FLUID_STRDUP(Name);assert(NewNode->Name); + if (Dummy){ + flags |= fluid_LADSPA_node_is_dummy; + }; + NewNode->InCount=0; + NewNode->OutCount=0; + NewNode->flags=flags; + + /* A nodename starting with "#" means that the node holds a constant value. */ + if (NewNode->Name[0] == '#'){ + assert(flags & fluid_LADSPA_node_is_control); + /* Skip the first character => +1 */ + NewNode->buf[0]=(LADSPA_Data)atof(NewNode->Name+1); + NewNode->InCount++; + }; + if (flags & fluid_LADSPA_node_is_source){ + NewNode->InCount++; +// printf("****************************** Source!\n"); + } else if (flags & fluid_LADSPA_node_is_sink){ + NewNode->OutCount++; + }; + FxUnit->Nodelist[FxUnit->NumberNodes++]=NewNode; + + L(printf("Node %s created.",Name)); + return NewNode; +}; + +void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit){ + int i; + int ii; + L(printf("ladspa_clear")); + assert(FxUnit); + + if (FxUnit->Bypass==fluid_LADSPA_Active){ + L(printf("clear: Requesting bypass from synthesis thread")); + /* Bypass the Fx unit before anything else. + * Reason: Not a good idea to release plugins, while another thread runs them. + */ + FxUnit->Bypass=fluid_LADSPA_BypassRequest; + pthread_mutex_lock(&FxUnit->mutex); + pthread_cond_wait(&FxUnit->cond,&FxUnit->mutex); + pthread_mutex_unlock(&FxUnit->mutex); + L(printf("clear: Synthesis thread has switched to bypass.")); + } else { + L(printf("clear: Fx unit was already bypassed. No action needed.")); + }; + + L(printf("Clear all user control node declarations")); + for (i=0; iNumberUserControlNodes; i++){ + FLUID_FREE(FxUnit->UserControlNodeNames[i]); + }; + FxUnit->NumberUserControlNodes=0; + + L(printf("Clear all plugin instances")); + for (i=0; iNumberPlugins; i++){ + assert(FxUnit->PluginDescriptorTable[i]); + assert(FxUnit->PluginInstanceTable[i]); + + /* Run deactivate function on plugin, if possible */ + if (FxUnit->PluginDescriptorTable[i]->deactivate){ + FxUnit->PluginDescriptorTable[i]->deactivate(FxUnit->PluginInstanceTable[i]); + }; + FxUnit->PluginDescriptorTable[i]->cleanup(FxUnit->PluginInstanceTable[i]); + }; + FxUnit->NumberPlugins=0; + + L(printf("Clear all nodes")); /* Only after removing plugins! */ + for (i=0; iNumberNodes; i++){ + FLUID_FREE(FxUnit->Nodelist[i]->buf); + FLUID_FREE(FxUnit->Nodelist[i]); + }; + FxUnit->NumberNodes=0; + + + L(printf("Clear all plugin libraries")); + for (i=0; iNumberLibs; i++){ + + assert(FxUnit->ppvPluginLibs[i]); + dlclose(FxUnit->ppvPluginLibs[i]); + + assert(FxUnit->ppvPluginLibNames[i]); + FLUID_FREE(FxUnit->ppvPluginLibNames[i]); + }; + FxUnit->NumberLibs=0; + + L(printf("Clear all command lines")); + for (i=0; iNumberCommands;i++){ + ii=0; + assert(FxUnit->LADSPA_Command_Sequence[i]); + while (FxUnit->LADSPA_Command_Sequence[i][ii]){ + FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i][ii]); + ii++; + }; + FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i]); + }; + FxUnit->NumberCommands=0; +}; + +int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ + int i; + char * Token; + char ** CommandLine; + fluid_LADSPA_FxUnit_t* FxUnit; + assert(synth); + FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); + if (ac>=FLUID_LADSPA_MaxTokens){ + /* Can't be tested. fluidsynth limits the number of tokens. */ + printf("***Error001***\n" + "Too many ports.\nChange FLUID_LADSPA_MaxTokens!"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + if (ac<2){ + printf("***Error002***\n" + "ladspa_add needs at least two arguments - libname and plugin name!"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + if (FxUnit->NumberCommands>=FLUID_LADSPA_MaxPlugins){ + printf("***Error032***\n" + "Too many plugins.\nChange FLUID_LADSPA_MaxPlugins!"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + /* CommandLine (token sequence) is terminated with NULL. + * Add two more NULLs, so that a chunk of three tokens can be checked later without risk.*/ + + CommandLine=FLUID_ARRAY(char*, (ac+3));assert(CommandLine); + for (i=0; iLADSPA_Command_Sequence[FxUnit->NumberCommands]=CommandLine; + FxUnit->NumberCommands++; + return(FLUID_OK); +}; + +int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ + int i; + char * Token; + char ** CommandLine; + char * NodeName; + fluid_real_t NodeValue; + fluid_LADSPA_FxUnit_t* FxUnit; + assert(synth); + FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); + + if (ac<2){ + printf("***Error028***\n" + "ladspa_declnode needs two arguments - node name and value!\n"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + if (FxUnit->NumberUserControlNodes>=FLUID_LADSPA_MaxNodes){ + printf("***Error033***\n" + "Too many user-control nodes.\nChange FLUID_LADSPA_MaxNodes!"); + fluid_LADSPA_clear(FxUnit); + return(PrintErrorMessage); + }; + + NodeName=FLUID_STRDUP(av[0]); assert(NodeName); + NodeValue=atof(av[1]); + FxUnit->UserControlNodeNames[FxUnit->NumberUserControlNodes]=NodeName; + FxUnit->UserControlNodeValues[FxUnit->NumberUserControlNodes]=NodeValue; + FxUnit->NumberUserControlNodes++; + return(FLUID_OK); +}; +int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ + int i; + char * Token; + char * NodeName; + fluid_real_t NodeValue; + fluid_LADSPA_FxUnit_t* FxUnit; + fluid_LADSPA_Node_t* CurrentNode; + assert(synth); + FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); + + if (ac!=2){ + printf("***Error029***\n" + "ladspa_setnode needs two arguments - node name and value!\n"); + /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ + return(PrintErrorMessage); + }; + + NodeName=av[0]; assert(NodeName); + NodeValue=atof(av[1]); + + CurrentNode=fluid_LADSPA_RetrieveNode(FxUnit,NodeName); + if (!CurrentNode){ + printf("***Error030***\n" + "The node %s was not found. Please use the full name of a node, that was\n" + "previously declared with ladspa_declnode.\n",NodeName); + /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ + return(PrintErrorMessage); + }; + if (!(CurrentNode->flags & fluid_LADSPA_node_is_user_ctrl)){ + printf("***Error031***\n" + "The node %s is an ordinary control node.\n" + "Only user control nodes can be modified with ladspa_setnode.\n",NodeName); + /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ + return(PrintErrorMessage); + }; + L(printf("ladspa_setnode: Assigning value %f",NodeValue)); + CurrentNode->buf[0]=NodeValue; + return(FLUID_OK); +}; + +int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ + fluid_LADSPA_FxUnit_t* FxUnit; + assert(synth); + FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); + fluid_LADSPA_clear(FxUnit); + return(FLUID_OK); +}; + +void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit){ + /* The synthesis thread is not running anymore. + * Set the bypass switch, so that fluid_LADSPA_clear can proceed.*/ + FxUnit->Bypass=fluid_LADSPA_Bypassed; + fluid_LADSPA_clear(FxUnit); + pthread_cond_destroy(&FxUnit->cond); /* pro forma */ +}; +#endif /*LADSPA*/ diff --git a/src/libs/fluidsynth/src/fluid_ladspa.h b/src/libs/fluidsynth/src/fluid_ladspa.h new file mode 100644 index 0000000000..5f9ba86985 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_ladspa.h @@ -0,0 +1,240 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + + +#ifndef _FLUID_LADSPA_H +#define _FLUID_LADSPA_H + +/*************************************************************** + * + * INCLUDES + */ + +#include "fluidsynth_priv.h" + +#ifdef LADSPA +#include "fluid_list.h" +#include +#include + +/*************************************************************** + * + * DEFINES + */ + +/* How many different plugin libraries may be used at the same time? */ +#define FLUID_LADSPA_MaxLibs 100 +/* How many plugin instances may be used at the same time? */ +#define FLUID_LADSPA_MaxPlugins 100 +/* How many nodes are allowed? */ +#define FLUID_LADSPA_MaxNodes 100 +/* How many tokens are allowed in one command line? (for example 152 => max. 50 port plugin allowed) */ +#define FLUID_LADSPA_MaxTokens 152 +/* What is the maximum path length? */ +#define FLUID_LADSPA_MaxPathLength 512 +/*************************************************************** + * + * ENUM + */ + +typedef enum { + fluid_LADSPA_NoMatch, + fluid_LADSPA_PartialMatch, + fluid_LADSPA_FullMatch +} fluid_LADSPA_Stringmatch_t; + +/* Bypass state of the Fx unit */ +typedef enum { + fluid_LADSPA_Active, + fluid_LADSPA_Bypassed, + fluid_LADSPA_BypassRequest +} fluid_LADSPA_BypassState; + +typedef enum { + fluid_LADSPA_node_is_source=1, + fluid_LADSPA_node_is_sink=2, + fluid_LADSPA_node_is_audio=4, + fluid_LADSPA_node_is_control=8, + fluid_LADSPA_node_is_dummy=16, + fluid_LADSPA_node_is_user_ctrl=32 +} fluid_LADSPA_nodeflags; + +/* fluid_LADSPA_Node_t + * An internal node of the Fx unit. + * A 'node' is the 'glue' that connects several LADSPA plugins. + * Basically it's a real-valued variable (control node) or a real-valued buffer (audio node). + */ +typedef struct { + LADSPA_Data * buf; /*Either the buffer (Audio node) or a single control value (Control node)*/ + char * Name; /* Unique identifier*/ + int InCount; /* How many sources feed into this node? (0 or 1) */ + int OutCount; /* How many other elements take data out of this node? */ + int flags; +} fluid_LADSPA_Node_t; + +/* + * fluid_LADSPA_Fx_t + * Fx unit using LADSPA. + * This includes a number of LADSPA plugins, their libraries, nodes etc. + * The Fx unit connects its input to Fluidsynth and its output to the soundcard. + */ +typedef struct { + /* LADSPA-plugins are in shared libraries (for example aw.so). + * Pointers to them are stored here. A library is uniquely identified through + * its filename (full path).*/ + fluid_synth_t* synth; + + int NumberLibs; + void * ppvPluginLibs[FLUID_LADSPA_MaxLibs]; + char * ppvPluginLibNames[FLUID_LADSPA_MaxLibs]; + + /*List of plugins (descriptor and instance) + * A LADSPA plugin descriptor points to the code, which is executed, when a plugin is run. + * The plugin instance is given as a parameter, when calling. + */ + int NumberPlugins; + const LADSPA_Descriptor * PluginDescriptorTable[FLUID_LADSPA_MaxPlugins]; + LADSPA_Handle * PluginInstanceTable[FLUID_LADSPA_MaxPlugins]; + + /* List of nodes */ + int NumberNodes; + fluid_LADSPA_Node_t * Nodelist[FLUID_LADSPA_MaxNodes]; + + /* List of Command lines + * During the setup phase, each ladspa_add command creates one command sequence. For example: + * ./aw.so alienwah_stereo Input <- Master_L_Synth Output -> Master_R_Synth Parameter <- #42.0 + * Those lists are stored in LADSPA_Command_Sequence. + * One command line results in one plugin => size MaxPlugins. + */ + int NumberCommands; + char ** LADSPA_Command_Sequence[FLUID_LADSPA_MaxPlugins]; + + /* User control nodes + * A user control node is declared at any time before the ladspa_start command. + * It acts as a constant node, but it has a name and can be changed with the ladspa_nodeset command. */ + int NumberUserControlNodes; + char * UserControlNodeNames[FLUID_LADSPA_MaxNodes]; + fluid_real_t UserControlNodeValues[FLUID_LADSPA_MaxNodes]; + + /* Bypass switch + * If set, the LADSPA Fx unit does not touch the signal.*/ + fluid_LADSPA_BypassState Bypass; + + /* Communication between the 'command line' process and the synthesis process. + * A possible conflict situation arises, when fluid_clear is called, and starts to destroy + * the plugins. But the synthesis thread still processes plugins at the same time. The consequences are ugly. + * Therefore ladspa_clear waits for acknowledgement from the synthesis thread, that the Fx unit is bypassed. + * 'cond' is used for the communication, the mutex is required for changing the condition. + */ + pthread_cond_t cond; + pthread_mutex_t mutex; +} fluid_LADSPA_FxUnit_t; + +/* + * misc + */ + +/* Purpose: + * Creates a new Fx unit in bypass mode with default settings. + * It is ready for further calls (add, clear, start). + */ +fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth); + +/* Purpose: + * Applies the master gain (from command line option --gain or gain command). + * Processes one block of sound data (generated from the synthesizer) through + * the LADSPA Fx unit. + * Acknowledges a bypass request. + */ +void fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* Fx_unit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]); + +/* Purpose: + * Returns the node belonging to Name or NULL, if not found + */ +fluid_LADSPA_Node_t* fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name); + +/* Purpose: + * Creates a new node with the given characteristics. + */ +fluid_LADSPA_Node_t* fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags); + +/* Purpose: + * - Resets LADSPA Fx unit to bypass. + * - Removes all plugins from the reverb unit. + * - Releases all libraries. + * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time + * a plugin is recompiled. + */ +void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit); + +/* Purpose: + * Frees all memory and shuts down the Fx block. + * The synthesis thread must be stopped, when calling. + */ +void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit); + + + +/* + * fluid_handle_LADSPA_XXX + * Those functions are called from fluid_cmd, when a command is entered on the command line. + */ + +/* Purpose: + * - Resets LADSPA Fx unit to bypass. + * - Removes all plugins from the reverb unit. + * - Releases all libraries. + * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time + * a plugin is recompiled. + */ +int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +/* Purpose: + * Loads the plugins added with 'ladspa_add' and then start the Fx unit. + * Internal processes: + * - load the LADSPA plugin libraries + * - instantiate the plugins + * - connect the plugins + * - set the bypass switch to 'not bypassed' + */ +int fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +/* Purpose: + * Adds one plugin into the list of the LADSPA Fx unit. + * This is only allowed, while the Fx block is in 'bypass' state (after clear). + */ +int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +/* Purpose: + * Declares a user control node and a value; for further processing in ladspa_start. + */ +int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +/* Purpose: + * Assigns a value to the a user control node + */ +int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +#endif /* LADSPA */ + +#endif /* _FLUID_LADSPA_H */ diff --git a/src/libs/fluidsynth/src/fluid_lash.c b/src/libs/fluidsynth/src/fluid_lash.c new file mode 100644 index 0000000000..744f89708a --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_lash.c @@ -0,0 +1,300 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#include "fluid_lash.h" +#include "fluid_synth.h" + +#include /* for usleep() */ +#include +#include +#include +#include +#include + +static void fluid_lash_save (fluid_synth_t * synth); +static void fluid_lash_load (fluid_synth_t * synth, const char * filename); +static void *fluid_lash_run (void * data); + +/* + * lash client - this symbol needs to be in the library else + * all clients would need a fluid_lash_client symbol. + */ +#ifdef HAVE_LASH +lash_client_t * fluid_lash_client; +#else +cca_client_t * fluid_lash_client; +#endif + +static pthread_t fluid_lash_thread; + + +#ifdef HAVE_LASH + +fluid_lash_args_t * +fluid_lash_extract_args (int * pargc, char *** pargv) +{ + return lash_extract_args (pargc, pargv); +} + +int +fluid_lash_connect (fluid_lash_args_t * args) +{ + fluid_lash_client = lash_init (args, PACKAGE, LASH_Config_Data_Set | LASH_Terminal, LASH_PROTOCOL (2,0)); + return fluid_lash_client && lash_enabled (fluid_lash_client); +} + +void +fluid_lash_create_thread (fluid_synth_t * synth) +{ + pthread_create (&fluid_lash_thread, NULL, fluid_lash_run, synth); +} + +static void +fluid_lash_save (fluid_synth_t * synth) +{ + int i; + int sfcount; + fluid_sfont_t * sfont; + lash_config_t * config; + char num[32]; + + sfcount = fluid_synth_sfcount (synth); + + config = lash_config_new (); + lash_config_set_key (config, "soundfont count"); + lash_config_set_value_int (config, sfcount); + lash_send_config (fluid_lash_client, config); + + for (i = sfcount - 1; i >= 0; i--) + { + sfont = fluid_synth_get_sfont (synth, i); + config = lash_config_new (); + + sprintf (num, "%d", i); + + lash_config_set_key (config, num); + lash_config_set_value_string (config, sfont->get_name (sfont)); + + lash_send_config (fluid_lash_client, config); + } +} + +static void +fluid_lash_load (fluid_synth_t * synth, const char * filename) +{ + fluid_synth_sfload (synth, filename, 1); +} + +static void * +fluid_lash_run (void * data) +{ + lash_event_t * event; + lash_config_t * config; + fluid_synth_t * synth; + int done = 0; + int err; + int pending_restores = 0; + + synth = (fluid_synth_t *) data; + + while (!done) + { + while ( (event = lash_get_event (fluid_lash_client)) ) + { + switch (lash_event_get_type (event)) + { + case LASH_Save_Data_Set: + fluid_lash_save (synth); + lash_send_event (fluid_lash_client, event); + break; + case LASH_Restore_Data_Set: + lash_event_destroy (event); + break; + case LASH_Quit: + err = kill (getpid(), SIGQUIT); + if (err) + fprintf (stderr, "%s: error sending signal: %s", + __FUNCTION__, strerror (errno)); + lash_event_destroy (event); + done = 1; + break; + case LASH_Server_Lost: + lash_event_destroy (event); + done = 1; + break; + default: + fprintf (stderr, "Recieved unknown LASH event of type %d\n", lash_event_get_type (event)); + lash_event_destroy (event); + break; + } + } + + while ( (config = lash_get_config (fluid_lash_client)) ) + { + if (strcmp (lash_config_get_key (config), "soundfont count") == 0) + pending_restores = lash_config_get_value_int (config); + else + { + fluid_lash_load (synth, lash_config_get_value_string (config)); + pending_restores--; + } + lash_config_destroy (config); + + if (!pending_restores) + { + event = lash_event_new_with_type (LASH_Restore_Data_Set); + lash_send_event (fluid_lash_client, event); + } + } + + usleep (10000); + } + + return NULL; +} + + +#else /* depricated LADCCA support, will remove someday */ + + +fluid_lash_args_t * +fluid_lash_extract_args (int * pargc, char *** pargv) +{ + return cca_extract_args (pargc, pargv); +} + +int +fluid_lash_connect (fluid_lash_args_t * args) +{ + fluid_lash_client = cca_init (args, PACKAGE, CCA_Config_Data_Set | CCA_Terminal, CCA_PROTOCOL (2,0)); + return fluid_lash_client && cca_enabled (fluid_lash_client); +} + +void +fluid_lash_create_thread (fluid_synth_t * synth) +{ + pthread_create (&fluid_lash_thread, NULL, fluid_lash_run, synth); +} + +static void +fluid_lash_save (fluid_synth_t * synth) +{ + int i; + int sfcount; + fluid_sfont_t * sfont; + cca_config_t * config; + char num[32]; + + sfcount = fluid_synth_sfcount (synth); + + config = cca_config_new (); + cca_config_set_key (config, "soundfont count"); + cca_config_set_value_int (config, sfcount); + cca_send_config (fluid_lash_client, config); + + for (i = sfcount - 1; i >= 0; i--) + { + sfont = fluid_synth_get_sfont (synth, i); + config = cca_config_new (); + + sprintf (num, "%d", i); + + cca_config_set_key (config, num); + cca_config_set_value_string (config, sfont->get_name (sfont)); + + cca_send_config (fluid_lash_client, config); + } +} + +static void +fluid_lash_load (fluid_synth_t * synth, const char * filename) +{ + fluid_synth_sfload (synth, filename, 1); +} + +/* LADCCA thread */ +static void * +fluid_lash_run (void * data) +{ + cca_event_t * event; + cca_config_t * config; + fluid_synth_t * synth; + int done = 0; + int err; + int pending_restores = 0; + + synth = (fluid_synth_t *) data; + + while (!done) + { + while ( (event = cca_get_event (fluid_lash_client)) ) + { + switch (cca_event_get_type (event)) + { + case CCA_Save_Data_Set: + fluid_lash_save (synth); + cca_send_event (fluid_lash_client, event); + break; + case CCA_Restore_Data_Set: + cca_event_destroy (event); + break; + case CCA_Quit: + err = kill (getpid(), SIGQUIT); + if (err) + fprintf (stderr, "%s: error sending signal: %s", + __FUNCTION__, strerror (errno)); + cca_event_destroy (event); + done = 1; + break; + case CCA_Server_Lost: + cca_event_destroy (event); + done = 1; + break; + default: + fprintf (stderr, "Recieved unknown LADCCA event of type %d\n", cca_event_get_type (event)); + cca_event_destroy (event); + break; + } + } + + while ( (config = cca_get_config (fluid_lash_client)) ) + { + if (strcmp (cca_config_get_key (config), "soundfont count") == 0) + pending_restores = cca_config_get_value_int (config); + else + { + fluid_lash_load (synth, cca_config_get_value_string (config)); + pending_restores--; + } + cca_config_destroy (config); + + if (!pending_restores) + { + event = cca_event_new_with_type (CCA_Restore_Data_Set); + cca_send_event (fluid_lash_client, event); + } + } + + usleep (10000); + } + + return NULL; +} + +#endif /* #if HAVE_LASH #else */ diff --git a/src/libs/fluidsynth/src/fluid_lash.h b/src/libs/fluidsynth/src/fluid_lash.h new file mode 100644 index 0000000000..00ad405ee0 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_lash.h @@ -0,0 +1,51 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#include "config.h" + +#if defined(HAVE_LASH) || defined(HAVE_LADCCA) + +#include "fluid_synth.h" + +#define LASH_ENABLED 1 + +#ifdef HAVE_LASH + +#include +extern lash_client_t * fluid_lash_client; +#define fluid_lash_args_t lash_args_t +#define fluid_lash_alsa_client_id lash_alsa_client_id +#define fluid_lash_jack_client_name lash_jack_client_name + +#else /* old depricated LADCCA support which will be removed someday */ + +#include +extern cca_client_t * fluid_lash_client; +#define fluid_lash_args_t cca_args_t +#define fluid_lash_alsa_client_id cca_alsa_client_id +#define fluid_lash_jack_client_name cca_jack_client_name + +#endif + + +fluid_lash_args_t *fluid_lash_extract_args (int * pargc, char *** pargv); +int fluid_lash_connect (fluid_lash_args_t * args); +void fluid_lash_create_thread (fluid_synth_t * synth); + +#endif /* defined(HAVE_LASH) || defined(HAVE_LADCCA) */ diff --git a/src/libs/fluidsynth/src/fluid_list.c b/src/libs/fluidsynth/src/fluid_list.c new file mode 100644 index 0000000000..31cf49ac9e --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_list.c @@ -0,0 +1,257 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + + + +#include "fluid_list.h" + + +fluid_list_t* +new_fluid_list(void) +{ + fluid_list_t* list; + list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t)); + list->data = NULL; + list->next = NULL; + return list; +} + +void +delete_fluid_list(fluid_list_t *list) +{ + fluid_list_t *next; + while (list) { + next = list->next; + FLUID_FREE(list); + list = next; + } +} + +void +delete1_fluid_list(fluid_list_t *list) +{ + if (list) { + FLUID_FREE(list); + } +} + +fluid_list_t* +fluid_list_append(fluid_list_t *list, void* data) +{ + fluid_list_t *new_list; + fluid_list_t *last; + + new_list = new_fluid_list(); + new_list->data = data; + + if (list) + { + last = fluid_list_last(list); + /* g_assert (last != NULL); */ + last->next = new_list; + + return list; + } + else + return new_list; +} + +fluid_list_t* +fluid_list_prepend(fluid_list_t *list, void* data) +{ + fluid_list_t *new_list; + + new_list = new_fluid_list(); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +fluid_list_t* +fluid_list_nth(fluid_list_t *list, int n) +{ + while ((n-- > 0) && list) { + list = list->next; + } + + return list; +} + +fluid_list_t* +fluid_list_remove(fluid_list_t *list, void* data) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while (tmp) { + if (tmp->data == data) { + if (prev) { + prev->next = tmp->next; + } + if (list == tmp) { + list = list->next; + } + tmp->next = NULL; + delete_fluid_list(tmp); + + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +fluid_list_t* +fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while (tmp) { + if (tmp == link) { + if (prev) { + prev->next = tmp->next; + } + if (list == tmp) { + list = list->next; + } + tmp->next = NULL; + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +static fluid_list_t* +fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func) +{ + fluid_list_t list, *l; + + l = &list; + + while (l1 && l2) { + if (compare_func(l1->data,l2->data) < 0) { + l = l->next = l1; + l1 = l1->next; + } else { + l = l->next = l2; + l2 = l2->next; + } + } + l->next= l1 ? l1 : l2; + + return list.next; +} + +fluid_list_t* +fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) +{ + fluid_list_t *l1, *l2; + + if (!list) { + return NULL; + } + if (!list->next) { + return list; + } + + l1 = list; + l2 = list->next; + + while ((l2 = l2->next) != NULL) { + if ((l2 = l2->next) == NULL) + break; + l1=l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return fluid_list_sort_merge(fluid_list_sort(list, compare_func), + fluid_list_sort(l2, compare_func), + compare_func); +} + + +fluid_list_t* +fluid_list_last(fluid_list_t *list) +{ + if (list) { + while (list->next) + list = list->next; + } + + return list; +} + +int +fluid_list_size(fluid_list_t *list) +{ + int n = 0; + while (list) { + n++; + list = list->next; + } + return n; +} + +fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data) +{ + fluid_list_t *new_list; + fluid_list_t *cur; + fluid_list_t *prev = NULL; + + new_list = new_fluid_list(); + new_list->data = data; + + cur = list; + while ((n-- > 0) && cur) { + prev = cur; + cur = cur->next; + } + + new_list->next = cur; + + if (prev) { + prev->next = new_list; + return list; + } else { + return new_list; + } +} diff --git a/src/libs/fluidsynth/src/fluid_list.h b/src/libs/fluidsynth/src/fluid_list.h new file mode 100644 index 0000000000..6d5779bf41 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_list.h @@ -0,0 +1,61 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _FLUID_LIST_H +#define _FLUID_LIST_H + +#include "fluidsynth_priv.h" + +/* + * + * Lists + * + * A sound font loader has to pack the data from the .SF2 file into + * list structures of this type. + * + */ + +typedef struct _fluid_list_t fluid_list_t; + +typedef int (*fluid_compare_func_t)(void* a, void* b); + +struct _fluid_list_t +{ + void* data; + fluid_list_t *next; +}; + +fluid_list_t* new_fluid_list(void); +void delete_fluid_list(fluid_list_t *list); +void delete1_fluid_list(fluid_list_t *list); +fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func); +fluid_list_t* fluid_list_append(fluid_list_t *list, void* data); +fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data); +fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data); +fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); +fluid_list_t* fluid_list_nth(fluid_list_t *list, int n); +fluid_list_t* fluid_list_last(fluid_list_t *list); +fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data); +int fluid_list_size(fluid_list_t *list); + +#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) +#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) + + +#endif /* _FLUID_LIST_H */ diff --git a/src/libs/fluidsynth/src/fluid_mdriver.c b/src/libs/fluidsynth/src/fluid_mdriver.c new file mode 100644 index 0000000000..0c20bb86dc --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_mdriver.c @@ -0,0 +1,184 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_mdriver.h" +#include "fluid_settings.h" + + +/* ALSA */ +#if ALSA_SUPPORT +fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); +int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p); +void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings); + +fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); +int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p); +void fluid_alsa_seq_driver_settings(fluid_settings_t* settings); +#endif + +/* OSS */ +#if OSS_SUPPORT +fluid_midi_driver_t* new_fluid_oss_midi_driver(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); +int delete_fluid_oss_midi_driver(fluid_midi_driver_t* p); +void fluid_oss_midi_driver_settings(fluid_settings_t* settings); +#endif + +/* Windows MIDI service */ +#if WINMIDI_SUPPORT +fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings, + handle_midi_event_func_t handler, + void* event_handler_data); +int delete_fluid_winmidi_driver(fluid_midi_driver_t* p); +#endif + +/* definitions for the MidiShare driver */ +#if MIDISHARE_SUPPORT +fluid_midi_driver_t* new_fluid_midishare_midi_driver(fluid_settings_t* settings, + void* event_handler_data, + handle_midi_event_func_t handler); +int delete_fluid_midishare_midi_driver(fluid_midi_driver_t* p); +#endif + + + +/* + * fluid_mdriver_definition + */ +struct fluid_mdriver_definition_t { + char* name; + fluid_midi_driver_t* (*new)(fluid_settings_t* settings, + handle_midi_event_func_t event_handler, + void* event_handler_data); + int (*free)(fluid_midi_driver_t* p); + void (*settings)(fluid_settings_t* settings); +}; + + +struct fluid_mdriver_definition_t fluid_midi_drivers[] = { +#if OSS_SUPPORT + { "oss", + new_fluid_oss_midi_driver, + delete_fluid_oss_midi_driver, + fluid_oss_midi_driver_settings }, +#endif +#if ALSA_SUPPORT + { "alsa_raw", + new_fluid_alsa_rawmidi_driver, + delete_fluid_alsa_rawmidi_driver, + fluid_alsa_rawmidi_driver_settings }, + { "alsa_seq", + new_fluid_alsa_seq_driver, + delete_fluid_alsa_seq_driver, + fluid_alsa_seq_driver_settings }, +#endif +#if WINMIDI_SUPPORT + { "winmidi", + new_fluid_winmidi_driver, + delete_fluid_winmidi_driver, + NULL }, +#endif +#if MIDISHARE_SUPPORT + { "midishare", + new_fluid_midishare_midi_driver, + delete_fluid_midishare_midi_driver, + NULL }, +#endif + { NULL, NULL, NULL, NULL } +}; + + + +void fluid_midi_driver_settings(fluid_settings_t* settings) +{ + int i; + + /* Set the default driver */ +#if ALSA_SUPPORT + fluid_settings_register_str(settings, "midi.driver", "alsa_seq", 0, NULL, NULL); +#elif OSS_SUPPORT + fluid_settings_register_str(settings, "midi.driver", "oss", 0, NULL, NULL); +#elif WINMIDI_SUPPORT + fluid_settings_register_str(settings, "midi.driver", "winmidi", 0, NULL, NULL); +#elif MIDISHARE_SUPPORT + fluid_settings_register_str(settings, "midi.driver", "midishare", 0, NULL, NULL); +#else + fluid_settings_register_str(settings, "midi.driver", "", 0, NULL, NULL); +#endif + + /* Add all drivers to the list of options */ +#if ALSA_SUPPORT + fluid_settings_add_option(settings, "midi.driver", "alsa_seq"); + fluid_settings_add_option(settings, "midi.driver", "alsa_raw"); +#endif +#if OSS_SUPPORT + fluid_settings_add_option(settings, "midi.driver", "oss"); +#endif +#if WINMIDI_SUPPORT + fluid_settings_add_option(settings, "midi.driver", "winmidi"); +#endif +#if MIDISHARE_SUPPORT + fluid_settings_add_option(settings, "midi.driver", "midishare"); +#endif + + for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { + if (fluid_midi_drivers[i].settings != NULL) { + fluid_midi_drivers[i].settings(settings); + } + } +} + + + +fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data) +{ + int i; + fluid_midi_driver_t* driver = NULL; + for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { + if (fluid_settings_str_equal(settings, "midi.driver", fluid_midi_drivers[i].name)) { + FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", fluid_midi_drivers[i].name); + driver = fluid_midi_drivers[i].new(settings, handler, event_handler_data); + if (driver) { + driver->name = fluid_midi_drivers[i].name; + } + return driver; + } + } + + FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver"); + return NULL; +} + +void delete_fluid_midi_driver(fluid_midi_driver_t* driver) +{ + int i; + + for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { + if (fluid_midi_drivers[i].name == driver->name) { + fluid_midi_drivers[i].free(driver); + return; + } + } +} diff --git a/src/libs/fluidsynth/src/fluid_mdriver.h b/src/libs/fluidsynth/src/fluid_mdriver.h new file mode 100644 index 0000000000..8d0dd2b013 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_mdriver.h @@ -0,0 +1,42 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_MDRIVER_H +#define _FLUID_MDRIVER_H + +#include "fluidsynth_priv.h" + +void fluid_midi_driver_settings(fluid_settings_t* settings); + + +/* + * fluid_midi_driver_t + */ + +struct _fluid_midi_driver_t +{ + char* name; + handle_midi_event_func_t handler; + void* data; +}; + + + +#endif /* _FLUID_AUDRIVER_H */ diff --git a/src/libs/fluidsynth/src/fluid_midi.c b/src/libs/fluidsynth/src/fluid_midi.c new file mode 100644 index 0000000000..04a6529a2d --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_midi.c @@ -0,0 +1,1529 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_midi.h" +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_settings.h" + +/* all outgoing user messages are stored in a global text buffer */ +#define MIDI_MESSAGE_LENGTH 1024 +char midi_message_buffer[MIDI_MESSAGE_LENGTH]; + + +/* Taken from Nagano Daisuke's USB-MIDI driver */ + +static int remains_f0f6[] = { + 0, /** 0xF0 **/ + 2, /** 0XF1 **/ + 3, /** 0XF2 **/ + 2, /** 0XF3 **/ + 2, /** 0XF4 (Undefined by MIDI Spec, and subject to change) **/ + 2, /** 0XF5 (Undefined by MIDI Spec, and subject to change) **/ + 1 /** 0XF6 **/ +}; + +static int remains_80e0[] = { + 3, /** 0x8X Note Off **/ + 3, /** 0x9X Note On **/ + 3, /** 0xAX Poly-key pressure **/ + 3, /** 0xBX Control Change **/ + 2, /** 0xCX Program Change **/ + 2, /** 0xDX Channel pressure **/ + 3 /** 0xEX PitchBend Change **/ +}; + + +/*************************************************************** + * + * MIDIFILE + */ +/* + * new_fluid_midi_file + */ +fluid_midi_file* new_fluid_midi_file(char* filename) +{ + fluid_midi_file* mf; + + mf = FLUID_NEW(fluid_midi_file); + if (mf == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); + + mf->c = -1; + mf->running_status = -1; + mf->fp = FLUID_FOPEN(filename, "rb"); + + if (mf->fp == NULL) { + FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); + FLUID_FREE(mf); + return NULL; + } + + if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { + FLUID_FREE(mf); + return NULL; + } + return mf; +} + +/* + * delete_fluid_midi_file + */ +void delete_fluid_midi_file(fluid_midi_file* mf) +{ + if (mf == NULL) { + return; + } + if (mf->fp != NULL) { + FLUID_FCLOSE(mf->fp); + } + FLUID_FREE(mf); + return; +} + +/* + * fluid_midi_file_getc + */ +int fluid_midi_file_getc(fluid_midi_file* mf) +{ + unsigned char c; + int n; + if (mf->c >= 0) { + c = mf->c; + mf->c = -1; + } else { + n = FLUID_FREAD(&c, 1, 1, mf->fp); + mf->trackpos++; + } + return (int) c; +} + +/* + * fluid_midi_file_push + */ +int fluid_midi_file_push(fluid_midi_file* mf, int c) +{ + mf->c = c; + return FLUID_OK; +} + +/* + * fluid_midi_file_read + */ +int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len) +{ + int num = FLUID_FREAD(buf, 1, len, mf->fp); + mf->trackpos += num; +#if DEBUG + if (num != len) { + FLUID_LOG(FLUID_DBG, "Coulnd't read the requested number of bytes"); + } +#endif + return (num != len)? FLUID_FAILED : FLUID_OK; +} + +/* + * fluid_midi_file_skip + */ +int fluid_midi_file_skip(fluid_midi_file* mf, int skip) +{ + int err = FLUID_FSEEK(mf->fp, skip, SEEK_CUR); + if (err) { + FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); + return FLUID_FAILED; + } + return FLUID_OK; +} + +/* + * fluid_midi_file_read_mthd + */ +int fluid_midi_file_read_mthd(fluid_midi_file* mf) +{ + char mthd[15]; + if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) { + return FLUID_FAILED; + } + if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) || (mthd[9] > 2)) { + FLUID_LOG(FLUID_ERR, "Doesn't look like a MIDI file: invalid MThd header"); + return FLUID_FAILED; + } + mf->type = mthd[9]; + mf->ntracks = (unsigned) mthd[11]; + mf->ntracks += (unsigned int) (mthd[10]) << 16; + if((mthd[12]) < 0){ + mf->uses_smpte = 1; + mf->smpte_fps = -mthd[12]; + mf->smpte_res = (unsigned) mthd[13]; + FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); + return FLUID_FAILED; + } else { + mf->uses_smpte = 0; + mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); + FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); + } + return FLUID_OK; +} + +/* + * fluid_midi_file_load_tracks + */ +int fluid_midi_file_load_tracks(fluid_midi_file* mf, fluid_player_t* player) +{ + int i; + for (i = 0; i < mf->ntracks; i++) { + if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) { + return FLUID_FAILED; + } + } + return FLUID_OK; +} + +/* + * fluid_isasciistring + */ +int fluid_isasciistring(char* s) +{ + int i; + int len = (int) FLUID_STRLEN(s); + for (i = 0; i < len; i++) { + if (!fluid_isascii(s[i])) { + return 0; + } + } + return 1; +} + +/* + * fluid_getlength + */ +long fluid_getlength(unsigned char *s) +{ + long i = 0; + i = s[3] | (s[2]<<8) | (s[1]<<16) | (s[0]<<24); + return i; +} + +/* + * fluid_midi_file_read_tracklen + */ +int fluid_midi_file_read_tracklen(fluid_midi_file* mf) +{ + unsigned char length[5]; + if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + return FLUID_FAILED; + } + mf->tracklen = fluid_getlength(length); + mf->trackpos = 0; + mf->eot = 0; + return FLUID_OK; +} + +/* + * fluid_midi_file_eot + */ +int fluid_midi_file_eot(fluid_midi_file* mf) +{ +#if DEBUG + if (mf->trackpos > mf->tracklen) { + printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); + } +#endif + return mf->eot || (mf->trackpos >= mf->tracklen); +} + +/* + * fluid_midi_file_read_track + */ +int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num) +{ + fluid_track_t* track; + unsigned char id[5], length[5]; + int found_track = 0; + int skip; + + if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) { + return FLUID_FAILED; + } + id[4]='\0'; + + while (!found_track){ + + if (fluid_isasciistring((char*) id) == 0) { + FLUID_LOG(FLUID_ERR, "An non-ascii track header found, currupt file"); + return FLUID_FAILED; + + } else if (strcmp((char*) id, "MTrk") == 0) { + + found_track = 1; + + if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) { + return FLUID_FAILED; + } + + track = new_fluid_track(num); + if (track == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + while (!fluid_midi_file_eot(mf)) { + if (fluid_midi_file_read_event(mf, track) != FLUID_OK) { + return FLUID_FAILED; + } + } + + fluid_player_add_track(player, track); + + } else { + found_track = 0; + if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + return FLUID_FAILED; + } + skip = fluid_getlength(length); +/* fseek(mf->fp, skip, SEEK_CUR); */ + if (fluid_midi_file_skip(mf, skip) != FLUID_OK) { + return FLUID_FAILED; + } + } + } + if (feof(mf->fp)) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + return FLUID_OK; +} + +/* + * fluid_midi_file_read_varlen + */ +int fluid_midi_file_read_varlen(fluid_midi_file* mf) +{ + int i; + int c; + mf->varlen = 0; + for (i = 0;;i++) { + if (i == 4) { + FLUID_LOG(FLUID_ERR, "Invalid variable length number"); + return FLUID_FAILED; + } + c = fluid_midi_file_getc(mf); + if (c < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + if (c & 0x80){ + mf->varlen |= (int) (c & 0x7F); + mf->varlen <<= 7; + } else { + mf->varlen += c; + break; + } + } + return FLUID_OK; +} + +/* + * fluid_midi_file_read_event + */ +int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track) +{ + int dtime; + int status; + int type; + int tempo; + unsigned char* metadata = NULL; + unsigned char* dyn_buf = NULL; + unsigned char static_buf[256]; + int nominator, denominator, clocks, notes, sf, mi; + fluid_midi_event_t* evt; + int channel = 0; + int param1 = 0; + int param2 = 0; + + /* read the delta-time of the event */ + if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + return FLUID_FAILED; + } + dtime = mf->varlen; + + /* read the status byte */ + status = fluid_midi_file_getc(mf); + if (status < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* not a valid status byte: use the running status instead */ + if ((status & 0x80) == 0) { + if ((mf->running_status & 0x80) == 0) { + FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); + return FLUID_FAILED; + } + fluid_midi_file_push(mf, status); + status = mf->running_status; + } + + /* check what message we have */ + if (status & 0x80) { + mf->running_status = status; + + if ((status == MIDI_SYSEX) || (status == MIDI_EOX)) { /* system exclusif */ + /* + * Sysex messages are not handled yet + */ + /* read the length of the message */ + if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + return FLUID_FAILED; + } + + if (mf->varlen) { + + if (mf->varlen < 255) { + metadata = &static_buf[0]; + } else { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); + dyn_buf = FLUID_MALLOC(mf->varlen + 1); + if (dyn_buf == NULL) { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + metadata = dyn_buf; + } + + /* read the data of the message */ + if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { + if (dyn_buf) { + FLUID_FREE(dyn_buf); + } + return FLUID_FAILED; + } + + if (dyn_buf) { + FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); + FLUID_FREE(dyn_buf); + } + } + + return FLUID_OK; + + } else if (status == MIDI_META_EVENT) { /* meta events */ + + int result = FLUID_OK; + + /* get the type of the meta message */ + type = fluid_midi_file_getc(mf); + if (type < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* get the length of the data part */ + if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + return FLUID_FAILED; + } + + if (mf->varlen < 255) { + metadata = &static_buf[0]; + } else { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); + dyn_buf = FLUID_MALLOC(mf->varlen + 1); + if (dyn_buf == NULL) { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + metadata = dyn_buf; + } + + /* read the data */ + if (mf->varlen) + { + if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { + if (dyn_buf) { + FLUID_FREE(dyn_buf); + } + return FLUID_FAILED; + } + } + + /* handle meta data */ + switch (type) { + + case MIDI_COPYRIGHT: + metadata[mf->varlen] = 0; + break; + + case MIDI_TRACK_NAME: + metadata[mf->varlen] = 0; + fluid_track_set_name(track, (char*) metadata); + break; + + case MIDI_INST_NAME: + metadata[mf->varlen] = 0; + break; + + case MIDI_LYRIC: + break; + + case MIDI_MARKER: + break; + + case MIDI_CUE_POINT: + break; /* don't care much for text events */ + + case MIDI_EOT: + if (mf->varlen != 0) { + FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); + result = FLUID_FAILED; + break; + } + mf->eot = 1; + break; + + case MIDI_SET_TEMPO: + if (mf->varlen != 3) { + FLUID_LOG(FLUID_ERR, "Invalid length for SetTempo meta event"); + result = FLUID_FAILED; + break; + } + tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; + evt = new_fluid_midi_event(); + if (evt == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + evt->dtime = dtime; + evt->type = MIDI_SET_TEMPO; + evt->channel = 0; + evt->param1 = tempo; + evt->param2 = 0; + fluid_track_add_event(track, evt); + break; + + case MIDI_SMPTE_OFFSET: + if (mf->varlen != 5) { + FLUID_LOG(FLUID_ERR, "Invalid length for SMPTE Offset meta event"); + result = FLUID_FAILED; + break; + } + break; /* we don't use smtp */ + + case MIDI_TIME_SIGNATURE: + if (mf->varlen != 4) { + FLUID_LOG(FLUID_ERR, "Invalid length for TimeSignature meta event"); + result = FLUID_FAILED; + break; + } + nominator = metadata[0]; + denominator = pow(2.0, (double) metadata[1]); + clocks = metadata[2]; + notes = metadata[3]; + + FLUID_LOG(FLUID_DBG, "signature=%d/%d, metronome=%d, 32nd-notes=%d", + nominator, denominator, clocks, notes); + + break; + + case MIDI_KEY_SIGNATURE: + if (mf->varlen != 2) { + FLUID_LOG(FLUID_ERR, "Invalid length for KeySignature meta event"); + result = FLUID_FAILED; + break; + } + sf = metadata[0]; + mi = metadata[1]; + break; + + case MIDI_SEQUENCER_EVENT: + break; + + default: + break; + } + + if (dyn_buf) { + FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); + FLUID_FREE(dyn_buf); + } + + return result; + + } else { /* channel messages */ + + type = status & 0xf0; + channel = status & 0x0f; + + /* all channel message have at least 1 byte of associated data */ + if ((param1 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + switch (type) { + + case NOTE_ON: + if ((param2 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + break; + + case NOTE_OFF: + if ((param2 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + break; + + case KEY_PRESSURE: + if ((param2 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + break; + + case CONTROL_CHANGE: + if ((param2 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + break; + + case PROGRAM_CHANGE: + break; + + case CHANNEL_PRESSURE: + break; + + case PITCH_BEND: + if ((param2 = fluid_midi_file_getc(mf)) < 0) { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); + param2 = 0; + break; + + default: + /* Can't possibly happen !? */ + FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + return FLUID_FAILED; + } + evt = new_fluid_midi_event(); + if (evt == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + evt->dtime = dtime; + evt->type = type; + evt->channel = channel; + evt->param1 = param1; + evt->param2 = param2; + fluid_track_add_event(track, evt); + } + } + return FLUID_OK; +} + +/* + * fluid_midi_file_get_division + */ +int fluid_midi_file_get_division(fluid_midi_file* midifile) +{ + return midifile->division; +} + +/****************************************************** + * + * fluid_track_t + */ + +/* + * new_fluid_midi_event + */ +fluid_midi_event_t* new_fluid_midi_event() +{ + fluid_midi_event_t* evt; + evt = FLUID_NEW(fluid_midi_event_t); + if (evt == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + evt->dtime = 0; + evt->type = 0; + evt->channel = 0; + evt->param1 = 0; + evt->param2 = 0; + evt->next = NULL; + return evt; +} + +/* + * delete_fluid_midi_event + */ +int delete_fluid_midi_event(fluid_midi_event_t* evt) +{ + fluid_midi_event_t *temp; + + while (evt) + { + temp = evt->next; + FLUID_FREE(evt); + evt = temp; + } + return FLUID_OK; +} + +/* + * fluid_midi_event_get_type + */ +int fluid_midi_event_get_type(fluid_midi_event_t* evt) +{ + return evt->type; +} + +/* + * fluid_midi_event_set_type + */ +int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type) +{ + evt->type = type; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_channel + */ +int fluid_midi_event_get_channel(fluid_midi_event_t* evt) +{ + return evt->channel; +} + +/* + * fluid_midi_event_set_channel + */ +int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan) +{ + evt->channel = chan; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_key + */ +int fluid_midi_event_get_key(fluid_midi_event_t* evt) +{ + return evt->param1; +} + +/* + * fluid_midi_event_set_key + */ +int fluid_midi_event_set_key(fluid_midi_event_t* evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_velocity + */ +int fluid_midi_event_get_velocity(fluid_midi_event_t* evt) +{ + return evt->param2; +} + +/* + * fluid_midi_event_set_velocity + */ +int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_control + */ +int fluid_midi_event_get_control(fluid_midi_event_t* evt) +{ + return evt->param1; +} + +/* + * fluid_midi_event_set_control + */ +int fluid_midi_event_set_control(fluid_midi_event_t* evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_value + */ +int fluid_midi_event_get_value(fluid_midi_event_t* evt) +{ + return evt->param2; +} + +/* + * fluid_midi_event_set_value + */ +int fluid_midi_event_set_value(fluid_midi_event_t* evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +int fluid_midi_event_get_program(fluid_midi_event_t* evt) +{ + return evt->param1; +} + +int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +int fluid_midi_event_get_pitch(fluid_midi_event_t* evt) +{ + return evt->param1; +} + +int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +/* + * fluid_midi_event_get_param1 + */ +/* int fluid_midi_event_get_param1(fluid_midi_event_t* evt) */ +/* { */ +/* return evt->param1; */ +/* } */ + +/* + * fluid_midi_event_set_param1 + */ +/* int fluid_midi_event_set_param1(fluid_midi_event_t* evt, int v) */ +/* { */ +/* evt->param1 = v; */ +/* return FLUID_OK; */ +/* } */ + +/* + * fluid_midi_event_get_param2 + */ +/* int fluid_midi_event_get_param2(fluid_midi_event_t* evt) */ +/* { */ +/* return evt->param2; */ +/* } */ + +/* + * fluid_midi_event_set_param2 + */ +/* int fluid_midi_event_set_param2(fluid_midi_event_t* evt, int v) */ +/* { */ +/* evt->param2 = v; */ +/* return FLUID_OK; */ +/* } */ + +/****************************************************** + * + * fluid_track_t + */ + +/* + * new_fluid_track + */ +fluid_track_t* new_fluid_track(int num) +{ + fluid_track_t* track; + track = FLUID_NEW(fluid_track_t); + if (track == NULL) { + return NULL; + } + track->name = NULL; + track->num = num; + track->first = NULL; + track->cur = NULL; + track->last = NULL; + track->ticks = 0; + return track; +} + +/* + * delete_fluid_track + */ +int delete_fluid_track(fluid_track_t* track) +{ + if (track->name != NULL) { + FLUID_FREE(track->name); + } + if (track->first != NULL) { + delete_fluid_midi_event(track->first); + } + FLUID_FREE(track); + return FLUID_OK; +} + +/* + * fluid_track_set_name + */ +int fluid_track_set_name(fluid_track_t* track, char* name) +{ + int len; + if (track->name != NULL) { + FLUID_FREE(track->name); + } + if (name == NULL) { + track->name = NULL; + return FLUID_OK; + } + len = FLUID_STRLEN(name); + track->name = FLUID_MALLOC(len + 1); + if (track->name == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + FLUID_STRCPY(track->name, name); + return FLUID_OK; +} + +/* + * fluid_track_get_name + */ +char* fluid_track_get_name(fluid_track_t* track) +{ + return track->name; +} + +/* + * fluid_track_get_duration + */ +int fluid_track_get_duration(fluid_track_t* track) +{ + int time = 0; + fluid_midi_event_t* evt = track->first; + while (evt != NULL) { + time += evt->dtime; + evt = evt->next; + } + return time; +} + +/* + * fluid_track_count_events + */ +int fluid_track_count_events(fluid_track_t* track, int* on, int* off) +{ + fluid_midi_event_t* evt = track->first; + while (evt != NULL) { + if (evt->type == NOTE_ON) { + (*on)++; + } else if (evt->type == NOTE_OFF) { + (*off)++; + } + evt = evt->next; + } + return FLUID_OK; +} + +/* + * fluid_track_add_event + */ +int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt) +{ + evt->next = NULL; + if (track->first == NULL) { + track->first = evt; + track->cur = evt; + track->last = evt; + } else { + track->last->next = evt; + track->last = evt; + } + return FLUID_OK; +} + +/* + * fluid_track_first_event + */ +fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track) +{ + track->cur = track->first; + return track->cur; +} + +/* + * fluid_track_next_event + */ +fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track) +{ + if (track->cur != NULL) { + track->cur = track->cur->next; + } + return track->cur; +} + +/* + * fluid_track_reset + */ +int +fluid_track_reset(fluid_track_t* track) +{ + track->ticks = 0; + track->cur = track->first; + return FLUID_OK; +} + +/* + * fluid_track_send_events + */ +int +fluid_track_send_events(fluid_track_t* track, + fluid_synth_t* synth, + fluid_player_t* player, + unsigned int ticks) +{ + int status = FLUID_OK; + fluid_midi_event_t* event; + + while (1) { + + event = track->cur; + if (event == NULL) { + return status; + } + +/* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */ +/* track->num, */ +/* ticks, */ +/* track->ticks, */ +/* event->dtime, */ +/* track->ticks + event->dtime); */ + + if (track->ticks + event->dtime > ticks) { + return status; + } + + + track->ticks += event->dtime; + status = fluid_midi_send_event(synth, player, event); + fluid_track_next_event(track); + + } + return status; +} + +/****************************************************** + * + * fluid_player + */ +/* + * new_fluid_player + */ +fluid_player_t* new_fluid_player(fluid_synth_t* synth) +{ + int i; + fluid_player_t* player; + player = FLUID_NEW(fluid_player_t); + if (player == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + player->status = FLUID_PLAYER_READY; + player->loop = 0; + player->ntracks = 0; + for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { + player->track[i] = NULL; + } + player->synth = synth; + player->timer = NULL; + player->playlist = NULL; + player->current_file = NULL; + player->division = 0; + player->send_program_change = 1; + player->miditempo = 480000; + player->deltatime = 4.0; + return player; +} + +/* + * delete_fluid_player + */ +int delete_fluid_player(fluid_player_t* player) +{ + if (player == NULL) { + return FLUID_OK; + } + fluid_player_stop(player); + fluid_player_reset(player); + FLUID_FREE(player); + return FLUID_OK; +} + +int fluid_player_reset(fluid_player_t* player) +{ + int i; + + for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { + if (player->track[i] != NULL) { + delete_fluid_track(player->track[i]); + player->track[i] = NULL; + } + } + player->current_file = NULL; + player->status = FLUID_PLAYER_READY; + player->loop = 0; + player->ntracks = 0; + player->division = 0; + player->send_program_change = 1; + player->miditempo = 480000; + player->deltatime = 4.0; + return 0; +} + +/* + * fluid_player_add_track + */ +int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track) +{ + if (player->ntracks < MAX_NUMBER_OF_TRACKS) { + player->track[player->ntracks++] = track; + return FLUID_OK; + } else { + return FLUID_FAILED; + } +} + +/* + * fluid_player_count_tracks + */ +int fluid_player_count_tracks(fluid_player_t* player) +{ + return player->ntracks; +} + +/* + * fluid_player_get_track + */ +fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i) +{ + if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) { + return player->track[i]; + } else { + return NULL; + } +} + +int fluid_player_add(fluid_player_t* player, char* midifile) +{ + char *s = FLUID_STRDUP(midifile); + player->playlist = fluid_list_append(player->playlist, s); + return 0; +} + +/* + * fluid_player_load + */ +int fluid_player_load(fluid_player_t* player, char *filename) +{ + fluid_midi_file* midifile; + + midifile = new_fluid_midi_file(filename); + if (midifile == NULL) { + return FLUID_FAILED; + } + player->division = fluid_midi_file_get_division(midifile); + + /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ + + if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK){ + return FLUID_FAILED; + } + delete_fluid_midi_file(midifile); + return FLUID_OK; +} + +/* + * fluid_player_callback + */ +int fluid_player_callback(void* data, unsigned int msec) +{ + int i; + int status = FLUID_PLAYER_DONE; + fluid_player_t* player; + fluid_synth_t* synth; + player = (fluid_player_t*) data; + synth = player->synth; + + /* Load the next file if necessary */ + while (player->current_file == NULL) { + + if (player->playlist == NULL) { + return 0; + } + + fluid_player_reset(player); + + player->current_file = fluid_list_get(player->playlist); + player->playlist = fluid_list_next(player->playlist); + + FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, player->current_file); + + if (fluid_player_load(player, player->current_file) == FLUID_OK) { + + player->begin_msec = msec; + player->start_msec = msec; + player->start_ticks = 0; + player->cur_ticks = 0; + + for (i = 0; i < player->ntracks; i++) { + if (player->track[i] != NULL) { + fluid_track_reset(player->track[i]); + } + } + + } else { + player->current_file = NULL; + } + } + + player->cur_msec = msec; + player->cur_ticks = (player->start_ticks + + (int) ((double) (player->cur_msec - player->start_msec) / player->deltatime)); + + for (i = 0; i < player->ntracks; i++) { + if (!fluid_track_eot(player->track[i])) { + status = FLUID_PLAYER_PLAYING; + if (fluid_track_send_events(player->track[i], synth, player, player->cur_ticks) != FLUID_OK) { + /* */ + } + } + } + + player->status = status; + + if (player->status == FLUID_PLAYER_DONE) { + FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", + __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0); + player->current_file = NULL; + } + + return 1; +} + +/* + * fluid_player_play + */ +int fluid_player_play(fluid_player_t* player) +{ + if (player->status == FLUID_PLAYER_PLAYING) { + return FLUID_OK; + } + + if (player->playlist == NULL) { + return FLUID_OK; + } + + player->status = FLUID_PLAYER_PLAYING; + + player->timer = new_fluid_timer((int) player->deltatime, fluid_player_callback, + (void*) player, 1, 0); + if (player->timer == NULL) { + return FLUID_FAILED; + } + return FLUID_OK; +} + +/* + * fluid_player_stop + */ +int fluid_player_stop(fluid_player_t* player) +{ + if (player->timer != NULL) { + delete_fluid_timer(player->timer); + } + player->status = FLUID_PLAYER_DONE; + player->timer = NULL; + return FLUID_OK; +} + +/* + * fluid_player_set_loop + */ +int fluid_player_set_loop(fluid_player_t* player, int loop) +{ + player->loop = loop; + return FLUID_OK; +} + +/* + * fluid_player_set_midi_tempo + */ +int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo) +{ + player->miditempo = tempo; + player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */ + player->start_msec = player->cur_msec; + player->start_ticks = player->cur_ticks; + + FLUID_LOG(FLUID_DBG,"tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", + tempo, player->deltatime, player->cur_msec, player->cur_ticks); + + return FLUID_OK; +} + +/* + * fluid_player_set_bpm + */ +int fluid_player_set_bpm(fluid_player_t* player, int bpm) +{ + return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm)); +} + +/* + * fluid_player_join + */ +int fluid_player_join(fluid_player_t* player) +{ + return player->timer? fluid_timer_join(player->timer) : FLUID_OK; +} + +/************************************************************************ + * MIDI PARSER + * + */ + +/* + * new_fluid_midi_parser + */ +fluid_midi_parser_t* new_fluid_midi_parser() +{ + fluid_midi_parser_t* parser; + parser = FLUID_NEW(fluid_midi_parser_t); + if (parser == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ + return parser; +} + +/* + * delete_fluid_midi_parser + */ +int delete_fluid_midi_parser(fluid_midi_parser_t* parser) +{ + FLUID_FREE(parser); + return FLUID_OK; +} + +/* + * fluid_midi_parser_parse + * + * Purpose: + * The MIDI byte stream is fed into the parser, one byte at a time. + * As soon as the parser has recognized an event, it will return it. + * Otherwise it returns NULL. + */ +fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c) +{ + /*********************************************************************/ + /* 'Process' system real-time messages */ + /*********************************************************************/ + /* There are not too many real-time messages that are of interest here. + * They can occur anywhere, even in the middle of a noteon message! + * Real-time range: 0xF8 .. 0xFF + * Note: Real-time does not affect (running) status. + */ + if (c >= 0xF8){ + if (c == MIDI_SYSTEM_RESET){ + parser->event.type = c; + parser->status = 0; /* clear the status */ + return &parser->event; + }; + return NULL; + }; + + /*********************************************************************/ + /* 'Process' system common messages (again, just skip them) */ + /*********************************************************************/ + /* There are no system common messages that are of interest here. + * System common range: 0xF0 .. 0xF7 + */ + + if (c > 0xF0){ + /* MIDI specs say: To ignore a non-real-time message, just discard all data up to + * the next status byte. + * And our parser will ignore data that is received without a valid status. + * Note: system common cancels running status. */ + parser->status = 0; + return NULL; + }; + + /*********************************************************************/ + /* Process voice category messages: */ + /*********************************************************************/ + /* Now that we have handled realtime and system common messages, only + * voice messages are left. + * Only a status byte has bit # 7 set. + * So no matter the status of the parser (in case we have lost sync), + * as soon as a byte >= 0x80 comes in, we are dealing with a status byte + * and start a new event. + */ + + if (c & 0x80){ + parser->channel = c & 0x0F; + parser->status = c & 0xF0; + /* The event consumes x bytes of data... (subtract 1 for the status byte) */ + parser->nr_bytes_total=fluid_midi_event_length(parser->status)-1; + /* of which we have read 0 at this time. */ + parser->nr_bytes = 0; + return NULL; + }; + + /*********************************************************************/ + /* Process data */ + /*********************************************************************/ + /* If we made it this far, then the received char belongs to the data + * of the last event. */ + if (parser->status == 0){ + /* We are not interested in the event currently received. + * Discard the data. */ + return NULL; + }; + + /* Store the first couple of bytes */ + if (parser->nr_bytes < FLUID_MIDI_PARSER_MAX_PAR){ + parser->p[parser->nr_bytes]=c; + }; + parser->nr_bytes++; + + /* Do we still need more data to get this event complete? */ + if (parser->nr_bytes < parser->nr_bytes_total){ + return NULL; + }; + + /*********************************************************************/ + /* Send the event */ + /*********************************************************************/ + /* The event is ready-to-go. + * About 'running status': + * The MIDI protocol has a built-in compression mechanism. If several similar events are sent + * in-a-row, for example note-ons, then the event type is only sent once. For this case, + * the last event type (status) is remembered. + * We simply keep the status as it is, just reset + * the parameter counter. If another status byte comes in, it will overwrite the status. */ + parser->event.type = parser->status; + parser->event.channel = parser->channel; + parser->nr_bytes = 0; /* Related to running status! */ + switch (parser->status){ + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + parser->event.param1 = parser->p[0]; /* For example key number */ + parser->event.param2 = parser->p[1]; /* For example velocity */ + break; + case PITCH_BEND: + /* Pitch-bend is transmitted with 14-bit precision. */ + parser->event.param1 = ((parser->p[1] << 7) | parser->p[0]); /* Note: '|' does here the same as '+' (no common bits), but might be faster */ + break; + default: + /* Unlikely */ + return NULL; + }; + return &parser->event; +}; + +/* Purpose: + * Returns the length of the MIDI message starting with c. + * Taken from Nagano Daisuke's USB-MIDI driver */ +int fluid_midi_event_length(unsigned char event){ + if ( event < 0xf0 ) { + return remains_80e0[((event-0x80)>>4)&0x0f]; + } else if ( event < 0xf7 ) { + return remains_f0f6[event-0xf0]; + } else { + return 1; + } +} + +/************************************************************************ + * fluid_midi_send_event + * + * This is a utility function that doesn't really belong to any class + * or structure. It is called by fluid_midi_track and fluid_midi_device. + */ +int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* event) +{ + switch (event->type) { + case NOTE_ON: + if (fluid_synth_noteon(synth, event->channel, event->param1, event->param2) != FLUID_OK) { + return FLUID_FAILED; + } + break; + case NOTE_OFF: + if (fluid_synth_noteoff(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } + break; + case CONTROL_CHANGE: + if (fluid_synth_cc(synth, event->channel, event->param1, event->param2) != FLUID_OK) { + return FLUID_FAILED; + } + break; + case MIDI_SET_TEMPO: + if (player != NULL) { + if (fluid_player_set_midi_tempo(player, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } + } + break; + case PROGRAM_CHANGE: + if (fluid_synth_program_change(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } + break; + case PITCH_BEND: + if (fluid_synth_pitch_bend(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } + break; + default: + break; + } + return FLUID_OK; +} + + diff --git a/src/libs/fluidsynth/src/fluid_midi.h b/src/libs/fluidsynth/src/fluid_midi.h new file mode 100644 index 0000000000..15ac14dd84 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_midi.h @@ -0,0 +1,324 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_MIDI_H +#define _FLUID_MIDI_H + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" +#include "fluid_list.h" + +typedef struct _fluid_midi_parser_t fluid_midi_parser_t; + +fluid_midi_parser_t* new_fluid_midi_parser(void); +int delete_fluid_midi_parser(fluid_midi_parser_t* parser); +fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c); + +int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* evt); + + +/*************************************************************** + * + * CONSTANTS & ENUM + */ + + +#define MAX_NUMBER_OF_TRACKS 128 + +enum fluid_midi_event_type { + /* channel messages */ + NOTE_OFF = 0x80, + NOTE_ON = 0x90, + KEY_PRESSURE = 0xa0, + CONTROL_CHANGE = 0xb0, + PROGRAM_CHANGE = 0xc0, + CHANNEL_PRESSURE = 0xd0, + PITCH_BEND = 0xe0, + /* system exclusive */ + MIDI_SYSEX = 0xf0, + /* system common - never in midi files */ + MIDI_TIME_CODE = 0xf1, + MIDI_SONG_POSITION = 0xf2, + MIDI_SONG_SELECT = 0xf3, + MIDI_TUNE_REQUEST = 0xf6, + MIDI_EOX = 0xf7, + /* system real-time - never in midi files */ + MIDI_SYNC = 0xf8, + MIDI_TICK = 0xf9, + MIDI_START = 0xfa, + MIDI_CONTINUE = 0xfb, + MIDI_STOP = 0xfc, + MIDI_ACTIVE_SENSING = 0xfe, + MIDI_SYSTEM_RESET = 0xff, + /* meta event - for midi files only */ + MIDI_META_EVENT = 0xff +}; + +enum fluid_midi_control_change { + BANK_SELECT_MSB = 0x00, + MODULATION_MSB = 0x01, + BREATH_MSB = 0x02, + FOOT_MSB = 0x04, + PORTAMENTO_TIME_MSB = 0x05, + DATA_ENTRY_MSB = 0x06, + VOLUME_MSB = 0x07, + BALANCE_MSB = 0x08, + PAN_MSB = 0x0A, + EXPRESSION_MSB = 0x0B, + EFFECTS1_MSB = 0x0C, + EFFECTS2_MSB = 0x0D, + GPC1_MSB = 0x10, /* general purpose controller */ + GPC2_MSB = 0x11, + GPC3_MSB = 0x12, + GPC4_MSB = 0x13, + BANK_SELECT_LSB = 0x20, + MODULATION_WHEEL_LSB = 0x21, + BREATH_LSB = 0x22, + FOOT_LSB = 0x24, + PORTAMENTO_TIME_LSB = 0x25, + DATA_ENTRY_LSB = 0x26, + VOLUME_LSB = 0x27, + BALANCE_LSB = 0x28, + PAN_LSB = 0x2A, + EXPRESSION_LSB = 0x2B, + EFFECTS1_LSB = 0x2C, + EFFECTS2_LSB = 0x2D, + GPC1_LSB = 0x30, + GPC2_LSB = 0x31, + GPC3_LSB = 0x32, + GPC4_LSB = 0x33, + SUSTAIN_SWITCH = 0x40, + PORTAMENTO_SWITCH = 0x41, + SOSTENUTO_SWITCH = 0x42, + SOFT_PEDAL_SWITCH = 0x43, + LEGATO_SWITCH = 0x45, + HOLD2_SWITCH = 0x45, + SOUND_CTRL1 = 0x46, + SOUND_CTRL2 = 0x47, + SOUND_CTRL3 = 0x48, + SOUND_CTRL4 = 0x49, + SOUND_CTRL5 = 0x4A, + SOUND_CTRL6 = 0x4B, + SOUND_CTRL7 = 0x4C, + SOUND_CTRL8 = 0x4D, + SOUND_CTRL9 = 0x4E, + SOUND_CTRL10 = 0x4F, + GPC5 = 0x50, + GPC6 = 0x51, + GPC7 = 0x52, + GPC8 = 0x53, + PORTAMENTO_CTRL = 0x54, + EFFECTS_DEPTH1 = 0x5B, + EFFECTS_DEPTH2 = 0x5C, + EFFECTS_DEPTH3 = 0x5D, + EFFECTS_DEPTH4 = 0x5E, + EFFECTS_DEPTH5 = 0x5F, + DATA_ENTRY_INCR = 0x60, + DATA_ENTRY_DECR = 0x61, + NRPN_LSB = 0x62, + NRPN_MSB = 0x63, + RPN_LSB = 0x64, + RPN_MSB = 0x65, + ALL_SOUND_OFF = 0x78, + ALL_CTRL_OFF = 0x79, + LOCAL_CONTROL = 0x7A, + ALL_NOTES_OFF = 0x7B, + OMNI_OFF = 0x7C, + OMNI_ON = 0x7D, + POLY_OFF = 0x7E, + POLY_ON = 0x7F +}; + +enum midi_meta_event { + MIDI_COPYRIGHT = 0x02, + MIDI_TRACK_NAME = 0x03, + MIDI_INST_NAME = 0x04, + MIDI_LYRIC = 0x05, + MIDI_MARKER = 0x06, + MIDI_CUE_POINT = 0x07, + MIDI_EOT = 0x2f, + MIDI_SET_TEMPO = 0x51, + MIDI_SMPTE_OFFSET = 0x54, + MIDI_TIME_SIGNATURE = 0x58, + MIDI_KEY_SIGNATURE = 0x59, + MIDI_SEQUENCER_EVENT = 0x7f +}; + +enum fluid_player_status +{ + FLUID_PLAYER_READY, + FLUID_PLAYER_PLAYING, + FLUID_PLAYER_DONE +}; + +enum fluid_driver_status +{ + FLUID_MIDI_READY, + FLUID_MIDI_LISTENING, + FLUID_MIDI_DONE +}; + +/*************************************************************** + * + * TYPE DEFINITIONS & FUNCTION DECLARATIONS + */ + +/* From ctype.h */ +#define fluid_isascii(c) (((c) & ~0x7f) == 0) + + + +/* + * fluid_midi_event_t + */ +struct _fluid_midi_event_t { + fluid_midi_event_t* next; /* Don't use it, it will dissappear. Used in midi tracks. */ + unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ + unsigned char type; /* MIDI event type */ + unsigned char channel; /* MIDI channel */ + unsigned int param1; /* First parameter */ + unsigned int param2; /* Second parameter */ +}; + + +/* + * fluid_track_t + */ +struct _fluid_track_t { + char* name; + int num; + fluid_midi_event_t *first; + fluid_midi_event_t *cur; + fluid_midi_event_t *last; + unsigned int ticks; +}; + +typedef struct _fluid_track_t fluid_track_t; + +fluid_track_t* new_fluid_track(int num); +int delete_fluid_track(fluid_track_t* track); +int fluid_track_set_name(fluid_track_t* track, char* name); +char* fluid_track_get_name(fluid_track_t* track); +int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt); +fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track); +fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track); +int fluid_track_get_duration(fluid_track_t* track); +int fluid_track_reset(fluid_track_t* track); + +int fluid_track_send_events(fluid_track_t* track, + fluid_synth_t* synth, + fluid_player_t* player, + unsigned int ticks); + +#define fluid_track_eot(track) ((track)->cur == NULL) + + +/* + * fluid_player + */ +struct _fluid_player_t { + int status; + int loop; + int ntracks; + fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; + fluid_synth_t* synth; + fluid_timer_t* timer; + fluid_list_t* playlist; + char* current_file; + char send_program_change; /* should we ignore the program changes? */ + int start_ticks; /* the number of tempo ticks passed at the last tempo change */ + int cur_ticks; /* the number of tempo ticks passed */ + int begin_msec; /* the time (msec) of the beginning of the file */ + int start_msec; /* the start time of the last tempo change */ + int cur_msec; /* the current time */ + int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ + double deltatime; /* milliseconds per midi tick. depends on set-tempo */ + unsigned int division; +}; + +int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track); +int fluid_player_callback(void* data, unsigned int msec); +int fluid_player_count_tracks(fluid_player_t* player); +fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i); +int fluid_player_reset(fluid_player_t* player); +int fluid_player_load(fluid_player_t* player, char *filename); + + +/* + * fluid_midi_file + */ +typedef struct { + fluid_file fp; + int running_status; + int c; + int type; + int ntracks; + int uses_smpte; + unsigned int smpte_fps; + unsigned int smpte_res; + unsigned int division; /* If uses_SMPTE == 0 then division is + ticks per beat (quarter-note) */ + double tempo; /* Beats per second (SI rules =) */ + int tracklen; + int trackpos; + int eot; + int varlen; +} fluid_midi_file; + +fluid_midi_file* new_fluid_midi_file(char* filename); +void delete_fluid_midi_file(fluid_midi_file* mf); +int fluid_midi_file_read_mthd(fluid_midi_file* midifile); +int fluid_midi_file_load_tracks(fluid_midi_file* midifile, fluid_player_t* player); +int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num); +int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track); +int fluid_midi_file_read_varlen(fluid_midi_file* mf); +int fluid_midi_file_getc(fluid_midi_file* mf); +int fluid_midi_file_push(fluid_midi_file* mf, int c); +int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len); +int fluid_midi_file_skip(fluid_midi_file* mf, int len); +int fluid_midi_file_read_tracklen(fluid_midi_file* mf); +int fluid_midi_file_eot(fluid_midi_file* mf); +int fluid_midi_file_get_division(fluid_midi_file* midifile); +int fluid_midi_event_length(unsigned char status); + +/* How many parameters may a MIDI event have? */ +#define FLUID_MIDI_PARSER_MAX_PAR 3 + +/* + * fluid_midi_parser_t + */ +struct _fluid_midi_parser_t { + unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */ + unsigned char channel; /* The channel of the event that is received (in case of a channel event) */ + unsigned int nr_bytes; /* How many bytes have been read for the current event? */ + unsigned int nr_bytes_total; /* How many bytes does the current event type include? */ + unsigned short p[FLUID_MIDI_PARSER_MAX_PAR]; /* The parameters */ + fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */ +}; + +int fluid_isasciistring(char* s); +long fluid_getlength(unsigned char *s); + + + +int fluid_midi_router_send_event(fluid_midi_router_t* router, fluid_midi_event_t* event); + + +#endif /* _FLUID_MIDI_H */ diff --git a/src/libs/fluidsynth/src/fluid_midi_router.c b/src/libs/fluidsynth/src/fluid_midi_router.c new file mode 100644 index 0000000000..30a7e89dd7 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_midi_router.c @@ -0,0 +1,879 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + +#include "fluid_midi_router.h" +#include "fluid_midi.h" +#include "fluid_synth.h" +#include "fluid_io.h" + +/* + * new_fluid_midi_router + */ +fluid_midi_router_t* +new_fluid_midi_router(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data) +{ + fluid_midi_router_t* router=NULL; + fluid_midi_router_rule_t* rule=NULL;; + /* create the router */ + router = FLUID_NEW(fluid_midi_router_t); if (router == NULL){ + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + }; + + /* Clear the router, so that error_recovery can safely free all the rules. + * Dump functions are also NULLed. + */ + FLUID_MEMSET(router, 0, sizeof(fluid_midi_router_t)); + /* Retrieve the number of MIDI channels for range limiting */ + fluid_settings_getint(settings, "synth.midi-channels", &router->nr_midi_channels); + + fluid_mutex_init(router->ruletables_mutex); + + router->synth = (fluid_synth_t*) event_handler_data; + router->event_handler=handler; + router->event_handler_data=event_handler_data; + + /* Create the default routing rules + * They accept events on any channel for any range of parameters, + * and route them unchanged ("result=par*1.0+0") */ + + if (fluid_midi_router_create_default_rules(router) != FLUID_OK) goto error_recovery; + + return router; + + error_recovery: + FLUID_LOG(FLUID_ERR, "new_fluid_midi_router failed"); + fluid_midi_router_destroy_all_rules(router); + FLUID_FREE(router); + return NULL; +} + +/* + * delete_fluid_midi_router + */ +int +delete_fluid_midi_router(fluid_midi_router_t* router) +{ + if (router == NULL) { + return FLUID_OK; + } + fluid_midi_router_destroy_all_rules(router); + FLUID_FREE(router); + return FLUID_OK; +} + +/* + * fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router) + * Purpose: + * Frees the used memory. This is used only for shutdown! + */ +void fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router){ + fluid_midi_router_rule_t* rules[6]; + fluid_midi_router_rule_t* current_rule; + fluid_midi_router_rule_t* next_rule; + int i; + + rules[0]=router->note_rules; + rules[1]=router->cc_rules; + rules[2]=router->progchange_rules; + rules[3]=router->pitchbend_rules; + rules[4]=router->channel_pressure_rules; + rules[5]=router->key_pressure_rules; + for (i=0; i < 6; i++){ + current_rule=rules[i]; + while (current_rule){ + next_rule=current_rule->next; + FLUID_FREE(current_rule); + current_rule=next_rule; + }; + }; +} + +/* + * new_fluid_midi_router_rule + */ +fluid_midi_router_rule_t* new_fluid_midi_router_rule(void) +{ + fluid_midi_router_rule_t* rule=FLUID_NEW(fluid_midi_router_rule_t); + if (rule == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(rule, 0, sizeof(fluid_midi_router_rule_t)); + return rule; +}; + +/* + * delete_fluid_midi_router_rule + */ +int +delete_fluid_midi_router_rule(fluid_midi_router_rule_t* rule) +{ + FLUID_FREE(rule); + return FLUID_OK; +} + + +int fluid_midi_router_create_default_rules(fluid_midi_router_t* router) +{ + fluid_midi_router_rule_t** rules[6]; + int i; + + rules[0]=&router->note_rules; + rules[1]=&router->cc_rules; + rules[2]=&router->progchange_rules; + rules[3]=&router->pitchbend_rules; + rules[4]=&router->channel_pressure_rules; + rules[5]=&router->key_pressure_rules; + + for (i=0; i < 6; i++){ + /* Start a new rule. rules[i] is the destination. _end will insert + * a new rule there into the linked list. + */ + if (fluid_midi_router_begin(router, rules[i]) != FLUID_OK) goto error_recovery; + if (fluid_midi_router_end(router) != FLUID_OK) goto error_recovery; + }; + + return FLUID_OK; + error_recovery: + FLUID_LOG(FLUID_ERR, "fluid_midi_router_create_default_rules failed"); + return FLUID_FAILED; +}; + +/* Purpose: + * Resets the new-rule registers in 'router' to their default values. + * Stores the destination (the linked list into which the new rule is inserted by _end) + */ +int fluid_midi_router_begin(fluid_midi_router_t* router, fluid_midi_router_rule_t** dest) +{ + if (!dest) goto error_recovery; + router->dest=dest; + + /* In the days before the router, MIDI data went straight to the synth. + * So there is no need to check for reasonable values here. + */ + router->new_rule_chan_min=0; + router->new_rule_chan_max=999999; + router->new_rule_chan_mul=1.; + router->new_rule_chan_add=0; + router->new_rule_par1_min=0; + router->new_rule_par1_max=999999; + router->new_rule_par1_mul=1.; + router->new_rule_par1_add=0; + router->new_rule_par2_min=0; + router->new_rule_par2_max=999999; + router->new_rule_par2_mul=1.; + router->new_rule_par2_add=0; + + return FLUID_OK; + error_recovery: + FLUID_LOG(FLUID_ERR, "fluid_midi_router_begin failed"); + return FLUID_FAILED; +}; + +/* Purpose: + * Concludes a sequence of commands: + * - router_start type + * - router_chan + * - router_par1 + * - router_par2 + * - router_end (this call). + * Creates a new event from the given parameters and stores it in + * the correct list. + */ + +int fluid_midi_router_end(fluid_midi_router_t* router){ + fluid_midi_router_rule_t* rule=new_fluid_midi_router_rule(); if (rule == NULL) goto error_recovery; + + rule->chan_min=router->new_rule_chan_min; + rule->chan_max=router->new_rule_chan_max; + rule->chan_mul=router->new_rule_chan_mul; + rule->chan_add=router->new_rule_chan_add; + rule->par1_min=router->new_rule_par1_min; + rule->par1_max=router->new_rule_par1_max; + rule->par1_mul=router->new_rule_par1_mul; + rule->par1_add=router->new_rule_par1_add; + rule->par2_min=router->new_rule_par2_min; + rule->par2_max=router->new_rule_par2_max; + rule->par2_mul=router->new_rule_par2_mul; + rule->par2_add=router->new_rule_par2_add; + + /* Now we modify the rules table. Lock it to make sure, that the RT + * thread is not in the middle of an event. Also prevent, that some + * other thread modifies the table at the same time. + */ + + fluid_mutex_lock(router->ruletables_mutex); + + rule->next= *(router->dest); + *router->dest=rule; + + fluid_mutex_unlock(router->ruletables_mutex); + + return FLUID_OK; + + error_recovery: + FLUID_LOG(FLUID_ERR, "fluid_midi_router_end failed"); + delete_fluid_midi_router_rule(rule); + return FLUID_FAILED; +}; + +/* + * fluid_midi_router_send_event + * Purpose: The midi router is called for each event, that is received + * via the 'physical' midi input. Each event can trigger an arbitrary number + * of generated events. + * + * In default mode, a noteon event is just forwarded to the synth's 'noteon' function, + * a 'CC' event to the synth's 'CC' function and so on. + * + * The router can be used to + * - filter messages (for example: Pass sustain pedal CCs only to selected channels), + * - split the keyboard (noteon with notenr < x: to ch 1, >x to ch 2), + * - layer sounds (for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...) + * - velocity scaling (for each noteon event, scale the velocity by 1.27 to give DX7 users + * a chance) + * - velocity switching ("v <=100: Angel Choir; V > 100: Hell's Bells") + * - get rid of aftertouch + * - ... + */ +int +fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event) +{ + fluid_midi_router_t* router=(fluid_midi_router_t*)data; + fluid_synth_t* synth = router->synth; + fluid_midi_router_rule_t* rule=NULL; + fluid_midi_router_rule_t* next_rule=NULL; + int event_has_par2=0; /* Flag, indicates that current event needs two parameters */ + int negative_event=0; /* Flag, indicates that current event releases a key / pedal */ + int par1_max=127; /* Range limit for par1 */ + int par2_max=127; /* Range limit for par2 */ + int ret_val=FLUID_OK; + + /* Some keyboards report noteoff through a noteon event with vel=0. + * Convert those to noteoff to ease processing.*/ + if (event->type == NOTE_ON && event->param2 == 0) { + /* Channel: Remains the same + * Param 1: Note number, remains the same + * Param 2: release velocity), set to max + */ + event->type = NOTE_OFF; + event->param2=127; + }; + + /* Lock the rules table, so that for example the shell thread doesn't + * clear the rules we are just working with */ + fluid_mutex_lock(router->ruletables_mutex); + + /* Depending on the event type, choose the correct table of rules. + * Also invoke the appropriate callback function for the event type + * to notify external applications. + * Note: An event type not listed here simply won't find any rules and + * will be ignored. + */ + switch (event->type) { + case NOTE_ON: + rule=router->note_rules; + event_has_par2=1; + break; + case NOTE_OFF: + rule=router->note_rules; + event_has_par2=1; + break; + case CONTROL_CHANGE: + rule=router->cc_rules; + event_has_par2=1; + break; + case PROGRAM_CHANGE: + rule=router->progchange_rules; + break; + case PITCH_BEND: + rule=router->pitchbend_rules; + par1_max=16383; + break; + case CHANNEL_PRESSURE: + rule=router->channel_pressure_rules; + break; + case KEY_PRESSURE: + rule=router->key_pressure_rules; + event_has_par2=1; + break; + case MIDI_SYSTEM_RESET: + return router->event_handler(router->event_handler_data,event); + default: + break; + } + + /* At this point 'rule' contains the first rule in a linked list. + * Check for all rules, whether channel and parameter ranges match. + */ + + while (rule){ + int chan; /* Channel of the generated event */ + int par1; /* par1 of the generated event */ + int par2=0; + int event_par1=(int)event->param1; + int event_par2=(int)event->param2; + fluid_midi_event_t new_event; + + /* Store the pointer to the next rule right now. If the rule is later flagged for destruction, + * it may not be accessed anymore. + */ + next_rule=rule->next; + + /* Check, whether the rule is still active. Expired rules cannot be removed immediately, + * because freeing memory in a realtime thread is no good idea. And the MIDI thread, + * which calls us here, has raised priority.*/ + if (rule->state == MIDIRULE_DONE){ + goto do_next_rule; + }; + + /* Channel window */ + if (rule->chan_min > rule->chan_max){ + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if (event->channel > rule->chan_max && event->channel < rule->chan_min){ + goto do_next_rule; + } + } else { + /* Normal rule: Exclude everything < max or > min (but not min/max) */ + if (event->channel > rule->chan_max || event->channel < rule->chan_min){ + goto do_next_rule; + } + }; + + /* Par 1 window */ + if (rule->par1_min > rule->par1_max){ + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if (event_par1 > rule->par1_max && event_par1 < rule->par1_min){ + goto do_next_rule; + } + } else { + /* Normal rule: Exclude everything < max or > min (but not min/max)*/ + if (event_par1 > rule->par1_max || event_par1 < rule->par1_min){ + goto do_next_rule; + } + }; + + /* Par 2 window (only applies to event types, which have 2 pars) + * For noteoff events, velocity switching doesn't make any sense. + * Velocity scaling might be useful, though. + */ + if (event_has_par2 && event->type != NOTE_OFF){ + if (rule->par2_min > rule->par2_max){ + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if (event_par2 > rule->par2_max && event_par2 < rule->par2_min){ + goto do_next_rule; + } + } else { + /* Normal rule: Exclude everything < max or > min (but not min/max)*/ + if (event_par2 > rule->par2_max || event_par2 < rule->par2_min){ + goto do_next_rule; + }; + }; + }; + + /* Channel scaling / offset + * Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all + * input channels is mapped to the same synth channel. + */ + chan=(int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul + (fluid_real_t)rule->chan_add + 0.5); + + /* Par 1 scaling / offset */ + par1=(int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul + (fluid_real_t)rule->par1_add + 0.5); + + /* Par 2 scaling / offset, if applicable */ + if (event_has_par2){ + par2=(int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul + (fluid_real_t)rule->par2_add + 0.5); + }; + + /* Channel range limiting */ + if (chan < 0){ + chan=0; + /* Upper limit is hard to implement, because the number of MIDI channels can be changed via settings any time. */ + }; + + /* Par1 range limiting */ + if (par1 < 0){ + par1=0; + } else if (par1 > par1_max){ + par1=par1_max; + }; + + /* Par2 range limiting */ + if (event_has_par2){ + if (par2 < 0){ + par2=0; + } else if (par2 > par2_max){ + par2=par2_max; + }; + }; + + /* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2). + * We keep track on the state of noteon and sustain pedal events. If the application tries + * to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have + * arrived. In the meantime (MIDIRULE_WAITING) state, it will only let through 'negative' events + * (noteoff or pedal up). + */ + + if ( + event->type == NOTE_ON + || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 >= 64) + ){ + /* Noteon or sustain pedal down event generated */ + if (rule->keys_cc[par1] == 0){ + rule->keys_cc[par1]=1; + rule->pending_events++; + }; + } else if ( + event->type == NOTE_OFF + || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 < 64) + ){ + /* Noteoff or sustain pedal up event generated */ + if (rule->keys_cc[par1] > 0){ + rule->keys_cc[par1]=0; + rule->pending_events--; + negative_event=1; + }; + }; + + if (rule->state == MIDIRULE_WAITING){ + if (negative_event){ + if (rule->pending_events == 0){ + /* There are no more pending events in the rule - all keys are up. + * Change its state to 'unused', it will be cleared up at the next + * opportunity to run 'free' safely. + * Note: After this, the rule may disappear at any time + */ + rule->state=MIDIRULE_DONE; + }; + } else { + + /* This rule only exists because of some unfinished business: + * There is still a note or sustain pedal waiting to be + * released. But the event that came in does not release any. + * So we just ignore the event and go on with the next rule. + */ + goto do_next_rule; + }; + }; + + /* At this point it is decided, what is sent to the synth. + * Create a new event and make the appropriate call + */ + + fluid_midi_event_set_type(&new_event, event->type); + fluid_midi_event_set_channel(&new_event, chan); +/* fluid_midi_event_set_param1(&new_event, par1); */ +/* fluid_midi_event_set_param2(&new_event, par2); */ + new_event.param1 = par1; + new_event.param2 = par2; + + /* Don't know what to do with return values: What is, if + * generating one event fails? Current solution: Make all calls, + * regardless of failures, but report failure to the caller, as + * soon as only one call fails. Strategy should be thought + * through. + */ + if (router->event_handler(router->event_handler_data, &new_event) != FLUID_OK) { + ret_val=FLUID_FAILED; + } + + do_next_rule: + rule=next_rule; + }; + + /* We are finished with the rule tables. Allow the other threads to do their job. */ + fluid_mutex_unlock(router->ruletables_mutex); + + return ret_val; +} + +int fluid_midi_router_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 0) { + fluid_ostream_printf(out, "router_clear needs no arguments.\n"); + goto error_recovery; + } + + /* Disable rules and mark for destruction */ + fluid_midi_router_disable_all_rules(router); + + /* Free unused rules */ + fluid_midi_router_free_unused_rules(router); + + return 0; + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 0) { + fluid_ostream_printf(out, "router_default needs no arguments.\n"); + return -1; + } + + /* Disable rules and mark for destruction */ + fluid_midi_router_disable_all_rules(router); + + /* Create default rules */ + if (fluid_midi_router_create_default_rules(router) != FLUID_OK){ + FLUID_LOG(FLUID_ERR, "create_default_rules failed"); + goto error_recovery; + }; + + /* Free unused rules */ + fluid_midi_router_free_unused_rules(router); + + return 0; + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_begin(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + fluid_midi_router_rule_t** dest=NULL; + + if (ac != 1) { + fluid_ostream_printf(out, "router_begin needs no arguments.\n"); + goto error_recovery; + } + + if (FLUID_STRCMP(av[0],"note") == 0){ + dest=& router->note_rules; + } else if (FLUID_STRCMP(av[0],"cc") == 0){ + dest=& router->cc_rules; + } else if (FLUID_STRCMP(av[0],"prog") == 0){ + dest=& router->progchange_rules; + } else if (FLUID_STRCMP(av[0],"pbend") == 0){ + dest=& router->pitchbend_rules; + } else if (FLUID_STRCMP(av[0],"cpress") == 0){ + dest=& router->channel_pressure_rules; + } else if (FLUID_STRCMP(av[0],"kpress") == 0){ + dest=& router->key_pressure_rules; + }; + + if (dest == NULL){ + fluid_ostream_printf(out, "router_begin args: note, cc, prog, pbend, cpress, kpress\n"); + goto error_recovery; + }; + + if (fluid_midi_router_begin(router, dest) != FLUID_OK){ + goto error_recovery; + }; + + /* Free unused rules (give it a try) */ + fluid_midi_router_free_unused_rules(router); + + return 0; + + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_end(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 0) { + fluid_ostream_printf(out, "router_end needs no arguments."); + goto error_recovery; + } + + if (fluid_midi_router_end(router) != FLUID_OK){ + FLUID_LOG(FLUID_ERR, "midi_router_end failed"); + goto error_recovery; + }; + + /* Free unused rules (give it a try) */ + fluid_midi_router_free_unused_rules(router); + + return 0; + + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_chan(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 4) { + fluid_ostream_printf(out, "router_chan needs four args: min, max, mul, add."); + goto error_recovery; + } + + router->new_rule_chan_min=atoi(av[0]); + router->new_rule_chan_max=atoi(av[1]); + router->new_rule_chan_mul=atoi(av[2]); + router->new_rule_chan_add=atoi(av[3]); + + /* Free unused rules (give it a try) */ + fluid_midi_router_free_unused_rules(router); + + return 0; + + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_par1(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 4) { + fluid_ostream_printf(out, "router_par1 needs four args: min, max, mul, add."); + goto error_recovery; + } + + router->new_rule_par1_min=atoi(av[0]); + router->new_rule_par1_max=atoi(av[1]); + router->new_rule_par1_mul=atoi(av[2]); + router->new_rule_par1_add=atoi(av[3]); + + /* Free unused rules (give it a try) */ + fluid_midi_router_free_unused_rules(router); + + return 0; + + error_recovery: + return -1; +}; + +int fluid_midi_router_handle_par2(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) +{ + fluid_midi_router_t* router=synth->midi_router; + + if (ac != 4) { + fluid_ostream_printf(out, "router_par2 needs four args: min, max, mul, add."); + goto error_recovery; + } + + router->new_rule_par2_min=atoi(av[0]); + router->new_rule_par2_max=atoi(av[1]); + router->new_rule_par2_mul=atoi(av[2]); + router->new_rule_par2_add=atoi(av[3]); + + /* Free unused rules (give it a try) */ + fluid_midi_router_free_unused_rules(router); + return 0; + + error_recovery: + return -1; +}; + +void fluid_midi_router_disable_all_rules(fluid_midi_router_t* router) +{ + /* Go through all rules. If the rule has no outstanding events, then + * flag it for destruction. Otherwise change it to wait mode. Then + * it will handle the pending events, and flag itself for destruction + * as soon as all events have arrived. + */ + + fluid_midi_router_rule_t* rules[6]; + fluid_midi_router_rule_t* current_rule; + int i; + rules[0]=router->note_rules; + rules[1]=router->cc_rules; + rules[2]=router->progchange_rules; + rules[3]=router->pitchbend_rules; + rules[4]=router->channel_pressure_rules; + rules[5]=router->key_pressure_rules; + + /* Lock the rules table. We live on the assumption, that the rules + * table does not change while we are chewing at it. + * Changes between the processing of note / cc etc are permitted. + */ + for (i=0; i < 6; i++){ + fluid_mutex_lock(router->ruletables_mutex); + current_rule=rules[i]; + while (current_rule){ + if (current_rule->pending_events == 0){ + /* Flag for destruction */ + current_rule->state=MIDIRULE_DONE; + } else { + /* Wait mode */ + current_rule->state=MIDIRULE_WAITING; + }; + current_rule=current_rule->next; + }; + fluid_mutex_unlock(router->ruletables_mutex); + }; +}; + +void fluid_midi_router_free_unused_rules(fluid_midi_router_t* router) +{ + int i; + + for (i=0; i < 6; i++){ + fluid_midi_router_rule_t** p=NULL; + + /* We assume, that the table does not change while we are at it. + * Between different types (note, cc etc) we can allow changes. + */ + fluid_mutex_lock(router->ruletables_mutex); + switch(i){ + case 0: + p=&router->note_rules; + break; + case 1: + p=&router->cc_rules; + break; + case 2: + p=&router->progchange_rules; + break; + case 3: + p=&router->pitchbend_rules; + break; + case 4: + p=&router->channel_pressure_rules; + break; + case 5: + p=&router->key_pressure_rules; + break; + default: + break; + }; + + while (*p){ + fluid_midi_router_rule_t* current_rule=*p; + fluid_midi_router_rule_t* next_rule=current_rule->next; + + if (current_rule->state == MIDIRULE_DONE){ + /* p points to current_rule. + * current_rule->next points to next_rule. + * Unlink current_rule from the chain by setting the content + * of p to next_rule. + */ + + *p=next_rule; + + /* Now the rule is not in the chain anymore. Destroy it. */ + delete_fluid_midi_router_rule(current_rule); + + } else { + /* We have to keep the rule, there is still unfinished business. */ + p = ¤t_rule->next; + }; + }; + fluid_mutex_unlock(router->ruletables_mutex); + }; +}; + +/* Purpose: + * This function demonstrates, how to access incoming MIDI messages. + * It prints a message to stdout (which can be used to hook up an external user interface), + * and hands the event on to the MIDI router. + * It is not a part of the MIDI router, but an added link in the MIDI chain. + */ +int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event) +{ + switch (event->type) { + case NOTE_ON: + fprintf(stdout, "event_pre_noteon %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + case NOTE_OFF: + fprintf(stdout, "event_pre_noteoff %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + break; + case CONTROL_CHANGE: + fprintf(stdout, "event_pre_cc %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + case PROGRAM_CHANGE: + fprintf(stdout, "event_pre_prog %i %i\n", event->channel, event->param1); + fflush(stdout); + break; + case PITCH_BEND: + fprintf(stdout, "event_pre_pitch %i %i\n", event->channel, event->param1); + fflush(stdout); + break; + case CHANNEL_PRESSURE: + fprintf(stdout, "event_pre_cpress %i %i\n", event->channel, event->param1); + fflush(stdout); + break; + case KEY_PRESSURE: + fprintf(stdout, "event_pre_kpress %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + default: + break; + }; + return fluid_midi_router_handle_midi_event((fluid_midi_router_t*) data, event); +}; + +/* Purpose: + * This function demonstrates, how to access MIDI messages going from the MIDI + * router to the synth. + * Again, it prints a message to stdout and hands the event on to the synth. + * It is not a part of the MIDI router, but an added link in the MIDI chain. + */ +int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event) +{ + switch (event->type) { + case NOTE_ON: + fprintf(stdout, "event_post_noteon %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + case NOTE_OFF: + fprintf(stdout, "event_post_noteoff %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + break; + case CONTROL_CHANGE: + fprintf(stdout, "event_post_cc %i %i %i\n", + event->channel, event->param1, event->param2); + fflush(stdout); + break; + case PROGRAM_CHANGE: + fprintf(stdout, "event_post_prog %i %i\n", event->channel, event->param1); + break; + case PITCH_BEND: + fprintf(stdout, "event_post_pitch %i %i\n", event->channel, event->param1); + break; + case CHANNEL_PRESSURE: + fprintf(stdout, "event_post_cpress %i %i\n", event->channel, event->param1); + break; + case KEY_PRESSURE: + fprintf(stdout, "event_post_kpress %i %i %i\n", + event->channel, event->param1, event->param2); + break; + default: + break; + }; + return fluid_synth_handle_midi_event((fluid_synth_t*) data, event); +}; diff --git a/src/libs/fluidsynth/src/fluid_midi_router.h b/src/libs/fluidsynth/src/fluid_midi_router.h new file mode 100644 index 0000000000..48ce003b45 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_midi_router.h @@ -0,0 +1,112 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + +#ifndef _FLUID_MIDIROUTER_H +#define _FLUID_MIDIROUTER_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_sys.h" + + + +void fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router); +fluid_midi_router_rule_t* new_fluid_midi_router_rule(void); + +int fluid_midi_router_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_begin(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_chan(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_par1(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_par2(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_midi_router_handle_end(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); + +int fluid_midi_router_begin(fluid_midi_router_t* router, fluid_midi_router_rule_t** dest); +int fluid_midi_router_end(fluid_midi_router_t* router); +int fluid_midi_router_create_default_rules(fluid_midi_router_t* router); +void fluid_midi_router_disable_all_rules(fluid_midi_router_t* router); +void fluid_midi_router_free_unused_rules(fluid_midi_router_t* router); + +/* + * fluid_midi_router + */ +struct _fluid_midi_router_t { + fluid_synth_t* synth; + + fluid_midi_router_rule_t* note_rules; + fluid_midi_router_rule_t* cc_rules; + fluid_midi_router_rule_t* progchange_rules; + fluid_midi_router_rule_t* pitchbend_rules; + fluid_midi_router_rule_t* channel_pressure_rules; + fluid_midi_router_rule_t* key_pressure_rules; + + int new_rule_chan_min; + int new_rule_chan_max; + double new_rule_chan_mul; + int new_rule_chan_add; + int new_rule_par1_min; + int new_rule_par1_max; + double new_rule_par1_mul; + int new_rule_par1_add; + int new_rule_par2_min; + int new_rule_par2_max; + double new_rule_par2_mul; + int new_rule_par2_add; + + fluid_midi_router_rule_t** dest; + + handle_midi_event_func_t event_handler; /* Callback function for generated events */ + void* event_handler_data; /* One arg for the callback */ + + int nr_midi_channels; /* For clipping the midi channel */ + fluid_mutex_t ruletables_mutex; +}; + +struct _fluid_midi_router_rule_t { + int chan_min; /* Channel window, for which this rule is valid */ + int chan_max; + fluid_real_t chan_mul; /* Channel multiplier (usually 0 or 1) */ + int chan_add; /* Channel offset */ + + int par1_min; /* Parameter 1 window, for which this rule is valid */ + int par1_max; + fluid_real_t par1_mul; + int par1_add; + + int par2_min; /* Parameter 2 window, for which this rule is valid */ + int par2_max; + fluid_real_t par2_mul; + int par2_add; + + int pending_events; /* In case of noteon: How many keys are still down? */ + char keys_cc[128]; /* Flags, whether a key is down / controller is set (sustain) */ + fluid_midi_router_rule_t* next; /* next entry */ + int state; /* Does this rule expire, as soon as (for example) all keys are up? */ +}; + +enum fluid_midirule_state { + MIDIRULE_ACTIVE = 0x00, + MIDIRULE_WAITING, + MIDIRULE_DONE +}; +#endif diff --git a/src/libs/fluidsynth/src/fluid_mod.c b/src/libs/fluidsynth/src/fluid_mod.c new file mode 100644 index 0000000000..dc91e429a8 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_mod.c @@ -0,0 +1,429 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_voice.h" + +/* + * fluid_mod_clone + */ +void +fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src) +{ + mod->dest = src->dest; + mod->src1 = src->src1; + mod->flags1 = src->flags1; + mod->src2 = src->src2; + mod->flags2 = src->flags2; + mod->amount = src->amount; +} + +/* + * fluid_mod_set_source1 + */ +void +fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags) +{ + mod->src1 = src; + mod->flags1 = flags; +} + +/* + * fluid_mod_set_source2 + */ +void +fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags) +{ + mod->src2 = src; + mod->flags2 = flags; +} + +/* + * fluid_mod_set_dest + */ +void +fluid_mod_set_dest(fluid_mod_t* mod, int dest) +{ + mod->dest = dest; +} + +/* + * fluid_mod_set_amount + */ +void +fluid_mod_set_amount(fluid_mod_t* mod, double amount) +{ + mod->amount = (double) amount; +} + +int fluid_mod_get_source1(fluid_mod_t* mod) +{ + return mod->src1; +} + +int fluid_mod_get_flags1(fluid_mod_t* mod) +{ + return mod->flags1; +} + +int fluid_mod_get_source2(fluid_mod_t* mod) +{ + return mod->src2; +} + +int fluid_mod_get_flags2(fluid_mod_t* mod) +{ + return mod->flags2; +} + +int fluid_mod_get_dest(fluid_mod_t* mod) +{ + return mod->dest; +} + +double fluid_mod_get_amount(fluid_mod_t* mod) +{ + return (fluid_real_t) mod->amount; +} + + +/* + * fluid_mod_get_value + */ +fluid_real_t +fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice) +{ + fluid_real_t v1 = 0.0, v2 = 1.0; + fluid_real_t range1 = 127.0, range2 = 127.0; + + if (chan == NULL) { + return 0.0f; + } + + /* 'special treatment' for default controller + * + * Reference: SF2.01 section 8.4.2 + * + * The GM default controller 'vel-to-filter cut off' is not clearly + * defined: If implemented according to the specs, the filter + * frequency jumps between vel=63 and vel=64. To maintain + * compatibility with existing sound fonts, the implementation is + * 'hardcoded', it is impossible to implement using only one + * modulator otherwise. + * + * I assume here, that the 'intention' of the paragraph is one + * octave (1200 cents) filter frequency shift between vel=127 and + * vel=64. 'amount' is (-2400), at least as long as the controller + * is set to default. + * + * Further, the 'appearance' of the modulator (source enumerator, + * destination enumerator, flags etc) is different from that + * described in section 8.4.2, but it matches the definition used in + * several SF2.1 sound fonts (where it is used only to turn it off). + * */ + if ((mod->src2 == FLUID_MOD_VELOCITY) && + (mod->src1 == FLUID_MOD_VELOCITY) && + (mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR + | FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) && + (mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR + | FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) && + (mod->dest == GEN_FILTERFC)) { + if (voice->vel < 64){ + return (fluid_real_t) mod->amount / 2.0; + } else { + return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; + } + } + + /* get the initial value of the first source */ + if (mod->src1 > 0) { + if (mod->flags1 & FLUID_MOD_CC) { + v1 = fluid_channel_get_cc(chan, mod->src1); + } else { /* source 1 is one of the direct controllers */ + switch (mod->src1) { + case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ + v1 = range1; + break; + case FLUID_MOD_VELOCITY: + v1 = voice->vel; + break; + case FLUID_MOD_KEY: + v1 = voice->key; + break; + case FLUID_MOD_KEYPRESSURE: + v1 = chan->key_pressure; + break; + case FLUID_MOD_CHANNELPRESSURE: + v1 = chan->channel_pressure; + break; + case FLUID_MOD_PITCHWHEEL: + v1 = chan->pitch_bend; + range1 = 0x4000; + break; + case FLUID_MOD_PITCHWHEELSENS: + v1 = chan->pitch_wheel_sensitivity; + break; + default: + v1 = 0.0; + } + } + + /* transform the input value */ + switch (mod->flags1 & 0x0f) { + case 0: /* linear, unipolar, positive */ + v1 /= range1; + break; + case 1: /* linear, unipolar, negative */ + v1 = 1.0f - v1 / range1; + break; + case 2: /* linear, bipolar, positive */ + v1 = -1.0f + 2.0f * v1 / range1; + break; + case 3: /* linear, bipolar, negative */ + v1 = -1.0f + 2.0f * v1 / range1; + break; + case 4: /* concave, unipolar, positive */ + v1 = fluid_concave(v1); + break; + case 5: /* concave, unipolar, negative */ + v1 = fluid_concave(127 - v1); + break; + case 6: /* concave, bipolar, positive */ + v1 = (v1 > 64)? fluid_concave(2 * (v1 - 64)) : -fluid_concave(2 * (64 - v1)); + break; + case 7: /* concave, bipolar, negative */ + v1 = (v1 > 64)? -fluid_concave(2 * (v1 - 64)) : fluid_concave(2 * (64 - v1)); + break; + case 8: /* convex, unipolar, positive */ + v1 = fluid_convex(v1); + break; + case 9: /* convex, unipolar, negative */ + v1 = fluid_convex(127 - v1); + break; + case 10: /* convex, bipolar, positive */ + v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1)); + break; + case 11: /* convex, bipolar, negative */ + v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1)); + break; + case 12: /* switch, unipolar, positive */ + v1 = (v1 >= 64)? 1.0f : 0.0f; + break; + case 13: /* switch, unipolar, negative */ + v1 = (v1 >= 64)? 0.0f : 1.0f; + break; + case 14: /* switch, bipolar, positive */ + v1 = (v1 >= 64)? 1.0f : -1.0f; + break; + case 15: /* switch, bipolar, negative */ + v1 = (v1 >= 64)? -1.0f : 1.0f; + break; + } + } else { + return 0.0; + } + + /* no need to go further */ + if (v1 == 0.0f) { + return 0.0f; + } + + /* get the second input source */ + if (mod->src2 > 0) { + if (mod->flags2 & FLUID_MOD_CC) { + v2 = fluid_channel_get_cc(chan, mod->src2); + } else { + switch (mod->src2) { + case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ + v2 = range2; + break; + case FLUID_MOD_VELOCITY: + v2 = voice->vel; + break; + case FLUID_MOD_KEY: + v2 = voice->key; + break; + case FLUID_MOD_KEYPRESSURE: + v2 = chan->key_pressure; + break; + case FLUID_MOD_CHANNELPRESSURE: + v2 = chan->channel_pressure; + break; + case FLUID_MOD_PITCHWHEEL: + v2 = chan->pitch_bend; + break; + case FLUID_MOD_PITCHWHEELSENS: + v2 = chan->pitch_wheel_sensitivity; + break; + default: + v1 = 0.0f; + } + } + + /* transform the second input value */ + switch (mod->flags2 & 0x0f) { + case 0: /* linear, unipolar, positive */ + v2 /= range2; + break; + case 1: /* linear, unipolar, negative */ + v2 = 1.0f - v2 / range2; + break; + case 2: /* linear, bipolar, positive */ + v2 = -1.0f + 2.0f * v2 / range2; + break; + case 3: /* linear, bipolar, negative */ + v2 = -1.0f + 2.0f * v2 / range2; + break; + case 4: /* concave, unipolar, positive */ + v2 = fluid_concave(v2); + break; + case 5: /* concave, unipolar, negative */ + v2 = fluid_concave(127 - v2); + break; + case 6: /* concave, bipolar, positive */ + v2 = (v2 > 64)? fluid_concave(2 * (v2 - 64)) : -fluid_concave(2 * (64 - v2)); + break; + case 7: /* concave, bipolar, negative */ + v2 = (v2 > 64)? -fluid_concave(2 * (v2 - 64)) : fluid_concave(2 * (64 - v2)); + break; + case 8: /* convex, unipolar, positive */ + v2 = fluid_convex(v2); + break; + case 9: /* convex, unipolar, negative */ + v2 = 1.0f - fluid_convex(v2); + break; + case 10: /* convex, bipolar, positive */ + v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2)); + break; + case 11: /* convex, bipolar, negative */ + v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2)); + break; + case 12: /* switch, unipolar, positive */ + v2 = (v2 >= 64)? 1.0f : 0.0f; + break; + case 13: /* switch, unipolar, negative */ + v2 = (v2 >= 64)? 0.0f : 1.0f; + break; + case 14: /* switch, bipolar, positive */ + v2 = (v2 >= 64)? 1.0f : -1.0f; + break; + case 15: /* switch, bipolar, negative */ + v2 = (v2 >= 64)? -1.0f : 1.0f; + break; + } + } else { + v2 = 1.0f; + } + + /* it's as simple as that: */ + return (fluid_real_t) mod->amount * v1 * v2; +} + +/* + * fluid_mod_new + */ +fluid_mod_t* +fluid_mod_new() +{ + fluid_mod_t* mod = FLUID_NEW(fluid_mod_t); + if (mod == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + return mod; +}; + +/* + * fluid_mod_delete + */ +void +fluid_mod_delete(fluid_mod_t * mod) +{ + FLUID_FREE(mod); +}; + +/* + * fluid_mod_test_identity + */ +/* Purpose: + * Checks, if two modulators are identical. + * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'. + */ +int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2){ + if (mod1->dest != mod2->dest){return 0;}; + if (mod1->src1 != mod2->src1){return 0;}; + if (mod1->src2 != mod2->src2){return 0;}; + if (mod1->flags1 != mod2->flags1){return 0;} + if (mod1->flags2 != mod2->flags2){return 0;} + return 1; +}; + +/* debug function: Prints the contents of a modulator */ +void fluid_dump_modulator(fluid_mod_t * mod){ + int src1=mod->src1; + int dest=mod->dest; + int src2=mod->src2; + int flags1=mod->flags1; + int flags2=mod->flags2; + fluid_real_t amount=(fluid_real_t)mod->amount; + + printf("Src: "); + if (flags1 & FLUID_MOD_CC){ + printf("MIDI CC=%i",src1); + } else { + switch(src1){ + case FLUID_MOD_NONE: + printf("None"); break; + case FLUID_MOD_VELOCITY: + printf("note-on velocity"); break; + case FLUID_MOD_KEY: + printf("Key nr"); break; + case FLUID_MOD_KEYPRESSURE: + printf("Poly pressure"); break; + case FLUID_MOD_CHANNELPRESSURE: + printf("Chan pressure"); break; + case FLUID_MOD_PITCHWHEEL: + printf("Pitch Wheel"); break; + case FLUID_MOD_PITCHWHEELSENS: + printf("Pitch Wheel sens"); break; + default: + printf("(unknown: %i)", src1); + }; /* switch src1 */ + }; /* if not CC */ + if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");}; + if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");}; + printf("-> "); + switch(dest){ + case GEN_FILTERQ: printf("Q"); break; + case GEN_FILTERFC: printf("fc"); break; + case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break; + case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break; + case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break; + case GEN_CHORUSSEND: printf("Chorus send"); break; + case GEN_REVERBSEND: printf("Reverb send"); break; + case GEN_PAN: printf("pan"); break; + case GEN_ATTENUATION: printf("att"); break; + default: printf("dest %i",dest); + }; /* switch dest */ + printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2); +}; + + diff --git a/src/libs/fluidsynth/src/fluid_mod.h b/src/libs/fluidsynth/src/fluid_mod.h new file mode 100644 index 0000000000..b24c62a2f0 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_mod.h @@ -0,0 +1,40 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_MOD_H +#define _FLUID_MOD_H + +#include "fluidsynth_priv.h" +#include "fluid_conv.h" + +void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src); +fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice); +void fluid_dump_modulator(fluid_mod_t * mod); + +#define fluid_mod_has_source(mod,cc,ctrl) \ +( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \ + || ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \ +|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \ + || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0))))) + +#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen) + + +#endif /* _FLUID_MOD_H */ diff --git a/src/libs/fluidsynth/src/fluid_phase.h b/src/libs/fluidsynth/src/fluid_phase.h new file mode 100644 index 0000000000..411fbfc0d2 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_phase.h @@ -0,0 +1,177 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_PHASE_H +#define _FLUID_PHASE_H + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * phase + */ + +#define FLUID_INTERP_BITS 8 +#define FLUID_INTERP_BITS_MASK 0xff000000 +#define FLUID_INTERP_BITS_SHIFT 24 +#define FLUID_INTERP_MAX 256 + +#define FLUID_FRACT_MAX ((double)4294967296.0) + +/* fluid_phase_t +* Purpose: +* Playing pointer for voice playback +* +* When a sample is played back at a different pitch, the playing pointer in the +* source sample will not advance exactly one sample per output sample. +* This playing pointer is implemented using fluid_phase_t. +* It is a 64 bit number. The higher 32 bits contain the 'index' (number of +* the current sample), the lower 32 bits the fractional part. +* Access is possible in two ways: +* -through the 64 bit part 'b64', if the architecture supports 64 bit integers +* -through 'index' and 'fract' +* Note: b64 and index / fract share the same memory location! +*/ +typedef union { + struct{ + /* Note, that the two 32-bit ints form a 64-bit int! */ +#ifdef WORDS_BIGENDIAN + sint32 index; + uint32 fract; +#else + uint32 fract; + sint32 index; +#endif + } b32; +#ifdef USE_LONGLONG + long long b64; +#endif +} fluid_phase_t; + +/* Purpose: + * Set a to b. + * a: fluid_phase_t + * b: fluid_phase_t + */ +#ifdef USE_LONGLONG +#define fluid_phase_set(a,b) a=b; +#else +#define fluid_phase_set(a, b) { \ + (a).b32.fract = (b).b32.fract; \ + (a).b32.index = (b).b32.index; \ +} +#endif + +#define fluid_phase_set_int(a, b) { \ + (a).b32.index = (sint32) (b); \ + (a).b32.fract = 0; \ +} + +/* Purpose: + * Sets the phase a to a phase increment given in b. + * For example, assume b is 0.9. After setting a to it, adding a to + * the playing pointer will advance it by 0.9 samples. */ +#define fluid_phase_set_float(a, b) { \ + (a).b32.index = (sint32) (b); \ + (a).b32.fract = (uint32) (((double)(b) - (double)((a).b32.index)) * (double)FLUID_FRACT_MAX); \ +} + +/* Purpose: + * Return the index and the fractional part, respectively. */ +#define fluid_phase_index(_x) \ + ((int)(_x).b32.index) +#define fluid_phase_fract(_x) \ + ((_x).b32.fract) + +/* Purpose: + * Takes the fractional part of the argument phase and + * calculates the corresponding position in the interpolation table. + * The fractional position of the playing pointer is calculated with a quite high + * resolution (32 bits). It would be unpractical to keep a set of interpolation + * coefficients for each possible fractional part... + */ +#define fluid_phase_fract_to_tablerow(_x) \ + ((int)(((_x).b32.fract & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT)) + +#define fluid_phase_double(_x) \ + ((double)((_x).b32.index) + ((double)((_x).b32.fract) / FLUID_FRACT_MAX)) + +/* Purpose: + * Advance a by a step of b (both are fluid_phase_t). + */ +#ifdef USE_LONGLONG +#define fluid_phase_incr(a, b) (a).b64 += (b).b64; +#else +/* The idea to use (a).index += (b).index + ((a).fract < (b).fract) to + handle wrap-arounds comes from Mozilla's macros to handle 64-bit + integer on 32-bit platforms. Header prlong.h in the NSPR + library. www.mozilla.org. */ +#define fluid_phase_incr(a, b) { \ + (a).b32.fract += (b).b32.fract; \ + (a).b32.index += (b).b32.index + ((a).b32.fract < (b).b32.fract); \ +} +#endif + +/* Purpose: + * Subtract b from a (both are fluid_phase_t). + */ +#ifdef USE_LONGLONG +#define fluid_phase_decr(a, b) a-=b; +#else +#define fluid_phase_decr(a, b) { \ + (a).b32.index -= b.b32.index - ((a).b32.fract < (b).b32.fract); \ + (a).b32.fract -= b.b32.fract; \ +} +#endif + +/* Purpose: + * Subtract b samples from a. + */ +#define fluid_phase_sub_int(a, b) { (a).b32.index -= b; } + +#if 0 +#define fluid_phase_fract(_x) \ + ((fluid_real_t)((double)((_x).fract) / FLUID_FRACT_MAX)) + +#define fluid_phase_lt(a, b) \ + (((a).index < (b).index) || (((a).index == (b).index) && ((a).fract < (b).fract))) + +#define fluid_phase_gt(a, b) \ + (((a).index > (b).index) || (((a).index == (b).index) && ((a).fract > (b).fract))) + +#define fluid_phase_eq(a, b) \ + (((a).index == (b).index) && ((a).fract == (b).fract)) +#endif + +/* Purpose: + * The playing pointer is _phase. How many output samples are produced, until the point _p1 in the sample is reached, + * if _phase advances in steps of _incr? + */ +#define fluid_phase_steps(_phase,_index,_incr) \ + (int)(((double)(_index) - fluid_phase_double(_phase)) / (double)_incr) + +/* Purpose: + * Creates the expression a.index++. + * It is slightly different, when USE_LONGLONG is turned on. */ +#define fluid_phase_index_plusplus(a) (((a).b32.index)++) + +#endif /* _FLUID_PHASE_H */ diff --git a/src/libs/fluidsynth/src/fluid_ramsfont.c b/src/libs/fluidsynth/src/fluid_ramsfont.c new file mode 100644 index 0000000000..d705fb58e4 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_ramsfont.c @@ -0,0 +1,1115 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluid_ramsfont.h" +#include "fluid_sys.h" +#include "fluid_synth.h" + +/* thenumber of samples before the start and after the end */ +#define SAMPLE_LOOP_MARGIN 8 + +/* Prototypes */ +int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey); +int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample, int gen_type, float value); +int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample, int on, float loopstart, float loopend); +int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample); +void fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val); + +/* + * fluid_ramsfont_create_sfont + */ +fluid_sfont_t* +fluid_ramsfont_create_sfont() +{ + fluid_sfont_t* sfont; + fluid_ramsfont_t* ramsfont; + + ramsfont = new_fluid_ramsfont(); + if (ramsfont == NULL) { + return NULL; + } + + sfont = FLUID_NEW(fluid_sfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->data = ramsfont; + sfont->free = fluid_ramsfont_sfont_delete; + sfont->get_name = fluid_ramsfont_sfont_get_name; + sfont->get_preset = fluid_ramsfont_sfont_get_preset; + sfont->iteration_start = fluid_ramsfont_sfont_iteration_start; + sfont->iteration_next = fluid_ramsfont_sfont_iteration_next; + + return sfont; + +} + +/*************************************************************** + * + * PUBLIC INTERFACE + */ + +int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont) +{ + if (delete_fluid_ramsfont(sfont->data) != 0) + return -1; + FLUID_FREE(sfont); + return 0; +} + +char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont) +{ + return fluid_ramsfont_get_name((fluid_ramsfont_t*) sfont->data); +} + +fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) +{ + fluid_preset_t* preset; + fluid_rampreset_t* rampreset; + + rampreset = fluid_ramsfont_get_preset((fluid_ramsfont_t*) sfont->data, bank, prenum); + + if (rampreset == NULL) { + return NULL; + } + + preset = FLUID_NEW(fluid_preset_t); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + preset->sfont = sfont; + preset->data = rampreset; + preset->free = fluid_rampreset_preset_delete; + preset->get_name = fluid_rampreset_preset_get_name; + preset->get_banknum = fluid_rampreset_preset_get_banknum; + preset->get_num = fluid_rampreset_preset_get_num; + preset->noteon = fluid_rampreset_preset_noteon; + preset->notify = NULL; + + return preset; +} + +void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont) +{ + fluid_ramsfont_iteration_start((fluid_ramsfont_t*) sfont->data); +} + +int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) +{ + preset->free = fluid_rampreset_preset_delete; + preset->get_name = fluid_rampreset_preset_get_name; + preset->get_banknum = fluid_rampreset_preset_get_banknum; + preset->get_num = fluid_rampreset_preset_get_num; + preset->noteon = fluid_rampreset_preset_noteon; + preset->notify = NULL; + + return fluid_ramsfont_iteration_next((fluid_ramsfont_t*) sfont->data, preset); +} + +int fluid_rampreset_preset_delete(fluid_preset_t* preset) +{ + FLUID_FREE(preset); + printf("TODO: free modulators\n"); + return 0; +} + +char* fluid_rampreset_preset_get_name(fluid_preset_t* preset) +{ + return fluid_rampreset_get_name((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset) +{ + return fluid_rampreset_get_banknum((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_get_num(fluid_preset_t* preset) +{ + return fluid_rampreset_get_num((fluid_rampreset_t*) preset->data); +} + +int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +{ + return fluid_rampreset_noteon((fluid_rampreset_t*) preset->data, synth, chan, key, vel); +} + + + + +/*************************************************************** + * + * SFONT + */ + +/* + * new_fluid_ramsfont + */ +fluid_ramsfont_t* new_fluid_ramsfont() +{ + fluid_ramsfont_t* sfont; + + sfont = FLUID_NEW(fluid_ramsfont_t); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + sfont->name[0] = 0; + sfont->sample = NULL; + sfont->preset = NULL; + + return sfont; +} + +/* + * delete_fluid_ramsfont + */ +int delete_fluid_ramsfont(fluid_ramsfont_t* sfont) +{ + fluid_list_t *list; + fluid_rampreset_t* preset; + + /* Check that no samples are currently used */ + for (list = sfont->sample; list; list = fluid_list_next(list)) { + fluid_sample_t* sam = (fluid_sample_t*) fluid_list_get(list); + if (fluid_sample_refcount(sam) != 0) { + return -1; + } + } + + for (list = sfont->sample; list; list = fluid_list_next(list)) { + /* in ram soundfonts, the samples hold their data : so we should free it ourselves */ + fluid_sample_t* sam = (fluid_sample_t*)fluid_list_get(list); + delete_fluid_ramsample(sam); + } + + if (sfont->sample) { + delete_fluid_list(sfont->sample); + } + + preset = sfont->preset; + while (preset != NULL) { + sfont->preset = preset->next; + delete_fluid_rampreset(preset); + preset = sfont->preset; + } + + FLUID_FREE(sfont); + return FLUID_OK; +} + +/* + * fluid_ramsfont_get_name + */ +char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont) +{ + return sfont->name; +} + +/* + * fluid_ramsfont_set_name + */ +int +fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name) +{ + FLUID_MEMCPY(sfont->name, name, 20); + return FLUID_OK; +} + + +/* fluid_ramsfont_add_preset + * + * Add a preset to the SoundFont + */ +int fluid_ramsfont_add_preset(fluid_ramsfont_t* sfont, fluid_rampreset_t* preset) +{ + fluid_rampreset_t *cur, *prev; + if (sfont->preset == NULL) { + preset->next = NULL; + sfont->preset = preset; + } else { + /* sort them as we go along. very basic sorting trick. */ + cur = sfont->preset; + prev = NULL; + while (cur != NULL) { + if ((preset->bank < cur->bank) + || ((preset->bank == cur->bank) && (preset->num < cur->num))) { + if (prev == NULL) { + preset->next = cur; + sfont->preset = preset; + } else { + preset->next = cur; + prev->next = preset; + } + return FLUID_OK; + } + prev = cur; + cur = cur->next; + } + preset->next = NULL; + prev->next = preset; + } + return FLUID_OK; +} + +/* + * fluid_ramsfont_add_ramsample + */ +int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int lokey, int hikey) +{ + /*- find or create a preset + - add it the sample using the fluid_rampreset_add_sample fucntion + - add the sample to the list of samples + */ + int err; + + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + // Create it + preset = new_fluid_rampreset(sfont); + if (preset == NULL) { + return FLUID_FAILED; + } + + preset->bank = bank; + preset->num = num; + + err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); + if (err != FLUID_OK) { + return FLUID_FAILED; + } + + // sort the preset + fluid_ramsfont_add_preset(sfont, preset); + + } else { + + // just add it + err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); + if (err != FLUID_OK) { + return FLUID_FAILED; + } + } + + sfont->sample = fluid_list_append(sfont->sample, sample); + return FLUID_OK; +} + +int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample) { + int err; + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + // Fixed a crash bug : remove the sample from the sfont list after + // removing the izone (aschmitt august 2005) + err = fluid_rampreset_remove_izone(preset, sample); + if (err != FLUID_OK) + return err; + + // now we must remove the sample from sfont->sample + sfont->sample = fluid_list_remove(sfont->sample, sample); + + return FLUID_OK; +} + + +/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_gen - Antoine Schmitt May 2003 */ +int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int gen_type, float value) { + + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + return fluid_rampreset_izone_set_gen(preset, sample, gen_type, value); +} + +/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_loop - Antoine Schmitt May 2003 */ +int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, + unsigned int bank, unsigned int num, fluid_sample_t* sample, + int on, float loopstart, float loopend) { + fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); + if (preset == NULL) { + return FLUID_FAILED; + } + + return fluid_rampreset_izone_set_loop(preset, sample, on, loopstart, loopend); +} + +/* + * fluid_ramsfont_get_preset + */ +fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num) +{ + fluid_rampreset_t* preset = sfont->preset; + while (preset != NULL) { + if ((preset->bank == bank) && ((preset->num == num))) { + return preset; + } + preset = preset->next; + } + return NULL; +} + +/* + * fluid_ramsfont_iteration_start + */ +void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont) +{ + sfont->iter_cur = sfont->preset; +} + +/* + * fluid_ramsfont_iteration_next + */ +int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset) +{ + if (sfont->iter_cur == NULL) { + return 0; + } + + preset->data = (void*) sfont->iter_cur; + sfont->iter_cur = fluid_rampreset_next(sfont->iter_cur); + return 1; +} + +/*************************************************************** + * + * PRESET + */ + +typedef struct _fluid_rampreset_voice_t fluid_rampreset_voice_t; +struct _fluid_rampreset_voice_t { + fluid_voice_t *voice; + unsigned int voiceID; +}; + +/* + * new_fluid_rampreset + */ +fluid_rampreset_t* +new_fluid_rampreset(fluid_ramsfont_t* sfont) +{ + fluid_rampreset_t* preset = FLUID_NEW(fluid_rampreset_t); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + preset->next = NULL; + preset->sfont = sfont; + preset->name[0] = 0; + preset->bank = 0; + preset->num = 0; + preset->global_zone = NULL; + preset->zone = NULL; + preset->presetvoices = NULL; + return preset; +} + +/* + * delete_fluid_rampreset + */ +int +delete_fluid_rampreset(fluid_rampreset_t* preset) +{ + int err = FLUID_OK; + fluid_preset_zone_t* zone; + fluid_rampreset_voice_t *data; + + if (preset->global_zone != NULL) { + if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) { + err = FLUID_FAILED; + } + preset->global_zone = NULL; + } + zone = preset->zone; + while (zone != NULL) { + preset->zone = zone->next; + if (delete_fluid_preset_zone(zone) != FLUID_OK) { + err = FLUID_FAILED; + } + zone = preset->zone; + } + + if (preset->presetvoices != NULL) { + fluid_list_t *tmp = preset->presetvoices, *next; + while (tmp) { + data = (fluid_rampreset_voice_t *)(tmp->data); + FLUID_FREE(data); + + next = tmp->next; + FLUID_FREE(tmp); + tmp = next; + } + } + preset->presetvoices = NULL; + + FLUID_FREE(preset); + return err; +} + +int +fluid_rampreset_get_banknum(fluid_rampreset_t* preset) +{ + return preset->bank; +} + +int +fluid_rampreset_get_num(fluid_rampreset_t* preset) +{ + return preset->num; +} + +char* +fluid_rampreset_get_name(fluid_rampreset_t* preset) +{ + return preset->name; +} + +/* + * fluid_rampreset_next + */ +fluid_rampreset_t* +fluid_rampreset_next(fluid_rampreset_t* preset) +{ + return preset->next; +} + + +/* + * fluid_rampreset_add_zone + */ +int +fluid_rampreset_add_zone(fluid_rampreset_t* preset, fluid_preset_zone_t* zone) +{ + if (preset->zone == NULL) { + zone->next = NULL; + preset->zone = zone; + } else { + zone->next = preset->zone; + preset->zone = zone; + } + return FLUID_OK; +} + + +/* + * fluid_rampreset_add_sample + */ +int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey) +{ + /* create a new instrument zone, with the given sample */ + + /* one preset zone */ + if (preset->zone == NULL) { + fluid_preset_zone_t* zone; + zone = new_fluid_preset_zone(""); + if (zone == NULL) { + return FLUID_FAILED; + } + + /* its instrument */ + zone->inst = (fluid_inst_t*) new_fluid_inst(); + if (zone->inst == NULL) { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + fluid_rampreset_add_zone(preset, zone); + } + + /* add an instrument zone for each sample */ + { + fluid_inst_t* inst = fluid_preset_zone_get_inst(preset->zone); + fluid_inst_zone_t* izone = new_fluid_inst_zone(""); + if (izone == NULL) { + return FLUID_FAILED; + } + + if (fluid_inst_add_zone(inst, izone) != FLUID_OK) { + delete_fluid_inst_zone(izone); + return FLUID_FAILED; + } + + izone->sample = sample; + izone->keylo = lokey; + izone->keyhi = hikey; + + // give the preset the name of the sample + FLUID_MEMCPY(preset->name, sample->name, 20); + } + + return FLUID_OK; +} + +fluid_inst_zone_t* fluid_rampreset_izoneforsample(fluid_rampreset_t* preset, fluid_sample_t* sample) +{ + fluid_inst_t* inst; + fluid_inst_zone_t* izone; + + if (preset->zone == NULL) return NULL; + + inst = fluid_preset_zone_get_inst(preset->zone); + izone = inst->zone; + while (izone) { + if (izone->sample == sample) + return izone; + izone = izone->next; + } + return NULL; +} + +int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample, + int on, float loopstart, float loopend) { + fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); + short coarse, fine; + + if (izone == NULL) + return FLUID_FAILED; + + if (!on) { + izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; + izone->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; + fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_UNLOOPED); + return FLUID_OK; +} + + /* NOTE : We should check that (sample->startloop + loopStart <= sample->endloop - loopend - 32) */ + + /* loopstart */ + if (loopstart > 32767. || loopstart < -32767.) { + coarse = (short)(loopstart/32768.); + fine = (short)(loopstart - (float)(coarse)*32768.); + } else { + coarse = 0; + fine = (short)loopstart; + } + izone->gen[GEN_STARTLOOPADDROFS].flags = GEN_SET; + izone->gen[GEN_STARTLOOPADDROFS].val = fine; + fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDROFS, fine); + if (coarse) { + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_SET; + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].val = coarse; + } else { + izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_UNUSED; + } + fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDRCOARSEOFS, coarse); + + /* loopend */ + if (loopend > 32767. || loopend < -32767.) { + coarse = (short)(loopend/32768.); + fine = (short)(loopend - (float)(coarse)*32768.); + } else { + coarse = 0; + fine = (short)loopend; + } + izone->gen[GEN_ENDLOOPADDROFS].flags = GEN_SET; + izone->gen[GEN_ENDLOOPADDROFS].val = fine; + fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDROFS, fine); + if (coarse) { + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_SET; + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].val = coarse; + } else { + izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_UNUSED; + } + fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDRCOARSEOFS, coarse); + + izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; + izone->gen[GEN_SAMPLEMODE].val = FLUID_LOOP_DURING_RELEASE; + fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_LOOP_DURING_RELEASE); + + /* If the loop points are the whole samples, we are supposed to + copy the frames around in the margins (the start to the end margin and + the end to the start margin), but it works fine without this. Maybe some time + it will be needed (see SAMPLE_LOOP_MARGIN) -- Antoie Schmitt May 2003 */ + + return FLUID_OK; +} + +int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample, + int gen_type, float value) { + fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); + if (izone == NULL) + return FLUID_FAILED; + + izone->gen[gen_type].flags = GEN_SET; + izone->gen[gen_type].val = value; + + fluid_rampreset_updatevoices(preset, gen_type, value); + + return FLUID_OK; +} + +int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample) { + fluid_inst_t* inst; + fluid_inst_zone_t* izone, * prev; + int found = 0; + + if (preset->zone == NULL) return FLUID_FAILED; + inst = fluid_preset_zone_get_inst(preset->zone); + izone = inst->zone; + prev = NULL; + while (izone && !found) { + if (izone->sample == sample) { + if (prev == NULL) { + inst->zone = izone->next; + } else { + prev->next = izone->next; + } + izone->next = NULL; + delete_fluid_inst_zone(izone); + found = 1; + } else { + prev = izone; + izone = izone->next; + } + } + if (!found) + return FLUID_FAILED; + + // stop all the voices that use this sample, so that + // the sample can be cleared up + { + fluid_list_t *tmp = preset->presetvoices; + while (tmp) { + fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); + fluid_voice_t *voice = presetvoice->voice; + if (fluid_voice_is_playing(voice) && (fluid_voice_get_id(voice) == presetvoice->voiceID)) { + // still belongs to the preset + if (voice->sample == sample) { + // uses this sample : turn it off. + // our presetvoices struct will be cleaneup at the next update + fluid_voice_off(voice); + } + } + tmp = tmp->next; + } + } + return FLUID_OK; +} + +/* + * fluid_rampreset_remembervoice + */ +int +fluid_rampreset_remembervoice(fluid_rampreset_t* preset, fluid_voice_t* voice) { + /* stores the voice and the its ID in the preset for later update on gen_set */ + fluid_rampreset_voice_t *presetvoice = FLUID_NEW(fluid_rampreset_voice_t); + if (presetvoice == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + presetvoice->voice = voice; + presetvoice->voiceID = fluid_voice_get_id(voice); + + preset->presetvoices = fluid_list_append(preset->presetvoices, (void *)presetvoice); + if (preset->presetvoices == NULL) { + FLUID_FREE(presetvoice); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + return FLUID_OK; +} + +/* + * fluid_rampreset_updatevoice + */ +void +fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val) { + fluid_list_t *tmp = preset->presetvoices, *prev = NULL, *next; + + /* walk the presetvoice to update them if they are still active and ours. + If their ID has changed or their state is not playing, they are not ours, so we forget them + */ + while (tmp) { + fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); + fluid_voice_t *voice = presetvoice->voice; + if (!fluid_voice_is_playing(voice) || (fluid_voice_get_id(voice) != presetvoice->voiceID)) { + /* forget it */ + FLUID_FREE(presetvoice); + + /* unlink it */ + next = tmp->next; + FLUID_FREE(tmp); + if (prev) { + prev->next = next; + } else { + preset->presetvoices = next; + } + tmp = next; + + } else { + + /* update */ + fluid_voice_gen_set(voice, gen_type, val); + fluid_voice_update_param(voice, gen_type); + + /* next */ + prev = tmp; + tmp = tmp->next; + } + } +} + + +/* + * fluid_rampreset_noteon + */ +int +fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +{ + fluid_preset_zone_t *preset_zone; + fluid_inst_t* inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone, *z; + fluid_sample_t* sample; + fluid_voice_t* voice; + fluid_mod_t * mod; + fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ + int mod_list_count; + int i; + + /* run thru all the zones of this preset */ + preset_zone = preset->zone; + while (preset_zone != NULL) { + + /* check if the note falls into the key and velocity range of this + preset */ + if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { + + inst = fluid_preset_zone_get_inst(preset_zone); + global_inst_zone = fluid_inst_get_global_zone(inst); + + /* run thru all the zones of this instrument */ + inst_zone = fluid_inst_get_zone(inst); + while (inst_zone != NULL) { + + /* make sure this instrument zone has a valid sample */ + sample = fluid_inst_zone_get_sample(inst_zone); + if (fluid_sample_in_rom(sample) || (sample == NULL)) { + inst_zone = fluid_inst_zone_next(inst_zone); + continue; + } + + /* check if the note falls into the key and velocity range of this + instrument */ + + if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { + + /* this is a good zone. allocate a new synthesis process and + initialize it */ + + voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + if (voice == NULL) { + return FLUID_FAILED; + } + + if (fluid_rampreset_remembervoice(preset, voice) != FLUID_OK) { + return FLUID_FAILED; + } + + z = inst_zone; + + /* Instrument level, generators */ + + for (i = 0; i < GEN_LAST; i++) { + + /* SF 2.01 section 9.4 'bullet' 4: + * + * A generator in a local instrument zone supersedes a + * global instrument zone generator. Both cases supersede + * the default generator -> voice_gen_set */ + + if (inst_zone->gen[i].flags){ + fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); + + } else if (global_inst_zone != NULL && global_inst_zone->gen[i].flags){ + fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); + + } else { + /* The generator has not been defined in this instrument. + * Do nothing, leave it at the default. + */ + }; + + }; /* for all generators */ + + /* global instrument zone, modulators: Put them all into a + * list. */ + + mod_list_count = 0; + + if (global_inst_zone){ + mod = global_inst_zone->mod; + while (mod){ + mod_list[mod_list_count++] = mod; + mod = mod->next; + }; + }; + + /* local instrument zone, modulators. + * Replace modulators with the same definition in the list: + * SF 2.01 page 69, 'bullet' 8 + */ + mod = inst_zone->mod; + + while (mod){ + + /* 'Identical' modulators will be deleted by setting their + * list entry to NULL. The list length is known, NULL + * entries will be ignored later. SF2.01 section 9.5.1 + * page 69, 'bullet' 3 defines 'identical'. */ + + for (i = 0; i < mod_list_count; i++){ + if (fluid_mod_test_identity(mod,mod_list[i])){ + mod_list[i] = NULL; + }; + }; + + /* Finally add the new modulator to to the list. */ + mod_list[mod_list_count++] = mod; + mod = mod->next; + }; + + /* Add instrument modulators (global / local) to the voice. */ + for (i = 0; i < mod_list_count; i++){ + + mod = mod_list[i]; + + if (mod != NULL){ /* disabled modulators CANNOT be skipped. */ + + /* Instrument modulators -supersede- existing (default) + * modulators. SF 2.01 page 69, 'bullet' 6 */ + fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); + }; + }; + + /* Preset level, generators */ + + for (i = 0; i < GEN_LAST; i++) { + + /* SF 2.01 section 8.5 page 58: If some generators are + * encountered at preset level, they should be ignored */ + if ((i != GEN_STARTADDROFS) + && (i != GEN_ENDADDROFS) + && (i != GEN_STARTLOOPADDROFS) + && (i != GEN_ENDLOOPADDROFS) + && (i != GEN_STARTADDRCOARSEOFS) + && (i != GEN_ENDADDRCOARSEOFS) + && (i != GEN_STARTLOOPADDRCOARSEOFS) + && (i != GEN_KEYNUM) + && (i != GEN_VELOCITY) + && (i != GEN_ENDLOOPADDRCOARSEOFS) + && (i != GEN_SAMPLEMODE) + && (i != GEN_EXCLUSIVECLASS) + && (i != GEN_OVERRIDEROOTKEY)) { + + /* SF 2.01 section 9.4 'bullet' 9: A generator in a + * local preset zone supersedes a global preset zone + * generator. The effect is -added- to the destination + * summing node -> voice_gen_incr */ + + if (preset_zone->gen[i].flags){ + fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); + } else { + /* The generator has not been defined in this preset + * Do nothing, leave it unchanged. + */ + }; + }; /* if available at preset level */ + }; /* for all generators */ + + + /* Global preset zone, modulators: put them all into a + * list. */ + mod_list_count = 0; + + /* Process the modulators of the local preset zone. Kick + * out all identical modulators from the global preset zone + * (SF 2.01 page 69, second-last bullet) */ + + mod = preset_zone->mod; + while (mod){ + for (i = 0; i < mod_list_count; i++){ + if (fluid_mod_test_identity(mod,mod_list[i])){ + mod_list[i] = NULL; + }; + }; + + /* Finally add the new modulator to the list. */ + mod_list[mod_list_count++] = mod; + mod = mod->next; + }; + + /* Add preset modulators (global / local) to the voice. */ + for (i = 0; i < mod_list_count; i++){ + mod = mod_list[i]; + if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */ + + /* Preset modulators -add- to existing instrument / + * default modulators. SF2.01 page 70 first bullet on + * page */ + fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); + }; + }; + + /* add the synthesis process to the synthesis loop. */ + fluid_synth_start_voice(synth, voice); + + /* Store the ID of the first voice that was created by this noteon event. + * Exclusive class may only terminate older voices. + * That avoids killing voices, which have just been created. + * (a noteon event can create several voice processes with the same exclusive + * class - for example when using stereo samples) + */ + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + } + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + + + +/*************************************************************** + * + * SAMPLE + */ + + + +/* + * fluid_sample_set_name + */ +int +fluid_sample_set_name(fluid_sample_t* sample, char * name) +{ + FLUID_MEMCPY(sample->name, name, 20); + return FLUID_OK; +} + +/* + * fluid_sample_set_sound_data + */ +int +fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, unsigned int nbframes, short copy_data, int rootkey) +{ + /* 16 bit mono 44.1KHz data in */ + /* in all cases, the sample has ownership of the data : it will release it in the end */ + unsigned int storedNbFrames; + + /* in case we already have some data */ + if (sample->data != NULL) { + FLUID_FREE(sample->data); + } + + if (copy_data) { + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + if (storedNbFrames < 48) storedNbFrames = 48; + + sample->data = FLUID_MALLOC(storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); + if (sample->data == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + FLUID_MEMSET(sample->data, 0, storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); + FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN, data, nbframes*2); + +#if 0 + /* this would do the fill of the margins */ + FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN + storedNbFrames*2, (char*)data, 2*SAMPLE_LOOP_MARGIN); + FLUID_MEMCPY((char*)(sample->data), (char*)data + nbframes*2 - 2*SAMPLE_LOOP_MARGIN, 2*SAMPLE_LOOP_MARGIN); +#endif + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames; + } else { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->start = 0; + sample->end = nbframes; + } + + /* only used as markers for the LOOP generators : set them on the first real frame */ + sample->loopstart = sample->start; + sample->loopend = sample->end; + + sample->samplerate = 44100; + sample->origpitch = rootkey; + sample->pitchadj = 0; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->valid = 1; + + return FLUID_OK; +} + +/* + * new_fluid_ramsample + */ +fluid_sample_t* +new_fluid_ramsample() +{ + /* same as new_fluid_sample. Only here so that it is exported */ + fluid_sample_t* sample = NULL; + + sample = FLUID_NEW(fluid_sample_t); + if (sample == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + memset(sample, 0, sizeof(fluid_sample_t)); + + return sample; +} + +/* + * delete_fluid_ramsample + */ +int +delete_fluid_ramsample(fluid_sample_t* sample) +{ + /* same as delete_fluid_sample, plus frees the data */ + if (sample->data != NULL) { + FLUID_FREE(sample->data); + } + sample->data = NULL; + FLUID_FREE(sample); + return FLUID_OK; +} diff --git a/src/libs/fluidsynth/src/fluid_ramsfont.h b/src/libs/fluidsynth/src/fluid_ramsfont.h new file mode 100644 index 0000000000..5ffb4ae403 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_ramsfont.h @@ -0,0 +1,114 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_RAMSFONT_H +#define _FLUID_RAMSFONT_H + + +#include "fluidsynth.h" +#include "fluidsynth_priv.h" + +#include "fluid_defsfont.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + + Public interface + + */ + +int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont); +char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont); +fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont); +int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); + + +int fluid_rampreset_preset_delete(fluid_preset_t* preset); +char* fluid_rampreset_preset_get_name(fluid_preset_t* preset); +int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset); +int fluid_rampreset_preset_get_num(fluid_preset_t* preset); +int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + +/* + * fluid_ramsfont_t + */ +struct _fluid_ramsfont_t +{ + char name[21]; /* the name of the soundfont */ + fluid_list_t* sample; /* the samples in this soundfont */ + fluid_rampreset_t* preset; /* the presets of this soundfont */ + + fluid_preset_t iter_preset; /* preset interface used in the iteration */ + fluid_rampreset_t* iter_cur; /* the current preset in the iteration */ +}; + +/* interface */ +fluid_ramsfont_t* new_fluid_ramsfont(void); +int delete_fluid_ramsfont(fluid_ramsfont_t* sfont); +char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont); +fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int prenum); +void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont); +int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset); +/* specific */ + + + +/* + * fluid_preset_t + */ +struct _fluid_rampreset_t +{ + fluid_rampreset_t* next; + fluid_ramsfont_t* sfont; /* the soundfont this preset belongs to */ + char name[21]; /* the name of the preset */ + unsigned int bank; /* the bank number */ + unsigned int num; /* the preset number */ + fluid_preset_zone_t* global_zone; /* the global zone of the preset */ + fluid_preset_zone_t* zone; /* the chained list of preset zones */ + fluid_list_t *presetvoices; /* chained list of used voices */ +}; + +/* interface */ +fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont); +int delete_fluid_rampreset(fluid_rampreset_t* preset); +fluid_rampreset_t* fluid_rampreset_next(fluid_rampreset_t* preset); +char* fluid_rampreset_get_name(fluid_rampreset_t* preset); +int fluid_rampreset_get_banknum(fluid_rampreset_t* preset); +int fluid_rampreset_get_num(fluid_rampreset_t* preset); +int fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SFONT_H */ diff --git a/src/libs/fluidsynth/src/fluid_rev.c b/src/libs/fluidsynth/src/fluid_rev.c new file mode 100644 index 0000000000..a1a1c539bc --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_rev.c @@ -0,0 +1,560 @@ +/* + + Freeverb + + Written by Jezar at Dreampoint, June 2000 + http://www.dreampoint.co.uk + This code is public domain + + Translated to C by Peter Hanappe, Mai 2001 +*/ + +#include "fluid_rev.h" + +/*************************************************************** + * + * REVERB + */ + +/* Denormalising: + * + * According to music-dsp thread 'Denormalise', Pentium processors + * have a hardware 'feature', that is of interest here, related to + * numeric underflow. We have a recursive filter. The output decays + * exponentially, if the input stops. So the numbers get smaller and + * smaller... At some point, they reach 'denormal' level. This will + * lead to drastic spikes in the CPU load. The effect was reproduced + * with the reverb - sometimes the average load over 10 s doubles!!. + * + * The 'undenormalise' macro fixes the problem: As soon as the number + * is close enough to denormal level, the macro forces the number to + * 0.0f. The original macro is: + * + * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + * + * This will zero out a number when it reaches the denormal level. + * Advantage: Maximum dynamic range Disadvantage: We'll have to check + * every sample, expensive. The alternative macro comes from a later + * mail from Jon Watte. It will zap a number before it reaches + * denormal level. Jon suggests to run it once per block instead of + * every sample. + */ + +# if defined(WITH_FLOATX) +# define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample) +# else +/* 1e-20 was chosen as an arbitrary (small) threshold. */ +#define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample; +#endif + +/* Denormalising part II: + * + * Another method fixes the problem cheaper: Use a small DC-offset in + * the filter calculations. Now the signals converge not against 0, + * but against the offset. The constant offset is invisible from the + * outside world (i.e. it does not appear at the output. There is a + * very small turn-on transient response, which should not cause + * problems. + */ + + +//#define DC_OFFSET 0 +#define DC_OFFSET 1e-8 +//#define DC_OFFSET 0.001f +typedef struct _fluid_allpass fluid_allpass; +typedef struct _fluid_comb fluid_comb; + +struct _fluid_allpass { + fluid_real_t feedback; + fluid_real_t *buffer; + int bufsize; + int bufidx; +}; + +void fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size); +void fluid_allpass_init(fluid_allpass* allpass); +void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val); +fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass); + +void +fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size) +{ + allpass->bufidx = 0; + allpass->buffer = buf; + allpass->bufsize = size; +} + +void +fluid_allpass_init(fluid_allpass* allpass) +{ + int i; + int len = allpass->bufsize; + fluid_real_t* buf = allpass->buffer; + for (i = 0; i < len; i++) { + buf[i] = DC_OFFSET; /* this is not 100 % correct. */ + } +} + +void +fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val) +{ + allpass->feedback = val; +} + +fluid_real_t +fluid_allpass_getfeedback(fluid_allpass* allpass) +{ + return allpass->feedback; +} + +#define fluid_allpass_process(_allpass, _input) \ +{ \ + fluid_real_t output; \ + fluid_real_t bufout; \ + bufout = _allpass.buffer[_allpass.bufidx]; \ + output = bufout-_input; \ + _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \ + if (++_allpass.bufidx >= _allpass.bufsize) { \ + _allpass.bufidx = 0; \ + } \ + _input = output; \ +} + +/* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */ +/* { */ +/* fluid_real_t output; */ +/* fluid_real_t bufout; */ +/* bufout = allpass->buffer[allpass->bufidx]; */ +/* undenormalise(bufout); */ +/* output = -input + bufout; */ +/* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */ +/* if (++allpass->bufidx >= allpass->bufsize) { */ +/* allpass->bufidx = 0; */ +/* } */ +/* return output; */ +/* } */ + +struct _fluid_comb { + fluid_real_t feedback; + fluid_real_t filterstore; + fluid_real_t damp1; + fluid_real_t damp2; + fluid_real_t *buffer; + int bufsize; + int bufidx; +}; + +void fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size); +void fluid_comb_init(fluid_comb* comb); +void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val); +fluid_real_t fluid_comb_getdamp(fluid_comb* comb); +void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val); +fluid_real_t fluid_comb_getfeedback(fluid_comb* comb); + +void +fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size) +{ + comb->filterstore = 0; + comb->bufidx = 0; + comb->buffer = buf; + comb->bufsize = size; +} + +void +fluid_comb_init(fluid_comb* comb) +{ + int i; + fluid_real_t* buf = comb->buffer; + int len = comb->bufsize; + for (i = 0; i < len; i++) { + buf[i] = DC_OFFSET; /* This is not 100 % correct. */ + } +} + +void +fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val) +{ + comb->damp1 = val; + comb->damp2 = 1 - val; +} + +fluid_real_t +fluid_comb_getdamp(fluid_comb* comb) +{ + return comb->damp1; +} + +void +fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val) +{ + comb->feedback = val; +} + +fluid_real_t +fluid_comb_getfeedback(fluid_comb* comb) +{ + return comb->feedback; +} + +#define fluid_comb_process(_comb, _input, _output) \ +{ \ + fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \ + _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \ + _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \ + if (++_comb.bufidx >= _comb.bufsize) { \ + _comb.bufidx = 0; \ + } \ + _output += _tmp; \ +} + +/* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */ +/* { */ +/* fluid_real_t output; */ + +/* output = comb->buffer[comb->bufidx]; */ +/* undenormalise(output); */ +/* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */ +/* undenormalise(comb->filterstore); */ +/* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */ +/* if (++comb->bufidx >= comb->bufsize) { */ +/* comb->bufidx = 0; */ +/* } */ + +/* return output; */ +/* } */ + +#define numcombs 8 +#define numallpasses 4 +#define fixedgain 0.015f +#define scalewet 3.0f +#define scaledamp 0.4f +#define scaleroom 0.28f +#define offsetroom 0.7f +#define initialroom 0.5f +#define initialdamp 0.5f +#define initialwet 1 +#define initialdry 0 +#define initialwidth 1 +#define stereospread 23 + +/* + These values assume 44.1KHz sample rate + they will probably be OK for 48KHz sample rate + but would need scaling for 96KHz (or other) sample rates. + The values were obtained by listening tests. +*/ +#define combtuningL1 1116 +#define combtuningR1 1116 + stereospread +#define combtuningL2 1188 +#define combtuningR2 1188 + stereospread +#define combtuningL3 1277 +#define combtuningR3 1277 + stereospread +#define combtuningL4 1356 +#define combtuningR4 1356 + stereospread +#define combtuningL5 1422 +#define combtuningR5 1422 + stereospread +#define combtuningL6 1491 +#define combtuningR6 1491 + stereospread +#define combtuningL7 1557 +#define combtuningR7 1557 + stereospread +#define combtuningL8 1617 +#define combtuningR8 1617 + stereospread +#define allpasstuningL1 556 +#define allpasstuningR1 556 + stereospread +#define allpasstuningL2 441 +#define allpasstuningR2 441 + stereospread +#define allpasstuningL3 341 +#define allpasstuningR3 341 + stereospread +#define allpasstuningL4 225 +#define allpasstuningR4 225 + stereospread + +struct _fluid_revmodel_t { + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t wet, wet1, wet2; + fluid_real_t width; + fluid_real_t gain; + /* + The following are all declared inline + to remove the need for dynamic allocation + with its subsequent error-checking messiness + */ + /* Comb filters */ + fluid_comb combL[numcombs]; + fluid_comb combR[numcombs]; + /* Allpass filters */ + fluid_allpass allpassL[numallpasses]; + fluid_allpass allpassR[numallpasses]; + /* Buffers for the combs */ + fluid_real_t bufcombL1[combtuningL1]; + fluid_real_t bufcombR1[combtuningR1]; + fluid_real_t bufcombL2[combtuningL2]; + fluid_real_t bufcombR2[combtuningR2]; + fluid_real_t bufcombL3[combtuningL3]; + fluid_real_t bufcombR3[combtuningR3]; + fluid_real_t bufcombL4[combtuningL4]; + fluid_real_t bufcombR4[combtuningR4]; + fluid_real_t bufcombL5[combtuningL5]; + fluid_real_t bufcombR5[combtuningR5]; + fluid_real_t bufcombL6[combtuningL6]; + fluid_real_t bufcombR6[combtuningR6]; + fluid_real_t bufcombL7[combtuningL7]; + fluid_real_t bufcombR7[combtuningR7]; + fluid_real_t bufcombL8[combtuningL8]; + fluid_real_t bufcombR8[combtuningR8]; + /* Buffers for the allpasses */ + fluid_real_t bufallpassL1[allpasstuningL1]; + fluid_real_t bufallpassR1[allpasstuningR1]; + fluid_real_t bufallpassL2[allpasstuningL2]; + fluid_real_t bufallpassR2[allpasstuningR2]; + fluid_real_t bufallpassL3[allpasstuningL3]; + fluid_real_t bufallpassR3[allpasstuningR3]; + fluid_real_t bufallpassL4[allpasstuningL4]; + fluid_real_t bufallpassR4[allpasstuningR4]; +}; + +void fluid_revmodel_update(fluid_revmodel_t* rev); +void fluid_revmodel_init(fluid_revmodel_t* rev); + +fluid_revmodel_t* +new_fluid_revmodel() +{ + fluid_revmodel_t* rev; + rev = FLUID_NEW(fluid_revmodel_t); + if (rev == NULL) { + return NULL; + } + + /* Tie the components to their buffers */ + fluid_comb_setbuffer(&rev->combL[0], rev->bufcombL1, combtuningL1); + fluid_comb_setbuffer(&rev->combR[0], rev->bufcombR1, combtuningR1); + fluid_comb_setbuffer(&rev->combL[1], rev->bufcombL2, combtuningL2); + fluid_comb_setbuffer(&rev->combR[1], rev->bufcombR2, combtuningR2); + fluid_comb_setbuffer(&rev->combL[2], rev->bufcombL3, combtuningL3); + fluid_comb_setbuffer(&rev->combR[2], rev->bufcombR3, combtuningR3); + fluid_comb_setbuffer(&rev->combL[3], rev->bufcombL4, combtuningL4); + fluid_comb_setbuffer(&rev->combR[3], rev->bufcombR4, combtuningR4); + fluid_comb_setbuffer(&rev->combL[4], rev->bufcombL5, combtuningL5); + fluid_comb_setbuffer(&rev->combR[4], rev->bufcombR5, combtuningR5); + fluid_comb_setbuffer(&rev->combL[5], rev->bufcombL6, combtuningL6); + fluid_comb_setbuffer(&rev->combR[5], rev->bufcombR6, combtuningR6); + fluid_comb_setbuffer(&rev->combL[6], rev->bufcombL7, combtuningL7); + fluid_comb_setbuffer(&rev->combR[6], rev->bufcombR7, combtuningR7); + fluid_comb_setbuffer(&rev->combL[7], rev->bufcombL8, combtuningL8); + fluid_comb_setbuffer(&rev->combR[7], rev->bufcombR8, combtuningR8); + fluid_allpass_setbuffer(&rev->allpassL[0], rev->bufallpassL1, allpasstuningL1); + fluid_allpass_setbuffer(&rev->allpassR[0], rev->bufallpassR1, allpasstuningR1); + fluid_allpass_setbuffer(&rev->allpassL[1], rev->bufallpassL2, allpasstuningL2); + fluid_allpass_setbuffer(&rev->allpassR[1], rev->bufallpassR2, allpasstuningR2); + fluid_allpass_setbuffer(&rev->allpassL[2], rev->bufallpassL3, allpasstuningL3); + fluid_allpass_setbuffer(&rev->allpassR[2], rev->bufallpassR3, allpasstuningR3); + fluid_allpass_setbuffer(&rev->allpassL[3], rev->bufallpassL4, allpasstuningL4); + fluid_allpass_setbuffer(&rev->allpassR[3], rev->bufallpassR4, allpasstuningR4); + /* Set default values */ + fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f); + fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f); + fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f); + fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f); + fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f); + fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f); + fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f); + fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f); + + /* set values manually, since calling set functions causes update + and all values should be initialized for an update */ + rev->roomsize = initialroom * scaleroom + offsetroom; + rev->damp = initialdamp * scaledamp; + rev->wet = initialwet * scalewet; + rev->width = initialwidth; + rev->gain = fixedgain; + + /* now its okay to update reverb */ + fluid_revmodel_update(rev); + + /* Clear all buffers */ + fluid_revmodel_init(rev); + return rev; +} + +void +delete_fluid_revmodel(fluid_revmodel_t* rev) +{ + FLUID_FREE(rev); +} + +void +fluid_revmodel_init(fluid_revmodel_t* rev) +{ + int i; + for (i = 0; i < numcombs;i++) { + fluid_comb_init(&rev->combL[i]); + fluid_comb_init(&rev->combR[i]); + } + for (i = 0; i < numallpasses; i++) { + fluid_allpass_init(&rev->allpassL[i]); + fluid_allpass_init(&rev->allpassR[i]); + } +} + +void +fluid_revmodel_reset(fluid_revmodel_t* rev) +{ + fluid_revmodel_init(rev); +} + +void +fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k = 0; + fluid_real_t outL, outR, input; + + for (k = 0; k < FLUID_BUFSIZE; k++) { + + outL = outR = 0; + + /* The original Freeverb code expects a stereo signal and 'input' + * is set to the sum of the left and right input sample. Since + * this code works on a mono signal, 'input' is set to twice the + * input sample. */ + input = (2 * in[k] + DC_OFFSET) * rev->gain; + + /* Accumulate comb filters in parallel */ + for (i = 0; i < numcombs; i++) { + fluid_comb_process(rev->combL[i], input, outL); + fluid_comb_process(rev->combR[i], input, outR); + } + /* Feed through allpasses in series */ + for (i = 0; i < numallpasses; i++) { + fluid_allpass_process(rev->allpassL[i], outL); + fluid_allpass_process(rev->allpassR[i], outR); + } + + /* Remove the DC offset */ + outL -= DC_OFFSET; + outR -= DC_OFFSET; + + /* Calculate output REPLACING anything already there */ + left_out[k] = outL * rev->wet1 + outR * rev->wet2; + right_out[k] = outR * rev->wet1 + outL * rev->wet2; + } +} + +void +fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k = 0; + fluid_real_t outL, outR, input; + + for (k = 0; k < FLUID_BUFSIZE; k++) { + + outL = outR = 0; + + /* The original Freeverb code expects a stereo signal and 'input' + * is set to the sum of the left and right input sample. Since + * this code works on a mono signal, 'input' is set to twice the + * input sample. */ + input = (2 * in[k] + DC_OFFSET) * rev->gain; + + /* Accumulate comb filters in parallel */ + for (i = 0; i < numcombs; i++) { + fluid_comb_process(rev->combL[i], input, outL); + fluid_comb_process(rev->combR[i], input, outR); + } + /* Feed through allpasses in series */ + for (i = 0; i < numallpasses; i++) { + fluid_allpass_process(rev->allpassL[i], outL); + fluid_allpass_process(rev->allpassR[i], outR); + } + + /* Remove the DC offset */ + outL -= DC_OFFSET; + outR -= DC_OFFSET; + + /* Calculate output MIXING with anything already there */ + left_out[k] += outL * rev->wet1 + outR * rev->wet2; + right_out[k] += outR * rev->wet1 + outL * rev->wet2; + } +} + +void +fluid_revmodel_update(fluid_revmodel_t* rev) +{ + /* Recalculate internal values after parameter change */ + int i; + + rev->wet1 = rev->wet * (rev->width / 2 + 0.5f); + rev->wet2 = rev->wet * ((1 - rev->width) / 2); + + for (i = 0; i < numcombs; i++) { + fluid_comb_setfeedback(&rev->combL[i], rev->roomsize); + fluid_comb_setfeedback(&rev->combR[i], rev->roomsize); + } + for (i = 0; i < numcombs; i++) { + fluid_comb_setdamp(&rev->combL[i], rev->damp); + fluid_comb_setdamp(&rev->combR[i], rev->damp); + } +} + +/* + The following get/set functions are not inlined, because + speed is never an issue when calling them, and also + because as you develop the reverb model, you may + wish to take dynamic action when they are called. +*/ +void +fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value) +{ +/* fluid_clip(value, 0.0f, 1.0f); */ + rev->roomsize = (value * scaleroom) + offsetroom; + fluid_revmodel_update(rev); +} + +fluid_real_t +fluid_revmodel_getroomsize(fluid_revmodel_t* rev) +{ + return (rev->roomsize - offsetroom) / scaleroom; +} + +void +fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value) +{ +/* fluid_clip(value, 0.0f, 1.0f); */ + rev->damp = value * scaledamp; + fluid_revmodel_update(rev); +} + +fluid_real_t +fluid_revmodel_getdamp(fluid_revmodel_t* rev) +{ + return rev->damp / scaledamp; +} + +void +fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value) +{ +/* fluid_clip(value, 0.0f, 1.0f); */ +/* rev->wet = value * scalewet; */ +/* fluid_revmodel_update(rev); */ +} + +fluid_real_t +fluid_revmodel_getlevel(fluid_revmodel_t* rev) +{ + return rev->wet / scalewet; +} + +void +fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value) +{ +/* fluid_clip(value, 0.0f, 1.0f); */ + rev->width = value; + fluid_revmodel_update(rev); +} + +fluid_real_t +fluid_revmodel_getwidth(fluid_revmodel_t* rev) +{ + return rev->width; +} diff --git a/src/libs/fluidsynth/src/fluid_rev.h b/src/libs/fluidsynth/src/fluid_rev.h new file mode 100644 index 0000000000..ddfbf728a2 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_rev.h @@ -0,0 +1,67 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_REV_H +#define _FLUID_REV_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_revmodel_t fluid_revmodel_t; + + +/* + * reverb + */ +fluid_revmodel_t* new_fluid_revmodel(void); +void delete_fluid_revmodel(fluid_revmodel_t* rev); + +void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_reset(fluid_revmodel_t* rev); + +void fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value); +void fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value); +void fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value); +void fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value); +void fluid_revmodel_setmode(fluid_revmodel_t* rev, fluid_real_t value); + +fluid_real_t fluid_revmodel_getroomsize(fluid_revmodel_t* rev); +fluid_real_t fluid_revmodel_getdamp(fluid_revmodel_t* rev); +fluid_real_t fluid_revmodel_getlevel(fluid_revmodel_t* rev); +fluid_real_t fluid_revmodel_getwidth(fluid_revmodel_t* rev); + +/* + * reverb preset + */ +typedef struct _fluid_revmodel_presets_t { + char* name; + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t width; + fluid_real_t level; +} fluid_revmodel_presets_t; + + +#endif /* _FLUID_REV_H */ diff --git a/src/libs/fluidsynth/src/fluid_seq.c b/src/libs/fluidsynth/src/fluid_seq.c new file mode 100644 index 0000000000..7922b29d79 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_seq.c @@ -0,0 +1,1037 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ +*/ + +#include "fluid_event_priv.h" +#include "fluidsynth_priv.h" // FLUID_NEW, etc +#include "fluid_sys.h" // timer, threads, etc... +#include "fluid_list.h" + +/*************************************************************** + * + * SEQUENCER + */ + +#define FLUID_SEQUENCER_EVENTS_MAX 1000 + +/* Private data for SEQUENCER */ +struct _fluid_sequencer_t { + unsigned int startMs; + double scale; // ticks per second + fluid_list_t* clients; + short clientsID; + /* for queue + heap */ + fluid_evt_entry* preQueue; + fluid_evt_entry* preQueueLast; + fluid_timer_t* timer; + int queue0StartTime; + short prevCellNb; + fluid_evt_entry* queue0[256][2]; + fluid_evt_entry* queue1[255][2]; + fluid_evt_entry* queueLater; + fluid_evt_heap_t* heap; + fluid_mutex_t mutex; +#if FLUID_SEQ_WITH_TRACE + char *tracebuf; + char *traceptr; + int tracelen; +#endif +}; + +/* Private data for clients */ +typedef struct _fluid_sequencer_client_t { + short id; + char* name; + fluid_event_callback_t callback; + void* data; +} fluid_sequencer_client_t; + +/* prototypes */ +/* sorting API */ +short _fluid_seq_queue_init(fluid_sequencer_t* seq, int nbEvents); +void _fluid_seq_queue_end(fluid_sequencer_t* seq); +short _fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt); +void _fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, short src, short dest, int type); +int _fluid_seq_queue_process(void* data, unsigned int msec); // callback from timer + + +/* API implementation */ + +fluid_sequencer_t* +new_fluid_sequencer() +{ + fluid_sequencer_t* seq; + + seq = FLUID_NEW(fluid_sequencer_t); + if (seq == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + + FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t)); + + seq->scale = 1000; // default value + seq->startMs = fluid_curtime(); + seq->clients = NULL; + seq->clientsID = 0; + + if (-1 == _fluid_seq_queue_init(seq, FLUID_SEQUENCER_EVENTS_MAX)) { + FLUID_FREE(seq); + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + +#if FLUID_SEQ_WITH_TRACE + seq->tracelen = 1024*100; + seq->tracebuf = (char *)FLUID_MALLOC(seq->tracelen); + if (seq->tracebuf == NULL) { + _fluid_seq_queue_end(seq); + FLUID_FREE(seq); + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + seq->traceptr = seq->tracebuf; +#endif + + return(seq); +} + +void +delete_fluid_sequencer(fluid_sequencer_t* seq) +{ + + if (seq == NULL) { + return; + } + + _fluid_seq_queue_end(seq); + + /* cleanup clients */ + if (seq->clients) { + fluid_list_t *tmp = seq->clients; + while (tmp != NULL) { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; + if (client->name) FLUID_FREE(client->name); + tmp = tmp->next; + } + delete_fluid_list(seq->clients); + seq->clients = NULL; + } + +#if FLUID_SEQ_WITH_TRACE + if (seq->tracebuf != NULL) + FLUID_FREE(seq->tracebuf); + seq->tracebuf = NULL; +#endif + + FLUID_FREE(seq); +} + +#if FLUID_SEQ_WITH_TRACE + +/* trace */ +void +fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...) +{ + va_list args; + int len, remain = seq->tracelen - (seq->traceptr - seq->tracebuf); + if (remain <= 0) return; + + va_start (args, fmt); + len = vsnprintf(seq->traceptr, remain, fmt, args); + va_end (args); + + if (len > 0) { + if (len <= remain) { + // all written, with 0 at end + seq->traceptr += len; + } else { + // not enough room, set to end + seq->traceptr = seq->tracebuf + seq->tracelen; + } + } + + return; +} + +void +fluid_seq_cleartrace(fluid_sequencer_t* seq) +{ + seq->traceptr = seq->tracebuf; +} + +char * +fluid_seq_gettrace(fluid_sequencer_t* seq) +{ + return seq->tracebuf; +} +#else + +void fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...) {} + +#endif // FLUID_SEQ_WITH_TRACE + +/* clients */ + +short fluid_sequencer_register_client(fluid_sequencer_t* seq, char* name, + fluid_event_callback_t callback, void* data) { + + fluid_sequencer_client_t * client; + char * nameCopy; + + client = FLUID_NEW(fluid_sequencer_client_t); + if (client == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return -1; + } + + nameCopy = FLUID_STRDUP(name); + if (nameCopy == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return -1; + } + + seq->clientsID++; + + client->name = nameCopy; + client->id = seq->clientsID; + client->callback = callback; + client->data = data; + + seq->clients = fluid_list_append(seq->clients, (void *)client); + + return (client->id); +} + +/** Unregister a previously registered client. */ +void fluid_sequencer_unregister_client(fluid_sequencer_t* seq, short id) +{ + fluid_list_t *tmp; + + if (seq->clients == NULL) return; + + tmp = seq->clients; + while (tmp) { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; + + if (client->id == id) { + if (client->name) + FLUID_FREE(client->name); + seq->clients = fluid_list_remove_link(seq->clients, tmp); + delete1_fluid_list(tmp); + return; + } + tmp = tmp->next; + } + return; +} + +int fluid_sequencer_count_clients(fluid_sequencer_t* seq) +{ + if (seq->clients == NULL) + return 0; + return fluid_list_size(seq->clients); +} + +/** Returns the id of a registered client. */ +short fluid_sequencer_get_client_id(fluid_sequencer_t* seq, int index) +{ + fluid_list_t *tmp = fluid_list_nth(seq->clients, index); + if (tmp == NULL) { + return -1; + } else { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; + return client->id; + } +} + +/** Returns the name of a registered client. */ +char* fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int id) +{ + fluid_list_t *tmp; + + if (seq->clients == NULL) + return NULL; + + tmp = seq->clients; + while (tmp) { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; + + if (client->id == id) + return client->name; + + tmp = tmp->next; + } + return NULL; +} + +int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id) +{ + fluid_list_t *tmp; + + if (seq->clients == NULL) return 0; + + tmp = seq->clients; + while (tmp) { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; + + if (client->id == id) + return (client->callback != NULL); + + tmp = tmp->next; + } + return 0; +} + +/* sending events */ +void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt) +{ + short destID = fluid_event_get_dest(evt); + + /* find callback */ + fluid_list_t *tmp = seq->clients; + while (tmp) { + fluid_sequencer_client_t *dest = (fluid_sequencer_client_t*)tmp->data; + + if (dest->id == destID) { + if (dest->callback) + (dest->callback)(fluid_sequencer_get_tick(seq), + evt, seq, dest->data); + return; + } + tmp = tmp->next; + } +} + +int +fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int time, int absolute) +{ + unsigned int now = fluid_sequencer_get_tick(seq); + + /* set absolute */ + if (!absolute) + time = now + time; + + /* time stamp event */ + fluid_event_set_time(evt, time); + + /* process late */ + if (time < now) { + fluid_sequencer_send_now(seq, evt); + return 0; + } + + /* process now */ + if (time == now) { + fluid_sequencer_send_now(seq, evt); + return 0; + } + + /* queue for processing later */ + return _fluid_seq_queue_pre_insert(seq, evt); +} + +void +fluid_sequencer_remove_events(fluid_sequencer_t* seq, short source, short dest, int type) +{ + _fluid_seq_queue_pre_remove(seq, source, dest, type); +} + + +/************************************* + time +**************************************/ +unsigned int fluid_sequencer_get_tick(fluid_sequencer_t* seq) +{ + unsigned int absMs = fluid_curtime(); + double nowFloat; + unsigned int now; + nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f; + now = nowFloat; + return now; +} + +void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale) +{ + if (scale <= 0) { + fluid_log(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); + return; + } + + if (scale > 1000.0) + // Otherwise : problems with the timer = 0ms... + scale = 1000.0; + + if (seq->scale != scale) { + double oldScale = seq->scale; + + // stop timer + if (seq->timer) { + delete_fluid_timer(seq->timer); + seq->timer = NULL; + } + + seq->scale = scale; + + // change start0 so that cellNb is preserved + seq->queue0StartTime = (seq->queue0StartTime + seq->prevCellNb)*(seq->scale/oldScale) - seq->prevCellNb; + + // change all preQueue events for new scale + { + fluid_evt_entry* tmp; + tmp = seq->preQueue; + while (tmp) { + if (tmp->entryType == FLUID_EVT_ENTRY_INSERT) + tmp->evt.time = tmp->evt.time*seq->scale/oldScale; + + tmp = tmp->next; + } + } + + /* re-start timer */ + seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, 1, 0); + } +} + +/** Set the conversion from tick to absolute time (ticks per + second). */ +double fluid_sequencer_get_time_scale(fluid_sequencer_t* seq) +{ + return seq->scale; +} + + +/********************** + + the queue + +**********************/ + +/* + The queue stores all future events to be processed. + + Data structures + + There is a heap, allocated at init time, for managing a pool + of event entries, that is description of an event, its time, + and whether it is a normal event or a removal command. + + The queue is separated in two arrays, and a list. The first + array 'queue0' corresponds to the events to be sent in the + next 256 ticks (0 to 255), the second array 'queue1' contains + the events to be send from now+256 to now+65535. The list + called 'queueLater' contains the events to be sent later than + that. In each array, one cell contains a list of events having + the same time (in the queue0 array), or the same time/256 (in + the queue1 array), and a pointer to the last event in the list + of the cell so as to be able to insert fast at the end of the + list (i.e. a cell = 2 pointers). The 'queueLater' list is + ordered by time and by post time. This way, inserting 'soon' + events is fast (below 65535 ticks, that is about 1 minute if 1 + tick=1ms). Inserting later events is more slow, but this is a + realtime engine, isn't it ? + + The queue0 starts at queue0StartTime. When 256 ticks have + elapsed, the queue0 array is emptied, and the first cell of + the queue1 array is expanded in the queue0 array, according to + the time of each event. The queue1 array is shifted to the + left, and the first events of the queueLater list are inserte + in the last cell of the queue1 array. + + We remember the previously managed cell in queue0 in the + prevCellNb variable. When processing the current cell, we + process the events in between (late events). + + Functions + + The main thread functions first get an event entry from the + heap, and copy the given event into it, then merely enqueue it + in a preQueue. This is in order to protect the data structure: + everything is managed in the callback (thread or interrupt, + depending on the architecture). + + All queue data structure management is done in a timer + callback: '_fluid_seq_queue_process'. The + _fluid_seq_queue_process function first process the preQueue, + inserting or removing event entrys from the queue, then + processes the queue, by sending events ready to be sent at the + current time. + + Critical sections between the main thread (or app) and the + sequencer thread (or interrupt) are: + + - the heap management (if two threads get a free event at the + same time) + - the preQueue access. + + These are really small and fast sections (merely a pointer or + two changing value). They are not protected by a mutex for now + (August 2002). Waiting for crossplatform mutex solutions. When + changing this code, beware that the + _fluid_seq_queue_pre_insert function may be called by the + callback of the queue thread (ex : a note event inserts a + noteoff event). + +*/ + + + +void _fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry); +void _fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* temp); +void _fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq); + + +/********************/ +/* API */ +/********************/ + +short +_fluid_seq_queue_init(fluid_sequencer_t* seq, int maxEvents) +{ + int i; + + seq->heap = _fluid_evt_heap_init(maxEvents); + if (seq->heap == NULL) { + fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + return -1; + } + + seq->preQueue = NULL; + seq->preQueueLast = NULL; + + FLUID_MEMSET(seq->queue0, 0, 2*256*sizeof(fluid_evt_entry *)); + FLUID_MEMSET(seq->queue1, 0, 2*255*sizeof(fluid_evt_entry *)); + + seq->queueLater = NULL; + seq->queue0StartTime = fluid_sequencer_get_tick(seq); + seq->prevCellNb = -1; + + fluid_mutex_init(seq->mutex); + + /* start timer */ + seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, + (void *)seq, 1, 0); + return (0); +} + +void +_fluid_seq_queue_end(fluid_sequencer_t* seq) +{ + if (seq->timer) { + delete_fluid_timer(seq->timer); + seq->timer = NULL; + } + + if (seq->heap) { + _fluid_evt_heap_free(seq->heap); + seq->heap = NULL; + } + fluid_mutex_destroy(seq->mutex); +} + + + +/********************/ +/* queue management */ +/********************/ + +/* create event_entry and append to the preQueue */ +/* may be called from the main thread (usually) but also recursively + from the queue thread, when a callback itself does an insert... */ +short +_fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt) +{ + fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap); + if (evtentry == NULL) { + /* should not happen */ + fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); + return -1; + } + + evtentry->next = NULL; + evtentry->entryType = FLUID_EVT_ENTRY_INSERT; + FLUID_MEMCPY(&(evtentry->evt), evt, sizeof(fluid_event_t)); + + fluid_mutex_lock(seq->mutex); + + /* append to preQueue */ + if (seq->preQueueLast) { + seq->preQueueLast->next = evtentry; + } else { + seq->preQueue = evtentry; + } + seq->preQueueLast = evtentry; + + fluid_mutex_unlock(seq->mutex); + + return (0); +} + +/* create event_entry and append to the preQueue */ +/* may be called from the main thread (usually) but also recursively + from the queue thread, when a callback itself does an insert... */ +void +_fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, short src, short dest, int type) +{ + fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap); + if (evtentry == NULL) { + /* should not happen */ + fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); + return; + } + + evtentry->next = NULL; + evtentry->entryType = FLUID_EVT_ENTRY_REMOVE; + { + fluid_event_t* evt = &(evtentry->evt); + fluid_event_set_source(evt, src); + fluid_event_set_source(evt, src); + fluid_event_set_dest(evt, dest); + evt->type = type; + } + + fluid_mutex_lock(seq->mutex); + + /* append to preQueue */ + if (seq->preQueueLast) { + seq->preQueueLast->next = evtentry; + } else { + seq->preQueue = evtentry; + } + seq->preQueueLast = evtentry; + + fluid_mutex_unlock(seq->mutex); + return; +} + +/*********************** + * callback from timer + * (may be in a different thread, or in an interrupt) + * + ***********************/ +int +_fluid_seq_queue_process(void* data, unsigned int msec) +{ + fluid_sequencer_t* seq = (fluid_sequencer_t *)data; + + /* process prequeue */ + fluid_evt_entry* tmp; + fluid_evt_entry* next; + + fluid_mutex_lock(seq->mutex); + + /* get the preQueue */ + tmp = seq->preQueue; + seq->preQueue = NULL; + seq->preQueueLast = NULL; + + fluid_mutex_unlock(seq->mutex); + + /* walk all the preQueue and process them in order : inserts and removes */ + while (tmp) { + next = tmp->next; + + if (tmp->entryType == FLUID_EVT_ENTRY_REMOVE) { + _fluid_seq_queue_remove_entries_matching(seq, tmp); + } else { + _fluid_seq_queue_insert_entry(seq, tmp); + } + + tmp = next; + } + + /* send queued events */ + _fluid_seq_queue_send_queued_events(seq); + + /* continue timer */ + return 1; +} + + +void +_fluid_seq_queue_print_later(fluid_sequencer_t* seq) +{ + int count = 0; + fluid_evt_entry* tmp = seq->queueLater; + + printf("queueLater:\n"); + + while (tmp) { + unsigned int delay = tmp->evt.time - seq->queue0StartTime; + printf("queueLater: Delay = %i\n", delay); + tmp = tmp->next; + count++; + } + printf("queueLater: Total of %i events\n", count); +} + + +void +_fluid_seq_queue_insert_queue0(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell) +{ + if (seq->queue0[cell][1] == NULL) { + seq->queue0[cell][1] = seq->queue0[cell][0] = tmp; + } else { + seq->queue0[cell][1]->next = tmp; + seq->queue0[cell][1] = tmp; + } + tmp->next = NULL; +} + +void +_fluid_seq_queue_insert_queue1(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell) +{ + if (seq->queue1[cell][1] == NULL) { + seq->queue1[cell][1] = seq->queue1[cell][0] = tmp; + } else { + seq->queue1[cell][1]->next = tmp; + seq->queue1[cell][1] = tmp; + } + tmp->next = NULL; +} + +void +_fluid_seq_queue_insert_queue_later(fluid_sequencer_t* seq, fluid_evt_entry* evtentry) +{ + fluid_evt_entry* prev; + fluid_evt_entry* tmp; + unsigned int time = evtentry->evt.time; + + /* insert in 'queueLater', after the ones that have the same + * time */ + + /* first? */ + if ((seq->queueLater == NULL) + || (seq->queueLater->evt.time > time)) { + evtentry->next = seq->queueLater; + seq->queueLater = evtentry; + return; + } + + /* walk queueLater */ + /* this is the only slow thing : if the event is more + than 65535 ticks after the current time */ + + prev = seq->queueLater; + tmp = prev->next; + while (tmp) { + if (tmp->evt.time > time) { + /* insert before tmp */ + evtentry->next = tmp; + prev->next = evtentry; + return; + } + prev = tmp; + tmp = prev->next; + } + + /* last */ + evtentry->next = NULL; + prev->next = evtentry; +} + +void +_fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry) +{ + /* time is relative to seq origin, in ticks */ + fluid_event_t * evt = &(evtentry->evt); + unsigned int time = evt->time; + unsigned int delay; + + if (seq->queue0StartTime > 0) { + /* queue0StartTime could be < 0 if the scale changed a + lot early, breaking the following comparison + */ + if (time < (unsigned int)seq->queue0StartTime) { + /* we are late, send now */ + fluid_sequencer_send_now(seq, evt); + + _fluid_seq_heap_set_free(seq->heap, evtentry); + return; + } + } + + if (seq->prevCellNb >= 0) { + /* prevCellNb could be -1 is seq was just started - unlikely */ + /* prevCellNb can also be -1 if cellNb was reset to 0 in + _fluid_seq_queue_send_queued_events() */ + if (time <= (unsigned int)(seq->queue0StartTime + seq->prevCellNb)) { + /* we are late, send now */ + fluid_sequencer_send_now(seq, evt); + + _fluid_seq_heap_set_free(seq->heap, evtentry); + return; + } + } + + delay = time - seq->queue0StartTime; + + if (delay > 65535) { + _fluid_seq_queue_insert_queue_later(seq, evtentry); + + } else if (delay > 255) { + _fluid_seq_queue_insert_queue1(seq, evtentry, delay/256 - 1); + + } else { + _fluid_seq_queue_insert_queue0(seq, evtentry, delay); + } +} + +int +_fluid_seq_queue_matchevent(fluid_event_t* evt, int templType, short templSrc, short templDest) +{ + int eventType; + + if (templSrc != -1 && templSrc != fluid_event_get_source(evt)) + return 0; + + if (templDest != -1 && templDest != fluid_event_get_dest(evt)) + return 0; + + if (templType == -1) + return 1; + + eventType = fluid_event_get_type(evt); + + if (templType == eventType) + return 1; + + if (templType == FLUID_SEQ_ANYCONTROLCHANGE) + if (eventType == FLUID_SEQ_PITCHBEND || + eventType == FLUID_SEQ_MODULATION || + eventType == FLUID_SEQ_SUSTAIN || + eventType == FLUID_SEQ_PAN || + eventType == FLUID_SEQ_VOLUME || + eventType == FLUID_SEQ_REVERBSEND || + eventType == FLUID_SEQ_CONTROLCHANGE || + eventType == FLUID_SEQ_CHORUSSEND) + return 1; + + return 0; +} + +void +_fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* templ) +{ + /* we walk everything : this is slow, but that is life */ + int i, type; + short src, dest; + + src = templ->evt.src; + dest = templ->evt.dest; + type = templ->evt.type; + + /* we can set it free now */ + _fluid_seq_heap_set_free(seq->heap, templ); + + /* queue0 */ + for (i = 0 ; i < 256 ; i++) { + fluid_evt_entry* tmp = seq->queue0[i][0]; + fluid_evt_entry* prev = NULL; + while (tmp) { + /* remove and/or walk */ + if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { + /* remove */ + if (prev) { + prev->next = tmp->next; + if (tmp == seq->queue0[i][1]) // last one in list + seq->queue0[i][1] = prev; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = prev->next; + } else { + /* first one in list */ + seq->queue0[i][0] = tmp->next; + if (tmp == seq->queue0[i][1]) // last one in list + seq->queue0[i][1] = NULL; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = seq->queue0[i][0]; + } + } else { + prev = tmp; + tmp = prev->next; + } + } + } + + /* queue1 */ + for (i = 0 ; i < 255 ; i++) { + fluid_evt_entry* tmp = seq->queue1[i][0]; + fluid_evt_entry* prev = NULL; + while (tmp) { + if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { + /* remove */ + if (prev) { + prev->next = tmp->next; + if (tmp == seq->queue1[i][1]) // last one in list + seq->queue1[i][1] = prev; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = prev->next; + } else { + /* first one in list */ + seq->queue1[i][0] = tmp->next; + if (tmp == seq->queue1[i][1]) // last one in list + seq->queue1[i][1] = NULL; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = seq->queue1[i][0]; + } + } else { + prev = tmp; + tmp = prev->next; + } + } + } + + /* queueLater */ + { + fluid_evt_entry* tmp = seq->queueLater; + fluid_evt_entry* prev = NULL; + while (tmp) { + if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { + /* remove */ + if (prev) { + prev->next = tmp->next; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = prev->next; + } else { + seq->queueLater = tmp->next; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = seq->queueLater; + } + } else { + prev = tmp; + tmp = prev->next; + } + } + } +} + +void +_fluid_seq_queue_send_cell_events(fluid_sequencer_t* seq, int cellNb) +{ + fluid_evt_entry* next; + fluid_evt_entry* tmp; + + tmp = seq->queue0[cellNb][0]; + while (tmp) { + fluid_sequencer_send_now(seq, &(tmp->evt)); + + next = tmp->next; + + _fluid_seq_heap_set_free(seq->heap, tmp); + tmp = next; + } + seq->queue0[cellNb][0] = NULL; + seq->queue0[cellNb][1] = NULL; +} + +void +_fluid_seq_queue_slide(fluid_sequencer_t* seq) +{ + short i; + fluid_evt_entry* next; + fluid_evt_entry* tmp; + int count = 0; + + /* do the slide */ + seq->queue0StartTime += 256; + + /* sort all queue1[0] into queue0 according to new queue0StartTime */ + tmp = seq->queue1[0][0]; + while (tmp) { + unsigned int delay = tmp->evt.time - seq->queue0StartTime; + next = tmp->next; + if (delay > 255) { + /* should not happen !! */ + /* append it to queue1[1] */ + _fluid_seq_queue_insert_queue1(seq, tmp, 1); + } else { + _fluid_seq_queue_insert_queue0(seq, tmp, delay); + } + tmp = next; + count++; + } + + /* slide all queue1[i] into queue1[i-1] */ + for (i = 1 ; i < 255 ; i++) { + seq->queue1[i-1][0] = seq->queue1[i][0]; + seq->queue1[i-1][1] = seq->queue1[i][1]; + } + seq->queue1[254][0] = NULL; + seq->queue1[254][1] = NULL; + + + /* append queueLater to queue1[254] */ + count = 0; + tmp = seq->queueLater; + while (tmp) { + unsigned int delay = tmp->evt.time - seq->queue0StartTime; + + if (delay > 65535) { + break; + } + + next = tmp->next; + + /* append it */ + _fluid_seq_queue_insert_queue1(seq, tmp, 254); + tmp = next; + count++; + } + + seq->queueLater = tmp; +} + +void +_fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq) +{ + unsigned int nowTicks = fluid_sequencer_get_tick(seq); + short cellNb; + + cellNb = seq->prevCellNb + 1; + while (cellNb <= (int)(nowTicks - seq->queue0StartTime)) { + if (cellNb == 256) { + cellNb = 0; + _fluid_seq_queue_slide(seq); + } /* slide */ + + + /* process queue0[cellNb] */ + _fluid_seq_queue_send_cell_events(seq, cellNb); + + /* next cell */ + cellNb++; + } + + seq->prevCellNb = cellNb - 1; +} diff --git a/src/libs/fluidsynth/src/fluid_seqbind.c b/src/libs/fluidsynth/src/fluid_seqbind.c new file mode 100644 index 0000000000..542cdc63ce --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_seqbind.c @@ -0,0 +1,159 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ +*/ + +#include "fluidsynth_priv.h" + + /*************************************************************** + * + * SEQUENCER BINDING + */ + +void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data); + +/* registering the synth */ +short fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth) +{ + /* register fluidsynth itself */ + return fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)synth); +} + +/* the callback itself */ +void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_sequencer_t* seq, void* data) +{ + fluid_synth_t* synth = (fluid_synth_t *)data; + + switch (fluid_event_get_type(evt)) { + + case FLUID_SEQ_NOTEON: + fluid_synth_noteon(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_velocity(evt)); + break; + + case FLUID_SEQ_NOTEOFF: + fluid_synth_noteoff(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt)); + break; + + case FLUID_SEQ_NOTE: + { + unsigned int dur; + fluid_synth_noteon(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_velocity(evt)); + dur = fluid_event_get_duration(evt); + fluid_event_noteoff(evt, fluid_event_get_channel(evt), fluid_event_get_key(evt)); + fluid_sequencer_send_at(seq, evt, dur, 0); + } + break; + + case FLUID_SEQ_ALLSOUNDSOFF: + /* NYI */ + break; + + case FLUID_SEQ_ALLNOTESOFF: + fluid_synth_cc(synth, fluid_event_get_channel(evt), 0x7B, 0); + break; + + case FLUID_SEQ_BANKSELECT: + fluid_synth_bank_select(synth, fluid_event_get_channel(evt), fluid_event_get_bank(evt)); + break; + + case FLUID_SEQ_PROGRAMCHANGE: + fluid_synth_program_change(synth, fluid_event_get_channel(evt), fluid_event_get_program(evt)); + break; + + case FLUID_SEQ_PROGRAMSELECT: + fluid_synth_program_select(synth, fluid_event_get_channel(evt), fluid_event_get_sfont_id(evt), + fluid_event_get_bank(evt), fluid_event_get_program(evt)); + break; + + case FLUID_SEQ_ANYCONTROLCHANGE: + /* nothing = only used by remove_events */ + break; + + case FLUID_SEQ_PITCHBEND: + fluid_synth_pitch_bend(synth, fluid_event_get_channel(evt), fluid_event_get_pitch(evt)); + break; + + case FLUID_SEQ_PITCHWHHELSENS: + fluid_synth_pitch_wheel_sens(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_CONTROLCHANGE: + fluid_synth_cc(synth, fluid_event_get_channel(evt), fluid_event_get_control(evt), fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_MODULATION: + { + short ctrl = 0x01; // MODULATION_MSB + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_SUSTAIN: + { + short ctrl = 0x40; // SUSTAIN_SWITCH + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_PAN: + { + short ctrl = 0x0A; // PAN_MSB + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_VOLUME: + { + short ctrl = 0x07; // VOLUME_MSB + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_REVERBSEND: + { + short ctrl = 0x5B; // EFFECTS_DEPTH1 + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_CHORUSSEND: + { + short ctrl = 0x5D; // EFFECTS_DEPTH3 + fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); + } + break; + + case FLUID_SEQ_TIMER: + /* nothing in fluidsynth */ + break; + + default: + break; + } +} + + diff --git a/src/libs/fluidsynth/src/fluid_settings.c b/src/libs/fluidsynth/src/fluid_settings.c new file mode 100644 index 0000000000..923c538dff --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_settings.c @@ -0,0 +1,883 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluidsynth_priv.h" +#include "fluid_settings.h" +#include "fluid_hash.h" +#include "fluid_strtok.h" +#include "fluid_synth.h" +#include "fluid_cmd.h" +#include "fluid_adriver.h" +#include "fluid_mdriver.h" + + +static void fluid_settings_init(fluid_settings_t* settings); +static void fluid_settings_hash_delete(void* value, int type); +static int fluid_settings_tokenize(char* s, char* buf, char** ptr); + + +typedef struct { + char* value; + char* def; + int hints; + fluid_list_t* options; + fluid_str_update_t update; + void* data; +} fluid_str_setting_t; + +static fluid_str_setting_t* +new_fluid_str_setting(char* value, char* def, int hints, fluid_str_update_t fun, void* data) +{ + fluid_str_setting_t* str; + str = FLUID_NEW(fluid_str_setting_t); + str->value = value? FLUID_STRDUP(value) : NULL; + str->def = def? FLUID_STRDUP(def) : NULL; + str->hints = hints; + str->options = NULL; + str->update = fun; + str->data = data; + return str; +} + +static void delete_fluid_str_setting(fluid_str_setting_t* str) +{ + if (str) { + if (str->value) { + FLUID_FREE(str->value); + } + if (str->def) { + FLUID_FREE(str->def); + } + if (str->options) { + delete_fluid_list(str->options); + } + FLUID_FREE(str); + } +} + + + + +typedef struct { + double value; + double def; + double min; + double max; + int hints; + fluid_num_update_t update; + void* data; +} fluid_num_setting_t; + + +static fluid_num_setting_t* +new_fluid_num_setting(double min, double max, double def, + int hints, fluid_num_update_t fun, void* data) +{ + fluid_num_setting_t* setting; + setting = FLUID_NEW(fluid_num_setting_t); + setting->value = def; + setting->def = def; + setting->min = min; + setting->max = max; + setting->hints = hints; + setting->update = fun; + setting->data = data; + return setting; +} + +static void delete_fluid_num_setting(fluid_num_setting_t* setting) +{ + if (setting) { + FLUID_FREE(setting); + } +} + + + + +typedef struct { + int value; + int def; + int min; + int max; + int hints; + fluid_int_update_t update; + void* data; +} fluid_int_setting_t; + + +static fluid_int_setting_t* +new_fluid_int_setting(int min, int max, int def, + int hints, fluid_int_update_t fun, void* data) +{ + fluid_int_setting_t* setting; + setting = FLUID_NEW(fluid_int_setting_t); + setting->value = def; + setting->def = def; + setting->min = min; + setting->max = max; + setting->hints = hints; + setting->update = fun; + setting->data = data; + return setting; +} + +static void delete_fluid_int_setting(fluid_int_setting_t* setting) +{ + if (setting) { + FLUID_FREE(setting); + } +} + + + +fluid_settings_t* new_fluid_settings() +{ + fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete); + if (settings == NULL) { + return NULL; + } + fluid_settings_init(settings); + return settings; +} + +void delete_fluid_settings(fluid_settings_t* settings) +{ + delete_fluid_hashtable(settings); +} + +void fluid_settings_hash_delete(void* value, int type) +{ + switch (type) { + case FLUID_NUM_TYPE: + delete_fluid_num_setting((fluid_num_setting_t*) value); + break; + case FLUID_INT_TYPE: + delete_fluid_int_setting((fluid_int_setting_t*) value); + break; + case FLUID_STR_TYPE: + delete_fluid_str_setting((fluid_str_setting_t*) value); + break; + case FLUID_SET_TYPE: + delete_fluid_hashtable((fluid_hashtable_t*) value); + break; + } +} + +void fluid_settings_init(fluid_settings_t* settings) +{ + fluid_synth_settings(settings); + fluid_shell_settings(settings); + fluid_audio_driver_settings(settings); + fluid_midi_driver_settings(settings); +} + +static fluid_strtok_t* fluid_settings_strtok = NULL; + +int fluid_settings_tokenize(char* s, char* buf, char** ptr) +{ + int n = 0; + + FLUID_STRCPY(buf, s); + + if (fluid_settings_strtok == NULL) { + fluid_settings_strtok = new_fluid_strtok(buf, "."); + } else { + fluid_strtok_set(fluid_settings_strtok, buf, "."); + } + + while (fluid_strtok_has_more(fluid_settings_strtok)) { + ptr[n++] = fluid_strtok_next_token(fluid_settings_strtok); + } + + return n; +} + +/** returns 1 if the value exists, 0 otherwise */ +static int fluid_settings_get(fluid_settings_t* settings, + char** name, int len, + void** value, int* type) +{ + fluid_hashtable_t* table = settings; + int t; + void* v; + int n; + + for (n = 0; n < len; n++) { + + if (table == NULL) { + return 0; + } + + if (!fluid_hashtable_lookup(table, name[n], &v, &t)) { + return 0; + } + + table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL; + } + + if (value) { + *value = v; + } + + if (type) { + *type = t; + } + + return 1; +} + + +/** returns 1 if the value has been set, zero otherwise */ +static int fluid_settings_set(fluid_settings_t* settings, + char** name, int len, + void* value, int type) +{ + fluid_hashtable_t* table = settings; + int t; + void* v; + int n, num = len - 1; + + for (n = 0; n < num; n++) { + + if (fluid_hashtable_lookup(table, name[n], &v, &t)) { + + if (t == FLUID_SET_TYPE) { + table = (fluid_hashtable_t*) v; + } else { + /* path ends prematurely */ + FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); + return 0; + } + + } else { + /* create a new node */ + fluid_hashtable_t* tmp; + tmp = new_fluid_hashtable(fluid_settings_hash_delete); + fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE); + table = tmp; + } + } + + fluid_hashtable_replace(table, name[num], value, type); + + return 1; +} + +/** returns 1 if the value has been resgister correctly, 0 + otherwise */ +int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints, + fluid_str_update_t fun, void* data) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + fluid_str_setting_t* setting; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + setting = new_fluid_str_setting(def, def, hints, fun, data); + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); + + } else { + /* if variable already exists, don't change its value. */ + if (type == FLUID_STR_TYPE) { + setting = (fluid_str_setting_t*) value; + setting->update = fun; + setting->data = data; + setting->def = def? FLUID_STRDUP(def) : NULL; + setting->hints = hints; + return 1; + } else { + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + return 1; + } + } +} + +/** returns 1 if the value has been register correctly, zero + otherwise */ +int fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, + double min, double max, int hints, + fluid_num_update_t fun, void* data) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + /* insert a new setting */ + fluid_num_setting_t* setting; + setting = new_fluid_num_setting(min, max, def, hints, fun, data); + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); + + } else { + if (type == FLUID_NUM_TYPE) { + /* update the existing setting but don't change its value */ + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + setting->update = fun; + setting->data = data; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + return 1; + + } else { + /* type mismatch */ + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + return 0; + } + } +} + +/** returns 1 if the value has been register correctly, zero + otherwise */ +int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, + int min, int max, int hints, + fluid_int_update_t fun, void* data) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + /* insert a new setting */ + fluid_int_setting_t* setting; + setting = new_fluid_int_setting(min, max, def, hints, fun, data); + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); + + } else { + if (type == FLUID_INT_TYPE) { + /* update the existing setting but don't change its value */ + fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + setting->update = fun; + setting->data = data; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + return 1; + + } else { + /* type mismatch */ + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + return 0; + } + } +} + + +int fluid_settings_get_type(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE; +} + + +int fluid_settings_get_hints(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + if (type == FLUID_NUM_TYPE) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + return setting->hints; + } else if (type == FLUID_STR_TYPE) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + return setting->hints; + } else { + return 0; + } + } else { + return 0; + } +} + +int fluid_settings_is_realtime(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + if (type == FLUID_NUM_TYPE) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + return setting->update != NULL; + + } else if (type == FLUID_STR_TYPE) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + return setting->update != NULL; + } else { + return 0; + } + } else { + return 0; + } +} + + +int fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) +{ + char buf[1024]; + char* tokens[16]; + int ntokens; + int type; + void* value; + fluid_str_setting_t* setting; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + + if (type != FLUID_STR_TYPE) { + return 0; + } + + setting = (fluid_str_setting_t*) value; + + if (setting->value) { + FLUID_FREE(setting->value); + } + setting->value = str? FLUID_STRDUP(str) : NULL; + + if (setting->update) { + (*setting->update)(setting->data, name, setting->value); + } + + return 1; + + } else { + /* insert a new setting */ + fluid_str_setting_t* setting; + setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); + } +} + +int fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + *str = setting->value; + return 1; + } + *str = NULL; + return 0; +} + + +int fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + return FLUID_STRCMP(setting->value, s) == 0; + } + return 0; +} + + +char* +fluid_settings_getstr_default(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + return setting->def; + } else { + return NULL; + } +} + +int fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + char* copy = FLUID_STRDUP(s); + setting->options = fluid_list_append(setting->options, copy); + return 1; + } else { + return 0; + } +} + +int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + fluid_list_t* list = setting->options; + + while (list) { + char* option = (char*) fluid_list_get(list); + if (FLUID_STRCMP(s, option) == 0) { + setting->options = fluid_list_remove_link(setting->options, list); + return 1; + } + list = fluid_list_next(list); + } + + return 0; + } else { + return 0; + } +} + + +int fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) +{ + int type; + void* value; + fluid_num_setting_t* setting; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + + if (type != FLUID_NUM_TYPE) { + return 0; + } + + setting = (fluid_num_setting_t*) value; + + if (val < setting->min) { + val = setting->min; + } else if (val > setting->max) { + val = setting->max; + } + + setting->value = val; + + if (setting->update) { + (*setting->update)(setting->data, name, val); + } + + return 1; + + } else { + /* insert a new setting */ + fluid_num_setting_t* setting; + setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL); + setting->value = val; + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); + } +} + +int fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + *val = setting->value; + return 1; + } + return 0; +} + + +void fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, double* max) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + *min = setting->min; + *max = setting->max; + } +} + +double +fluid_settings_getnum_default(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + return setting->def; + } else { + return 0.0f; + } +} + + +int fluid_settings_setint(fluid_settings_t* settings, char* name, int val) +{ + int type; + void* value; + fluid_int_setting_t* setting; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + + if (type != FLUID_INT_TYPE) { + return 0; + } + + setting = (fluid_int_setting_t*) value; + + if (val < setting->min) { + val = setting->min; + } else if (val > setting->max) { + val = setting->max; + } + + setting->value = val; + + if (setting->update) { + (*setting->update)(setting->data, name, val); + } + + return 1; + + } else { + /* insert a new setting */ + fluid_int_setting_t* setting; + setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL); + setting->value = val; + return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); + } +} + +int fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + *val = setting->value; + return 1; + } + return 0; +} + + +void fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, int* max) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + *min = setting->min; + *max = setting->max; + } +} + +int +fluid_settings_getint_default(fluid_settings_t* settings, char* name) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + return setting->def; + } else { + return 0.0f; + } +} + + + + +void fluid_settings_foreach_option(fluid_settings_t* settings, char* name, void* data, + fluid_settings_foreach_option_t func) +{ + int type; + void* value; + char buf[1024]; + char* tokens[16]; + int ntokens; + + if (!func) { + return; + } + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if (fluid_settings_get(settings, tokens, ntokens, &value, &type) + && (type == FLUID_STR_TYPE)) { + + fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + fluid_list_t* list = setting->options; + + while (list) { + char* option = (char*) fluid_list_get(list); + (*func)(data, name, option); + list = fluid_list_next(list); + } + } +} + + +static fluid_settings_foreach_t fluid_settings_foreach_func; +static void* fluid_settings_foreach_data; + +int fluid_settings_foreach_iter(char* key, void* value, int type, void* data) +{ + char path[1024]; + + if (data == 0) { + snprintf(path, 1024, "%s", key); + } else { + snprintf(path, 1024, "%s.%s", (char*) data, key); + } + path[1023] = 0; + + switch (type) { + case FLUID_NUM_TYPE: + case FLUID_INT_TYPE: + case FLUID_STR_TYPE: + (*fluid_settings_foreach_func)(fluid_settings_foreach_data, path, type); + break; + case FLUID_SET_TYPE: + fluid_hashtable_foreach((fluid_hashtable_t*) value, fluid_settings_foreach_iter, &path[0]); + break; + } + + return 0; +} + +void fluid_settings_foreach(fluid_settings_t* settings, void* data, fluid_settings_foreach_t func) +{ + fluid_settings_foreach_func = func; + fluid_settings_foreach_data = data; + fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, 0); +} diff --git a/src/libs/fluidsynth/src/fluid_settings.h b/src/libs/fluidsynth/src/fluid_settings.h new file mode 100644 index 0000000000..d6d7dae5a7 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_settings.h @@ -0,0 +1,55 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_SETTINGS_H +#define _FLUID_SETTINGS_H + + + +/** returns 1 if the option was added, 0 otherwise */ +int fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s); + +/** returns 1 if the option was added, 0 otherwise */ +int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s); + + +typedef int (*fluid_num_update_t)(void* data, char* name, double value); +typedef int (*fluid_str_update_t)(void* data, char* name, char* value); +typedef int (*fluid_int_update_t)(void* data, char* name, int value); + +/** returns 0 if the value has been resgister correctly, non-zero + otherwise */ +int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints, + fluid_str_update_t fun, void* data); + +/** returns 0 if the value has been resgister correctly, non-zero + otherwise */ +int fluid_settings_register_num(fluid_settings_t* settings, char* name, double min, double max, + double def, int hints, fluid_num_update_t fun, void* data); + + +/** returns 0 if the value has been resgister correctly, non-zero + otherwise */ +int fluid_settings_register_int(fluid_settings_t* settings, char* name, int min, int max, + int def, int hints, fluid_int_update_t fun, void* data); + + +#endif /* _FLUID_SETTINGS_H */ diff --git a/src/libs/fluidsynth/src/fluid_sfont.h b/src/libs/fluidsynth/src/fluid_sfont.h new file mode 100644 index 0000000000..e22910b993 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_sfont.h @@ -0,0 +1,68 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _PRIV_FLUID_SFONT_H +#define _PRIV_FLUID_SFONT_H + + +/* + * Utility macros to access soundfonts, presets, and samples + */ + +#define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); } +#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) + + +#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) +#define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf) +#define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum) +#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf) +#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr) +#define fluid_sfont_get_data(_sf) (_sf)->data +#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); } + + +#define delete_fluid_preset(_preset) \ + { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} + +#define fluid_preset_get_data(_preset) (_preset)->data +#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); } +#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset) +#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset) +#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset) + +#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ + (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) + +#define fluid_preset_notify(_preset,_reason,_chan) \ + { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }} + + +#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } + +#define fluid_sample_decr_ref(_sample) \ + (_sample)->refcount--; \ + if (((_sample)->refcount == 0) && ((_sample)->notify)) \ + (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE); + + + +#endif /* _PRIV_FLUID_SFONT_H */ diff --git a/src/libs/fluidsynth/src/fluid_sse.h b/src/libs/fluidsynth/src/fluid_sse.h new file mode 100644 index 0000000000..84b958a852 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_sse.h @@ -0,0 +1,263 @@ +/* + * sse.h + * Copyright (C) 1999 R. Fisher + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * + * mpeg2dec 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. + * + * mpeg2dec 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 + */ + +/* Purpose: +* This file defines assembler macros for the SSE instructions of Pentium III and higher. +* They are used to execute several floating point multiplications in parallel. +*/ + + +#ifndef FLUID_SSE_H +#define FLUID_SSE_H +typedef union { + float sf[4]; /* Single-precision (32-bit) value */ +} __attribute__ ((aligned(16))) sse_t; /* On a 16 byte (128-bit) boundary */ + + +#define sse_i2r(op, imm, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (imm) ) + +#define sse_m2r(op, mem, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (mem)) + +#define sse_r2m(op, reg, mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=X" (mem) \ + : /* nothing */ ) + +#define sse_r2r(op, regs, regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + +#define sse_r2ri(op, regs, regd, imm) \ + __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ + : /* nothing */ \ + : "X" (imm) ) + +#define sse_m2ri(op, mem, reg, subop) \ + __asm__ __volatile__ (#op " %0, %%" #reg ", " #subop \ + : /* nothing */ \ + : "X" (mem)) + + +#define movaps_m2r(var, reg) sse_m2r(movaps, var, reg) +#define movaps_r2m(reg, var) sse_r2m(movaps, reg, var) +#define movaps_r2r(regs, regd) sse_r2r(movaps, regs, regd) + +#define movntps_r2m(xmmreg, var) sse_r2m(movntps, xmmreg, var) + +#define movups_m2r(var, reg) sse_m2r(movups, var, reg) +#define movups_r2m(reg, var) sse_r2m(movups, reg, var) +#define movups_r2r(regs, regd) sse_r2r(movups, regs, regd) + +#define movhlps_r2r(regs, regd) sse_r2r(movhlps, regs, regd) + +#define movlhps_r2r(regs, regd) sse_r2r(movlhps, regs, regd) + +#define movhps_m2r(var, reg) sse_m2r(movhps, var, reg) +#define movhps_r2m(reg, var) sse_r2m(movhps, reg, var) + +#define movlps_m2r(var, reg) sse_m2r(movlps, var, reg) +#define movlps_r2m(reg, var) sse_r2m(movlps, reg, var) + +#define movss_m2r(var, reg) sse_m2r(movss, var, reg) +#define movss_r2m(reg, var) sse_r2m(movss, reg, var) +#define movss_r2r(regs, regd) sse_r2r(movss, regs, regd) + +#define shufps_m2r(var, reg, index) sse_m2ri(shufps, var, reg, index) +#define shufps_r2r(regs, regd, index) sse_r2ri(shufps, regs, regd, index) + +#define cvtpi2ps_m2r(var, xmmreg) sse_m2r(cvtpi2ps, var, xmmreg) +#define cvtpi2ps_r2r(mmreg, xmmreg) sse_r2r(cvtpi2ps, mmreg, xmmreg) + +#define cvtps2pi_m2r(var, mmreg) sse_m2r(cvtps2pi, var, mmreg) +#define cvtps2pi_r2r(xmmreg, mmreg) sse_r2r(cvtps2pi, mmreg, xmmreg) + +#define cvttps2pi_m2r(var, mmreg) sse_m2r(cvttps2pi, var, mmreg) +#define cvttps2pi_r2r(xmmreg, mmreg) sse_r2r(cvttps2pi, mmreg, xmmreg) + +#define cvtsi2ss_m2r(var, xmmreg) sse_m2r(cvtsi2ss, var, xmmreg) +#define cvtsi2ss_r2r(reg, xmmreg) sse_r2r(cvtsi2ss, reg, xmmreg) + +#define cvtss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) +#define cvtss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) + +#define cvttss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) +#define cvttss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) + +#define movmskps(xmmreg, reg) \ + __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg) + +#define addps_m2r(var, reg) sse_m2r(addps, var, reg) +#define addps_r2r(regs, regd) sse_r2r(addps, regs, regd) + +#define addss_m2r(var, reg) sse_m2r(addss, var, reg) +#define addss_r2r(regs, regd) sse_r2r(addss, regs, regd) + +#define subps_m2r(var, reg) sse_m2r(subps, var, reg) +#define subps_r2r(regs, regd) sse_r2r(subps, regs, regd) + +#define subss_m2r(var, reg) sse_m2r(subss, var, reg) +#define subss_r2r(regs, regd) sse_r2r(subss, regs, regd) + +#define mulps_m2r(var, reg) sse_m2r(mulps, var, reg) +#define mulps_r2r(regs, regd) sse_r2r(mulps, regs, regd) + +#define mulss_m2r(var, reg) sse_m2r(mulss, var, reg) +#define mulss_r2r(regs, regd) sse_r2r(mulss, regs, regd) + +#define divps_m2r(var, reg) sse_m2r(divps, var, reg) +#define divps_r2r(regs, regd) sse_r2r(divps, regs, regd) + +#define divss_m2r(var, reg) sse_m2r(divss, var, reg) +#define divss_r2r(regs, regd) sse_r2r(divss, regs, regd) + +#define rcpps_m2r(var, reg) sse_m2r(rcpps, var, reg) +#define rcpps_r2r(regs, regd) sse_r2r(rcpps, regs, regd) + +#define rcpss_m2r(var, reg) sse_m2r(rcpss, var, reg) +#define rcpss_r2r(regs, regd) sse_r2r(rcpss, regs, regd) + +#define rsqrtps_m2r(var, reg) sse_m2r(rsqrtps, var, reg) +#define rsqrtps_r2r(regs, regd) sse_r2r(rsqrtps, regs, regd) + +#define rsqrtss_m2r(var, reg) sse_m2r(rsqrtss, var, reg) +#define rsqrtss_r2r(regs, regd) sse_r2r(rsqrtss, regs, regd) + +#define sqrtps_m2r(var, reg) sse_m2r(sqrtps, var, reg) +#define sqrtps_r2r(regs, regd) sse_r2r(sqrtps, regs, regd) + +#define sqrtss_m2r(var, reg) sse_m2r(sqrtss, var, reg) +#define sqrtss_r2r(regs, regd) sse_r2r(sqrtss, regs, regd) + +#define andps_m2r(var, reg) sse_m2r(andps, var, reg) +#define andps_r2r(regs, regd) sse_r2r(andps, regs, regd) + +#define andnps_m2r(var, reg) sse_m2r(andnps, var, reg) +#define andnps_r2r(regs, regd) sse_r2r(andnps, regs, regd) + +#define orps_m2r(var, reg) sse_m2r(orps, var, reg) +#define orps_r2r(regs, regd) sse_r2r(orps, regs, regd) + +#define xorps_m2r(var, reg) sse_m2r(xorps, var, reg) +#define xorps_r2r(regs, regd) sse_r2r(xorps, regs, regd) + +#define maxps_m2r(var, reg) sse_m2r(maxps, var, reg) +#define maxps_r2r(regs, regd) sse_r2r(maxps, regs, regd) + +#define maxss_m2r(var, reg) sse_m2r(maxss, var, reg) +#define maxss_r2r(regs, regd) sse_r2r(maxss, regs, regd) + +#define minps_m2r(var, reg) sse_m2r(minps, var, reg) +#define minps_r2r(regs, regd) sse_r2r(minps, regs, regd) + +#define minss_m2r(var, reg) sse_m2r(minss, var, reg) +#define minss_r2r(regs, regd) sse_r2r(minss, regs, regd) + +#define cmpps_m2r(var, reg, op) sse_m2ri(cmpps, var, reg, op) +#define cmpps_r2r(regs, regd, op) sse_r2ri(cmpps, regs, regd, op) + +#define cmpeqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 0) +#define cmpeqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 0) + +#define cmpltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 1) +#define cmpltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 1) + +#define cmpleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 2) +#define cmpleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 2) + +#define cmpunordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 3) +#define cmpunordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 3) + +#define cmpneqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 4) +#define cmpneqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 4) + +#define cmpnltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 5) +#define cmpnltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 5) + +#define cmpnleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 6) +#define cmpnleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 6) + +#define cmpordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 7) +#define cmpordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 7) + +#define cmpss_m2r(var, reg, op) sse_m2ri(cmpss, var, reg, op) +#define cmpss_r2r(regs, regd, op) sse_r2ri(cmpss, regs, regd, op) + +#define cmpeqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 0) +#define cmpeqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 0) + +#define cmpltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 1) +#define cmpltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 1) + +#define cmpless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 2) +#define cmpless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 2) + +#define cmpunordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 3) +#define cmpunordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 3) + +#define cmpneqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 4) +#define cmpneqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 4) + +#define cmpnltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 5) +#define cmpnltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 5) + +#define cmpnless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 6) +#define cmpnless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 6) + +#define cmpordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 7) +#define cmpordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 7) + +#define comiss_m2r(var, reg) sse_m2r(comiss, var, reg) +#define comiss_r2r(regs, regd) sse_r2r(comiss, regs, regd) + +#define ucomiss_m2r(var, reg) sse_m2r(ucomiss, var, reg) +#define ucomiss_r2r(regs, regd) sse_r2r(ucomiss, regs, regd) + +#define unpcklps_m2r(var, reg) sse_m2r(unpcklps, var, reg) +#define unpcklps_r2r(regs, regd) sse_r2r(unpcklps, regs, regd) + +#define unpckhps_m2r(var, reg) sse_m2r(unpckhps, var, reg) +#define unpckhps_r2r(regs, regd) sse_r2r(unpckhps, regs, regd) + +#define fxrstor(mem) \ + __asm__ __volatile__ ("fxrstor %0" \ + : /* nothing */ \ + : "X" (mem)) + +#define fxsave(mem) \ + __asm__ __volatile__ ("fxsave %0" \ + : /* nothing */ \ + : "X" (mem)) + +#define stmxcsr(mem) \ + __asm__ __volatile__ ("stmxcsr %0" \ + : /* nothing */ \ + : "X" (mem)) + +#define ldmxcsr(mem) \ + __asm__ __volatile__ ("ldmxcsr %0" \ + : /* nothing */ \ + : "X" (mem)) +#endif diff --git a/src/libs/fluidsynth/src/fluid_strtok.c b/src/libs/fluidsynth/src/fluid_strtok.c new file mode 100644 index 0000000000..6f115ca6fb --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_strtok.c @@ -0,0 +1,123 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_strtok.h" +#include "fluidsynth_priv.h" + +/* + * new_fluid_strtok + */ +fluid_strtok_t* new_fluid_strtok(char* s, char* d) +{ + fluid_strtok_t* st; + st = FLUID_NEW(fluid_strtok_t); + if (st == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + /* Careful! the strings are not copied for speed */ + st->string = s; + st->delimiters = d; + st->offset = 0; + st->len = (s == NULL)? 0 : strlen(s); + return st; +} + +int delete_fluid_strtok(fluid_strtok_t* st) +{ + if (st == NULL) { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return 0; + } + free(st); + return 0; +} + +int fluid_strtok_set(fluid_strtok_t* st, char* s, char* d) +{ + /* Careful! the strings are not copied for speed */ + st->string = s; + st->delimiters = d; + st->offset = 0; + st->len = (s == NULL)? 0 : strlen(s); + return 0; +} + +char* fluid_strtok_next_token(fluid_strtok_t* st) +{ + int start = st->offset; + int end; + if ((st == NULL) || (st->string == NULL) || (st->delimiters == NULL)) { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return NULL; + } + if (start >= st->len) { + return NULL; + } + while (fluid_strtok_char_index(st->string[start], st->delimiters) >= 0) { + if (start == st->len) { + return NULL; + } + start++; + } + end = start + 1; + while (fluid_strtok_char_index(st->string[end], st->delimiters) < 0) { + if (end == st->len) { + break; + } + end++; + } + st->string[end] = 0; + st->offset = end + 1; + return &st->string[start]; +} + +int fluid_strtok_has_more(fluid_strtok_t* st) +{ + int cur = st->offset; + if ((st == NULL) || (st->string == NULL) || (st->delimiters == NULL)) { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return -1; + } + while (cur < st->len) { + if (fluid_strtok_char_index(st->string[cur], st->delimiters) < 0) { + return -1; + } + cur++; + } + return 0; +} + +int fluid_strtok_char_index(char c, char* s) +{ + int i; + if (s == NULL) { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return -1; + } + for (i = 0; s[i] != 0; i++) { + if (s[i] == c) { + return i; + } + } + return -1; +} + diff --git a/src/libs/fluidsynth/src/fluid_strtok.h b/src/libs/fluidsynth/src/fluid_strtok.h new file mode 100644 index 0000000000..0764905848 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_strtok.h @@ -0,0 +1,44 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_STRTOK_H +#define _FLUID_STRTOK_H + +#include "fluidsynth_priv.h" + + +/** string tokenizer */ +typedef struct { + char* string; + char* delimiters; + int offset; + int len; +} fluid_strtok_t; + +fluid_strtok_t* new_fluid_strtok(char* s, char* d); +int delete_fluid_strtok(fluid_strtok_t* st); +int fluid_strtok_set(fluid_strtok_t* st, char* s, char* d); +char* fluid_strtok_next_token(fluid_strtok_t* st); +int fluid_strtok_has_more(fluid_strtok_t* st); +int fluid_strtok_char_index(char c, char* s); + + +#endif /* _FLUID_STRTOK_H */ diff --git a/src/libs/fluidsynth/src/fluid_synth.c b/src/libs/fluidsynth/src/fluid_synth.c new file mode 100644 index 0000000000..2baf4e5a1e --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_synth.c @@ -0,0 +1,3083 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_chan.h" +#include "fluid_tuning.h" +#include "fluid_settings.h" +#include "fluid_sfont.h" + + +fluid_sfloader_t* new_fluid_defsfloader(void); + +/************************************************************************ + * + * These functions were added after the v1.0 API freeze. They are not + * in synth.h. They should be added as soon as a new development + * version is started. + * + ************************************************************************/ + +int fluid_synth_program_select2(fluid_synth_t* synth, + int chan, + char* sfont_name, + unsigned int bank_num, + unsigned int preset_num); + +fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name); + +int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, + int param, float value, + int absolute, int normalized); + + +/*************************************************************** + * + * GLOBAL + */ + +/* has the synth module been initialized? */ +static int fluid_synth_initialized = 0; +static void fluid_synth_init(void); + +/* default modulators + * SF2.01 page 52 ff: + * + * There is a set of predefined default modulators. They have to be + * explicitly overridden by the sound font in order to turn them off. + */ + +fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ +fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ +fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ +fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ +fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ +fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ +fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ +fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ +fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ +fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ + +/* reverb presets */ +static fluid_revmodel_presets_t revmodel_preset[] = { + /* name */ /* roomsize */ /* damp */ /* width */ /* level */ + { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f }, + { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f }, + { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f }, + { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f }, + { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f }, + { NULL, 0.0f, 0.0f, 0.0f, 0.0f } +}; + + +/*************************************************************** + * + * INITIALIZATION & UTILITIES + */ + + +void fluid_synth_settings(fluid_settings_t* settings) +{ + fluid_settings_register_str(settings, "synth.verbose", "no", 0, NULL, NULL); + fluid_settings_register_str(settings, "synth.dump", "no", 0, NULL, NULL); + fluid_settings_register_str(settings, "synth.reverb.active", "yes", 0, NULL, NULL); + fluid_settings_register_str(settings, "synth.chorus.active", "yes", 0, NULL, NULL); + fluid_settings_register_str(settings, "synth.ladspa.active", "no", 0, NULL, NULL); + + fluid_settings_register_int(settings, "synth.polyphony", + 256, 16, 4096, 0, NULL, NULL); + fluid_settings_register_int(settings, "synth.midi-channels", + 16, 16, 256, 0, NULL, NULL); + fluid_settings_register_num(settings, "synth.gain", + 0.2f, 0.0f, 10.0f, + 0, NULL, NULL); + fluid_settings_register_int(settings, "synth.audio-channels", + 1, 1, 256, 0, NULL, NULL); + fluid_settings_register_int(settings, "synth.audio-groups", + 1, 1, 256, 0, NULL, NULL); + fluid_settings_register_int(settings, "synth.effects-channels", + 2, 2, 2, 0, NULL, NULL); + fluid_settings_register_num(settings, "synth.sample-rate", + 44100.0f, 22050.0f, 96000.0f, + 0, NULL, NULL); +} + +/* + * fluid_version + */ +void fluid_version(int *major, int *minor, int *micro) +{ + *major = FLUIDSYNTH_VERSION_MAJOR; + *minor = FLUIDSYNTH_VERSION_MINOR; + *micro = FLUIDSYNTH_VERSION_MICRO; +} + +/* + * fluid_version_str + */ +char* fluid_version_str(void) +{ + return FLUIDSYNTH_VERSION; +} + + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +static void +fluid_synth_init() +{ + fluid_synth_initialized++; + + fluid_conversion_config(); + + fluid_voice_config(); + + fluid_sys_config(); + + + /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ + fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ + FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */ + FLUID_MOD_GC /* Not a MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_vel2att_mod, 960.0); /* Modulation amount: 960 */ + + + + /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff + * Have to make a design decision here. The specs don't make any sense this way or another. + * One sound font, 'Kingston Piano', which has been praised for its quality, tries to + * override this modulator with an amount of 0 and positive polarity (instead of what + * the specs say, D=1) for the secondary source. + * So if we change the polarity to 'positive', one of the best free sound fonts works... + */ + fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_SWITCH /* type=3 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + // do not remove | FLUID_MOD_NEGATIVE /* D=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */ + fluid_mod_set_amount(&default_vel2filter_mod, -2400); + + + + /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_at2viblfo_mod, 0,0); /* no second source */ + fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_at2viblfo_mod, 50); + + + + /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_mod2viblfo_mod, 1, /* Index=1 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_mod2viblfo_mod, 0,0); /* no second source */ + fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_mod2viblfo_mod, 50); + + + + /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/ + fluid_mod_set_source1(&default_att_mod, 7, /* index=7 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_att_mod, 960.0); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ + fluid_mod_set_source1(&default_pan_mod, 10, /* index=10 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ + /* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000 + tenths of a percent". The center value (64) corresponds to 50%, + so it follows that amount = 50% x 1000/% = 500. */ + fluid_mod_set_amount(&default_pan_mod, 500.0); + + + /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/ + fluid_mod_set_source1(&default_expr_mod, 11, /* index=11 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_expr_mod, 960.0); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ + fluid_mod_set_source1(&default_reverb_mod, 91, /* index=91 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */ + fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Reverb send */ + fluid_mod_set_source1(&default_chorus_mod, 93, /* index=93 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */ + fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ + fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ + FLUID_MOD_GC /* CC =0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */ + fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ +} + + +int fluid_synth_verify_settings(fluid_settings_t *settings) +{ + return 0; +} + +/*************************************************************** + * + * FLUID SYNTH + */ + +/* + * new_fluid_synth + */ +fluid_synth_t* +new_fluid_synth(fluid_settings_t *settings) +{ + int i; + fluid_synth_t* synth; + fluid_sfloader_t* loader; + + /* initialize all the conversion tables and other stuff */ + if (fluid_synth_initialized == 0) { + fluid_synth_init(); + } + + fluid_synth_verify_settings(settings); + + /* allocate a new synthesizer object */ + synth = FLUID_NEW(fluid_synth_t); + if (synth == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); + + fluid_mutex_init(synth->busy); + + synth->settings = settings; + + synth->with_reverb = fluid_settings_str_equal(settings, "synth.reverb.active", "yes"); + synth->with_chorus = fluid_settings_str_equal(settings, "synth.chorus.active", "yes"); + synth->verbose = fluid_settings_str_equal(settings, "synth.verbose", "yes"); + synth->dump = fluid_settings_str_equal(settings, "synth.dump", "yes"); + + fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); + fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); + fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); + fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); + fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); + fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); + fluid_settings_getnum(settings, "synth.gain", &synth->gain); + + /* register the callbacks */ + fluid_settings_register_num(settings, "synth.gain", + 0.2f, 0.0f, 10.0f, 0, + (fluid_num_update_t) fluid_synth_update_gain, synth); + fluid_settings_register_int(settings, "synth.polyphony", + synth->polyphony, 16, 4096, 0, + (fluid_int_update_t) fluid_synth_update_polyphony, + synth); + + /* do some basic sanity checking on the settings */ + + if (synth->midi_channels % 16 != 0) { + int n = synth->midi_channels / 16; + synth->midi_channels = (n + 1) * 16; + fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels); + FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. " + "I'll increase the number of channels to the next multiple."); + } + + if (synth->audio_channels < 1) { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. " + "Changing this setting to 1."); + synth->audio_channels = 1; + } else if (synth->audio_channels > 128) { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). " + "Limiting this setting to 128.", synth->audio_channels); + synth->audio_channels = 128; + } + + if (synth->audio_groups < 1) { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. " + "Changing this setting to 1."); + synth->audio_groups = 1; + } else if (synth->audio_groups > 128) { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). " + "Limiting this setting to 128.", synth->audio_groups); + synth->audio_groups = 128; + } + + if (synth->effects_channels != 2) { + FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)." + "Setting effects channels to 2.", synth->effects_channels); + synth->effects_channels = 2; + } + + + /* The number of buffers is determined by the higher number of nr + * groups / nr audio channels. If LADSPA is unused, they should be + * the same. */ + synth->nbuf = synth->audio_channels; + if (synth->audio_groups > synth->nbuf) { + synth->nbuf = synth->audio_groups; + } + +#ifdef LADSPA + /* Create and initialize the Fx unit.*/ + synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth); +#endif + + /* as soon as the synth is created it starts playing. */ + synth->state = FLUID_SYNTH_PLAYING; + synth->sfont = NULL; + synth->noteid = 0; + synth->ticks = 0; + synth->tuning = NULL; + + /* allocate and add the default sfont loader */ + loader = new_fluid_defsfloader(); + + if (loader == NULL) { + FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader"); + } else { + fluid_synth_add_sfloader(synth, loader); + } + + /* allocate all channel objects */ + synth->channel = FLUID_ARRAY(fluid_channel_t*, synth->midi_channels); + if (synth->channel == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + for (i = 0; i < synth->midi_channels; i++) { + synth->channel[i] = new_fluid_channel(synth, i); + if (synth->channel[i] == NULL) { + goto error_recovery; + } + } + + /* allocate all synthesis processes */ + synth->nvoice = synth->polyphony; + synth->voice = FLUID_ARRAY(fluid_voice_t*, synth->nvoice); + if (synth->voice == NULL) { + goto error_recovery; + } + for (i = 0; i < synth->nvoice; i++) { + synth->voice[i] = new_fluid_voice(synth->sample_rate); + if (synth->voice[i] == NULL) { + goto error_recovery; + } + } + + /* Allocate the sample buffers + * + * GCC seems to have a bug with alignment (address must be multiple + * of 16 bytes. So we have to align for ourselves... As soon as + * GCC aligns reliably, this mess can be cleaned up. + */ + + synth->left_buf = NULL; + synth->right_buf = NULL; + synth->left_ubuf = NULL; + synth->right_ubuf = NULL; + synth->fx_left_buf = NULL; + synth->fx_right_buf = NULL; + synth->fx_left_ubuf = NULL; + synth->fx_right_ubuf = NULL; + + /* Left and right audio buffers */ + + synth->left_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); + synth->right_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); + synth->left_ubuf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); + synth->right_ubuf = FLUID_ARRAY(fluid_real_t*, synth->nbuf); + + if ((synth->left_buf == NULL) || (synth->right_buf == NULL) || + (synth->left_ubuf == NULL) || (synth->right_ubuf == NULL)) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(synth->left_buf, 0, synth->nbuf * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->right_buf, 0, synth->nbuf * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->left_ubuf, 0, synth->nbuf * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->right_ubuf, 0, synth->nbuf * sizeof(fluid_real_t*)); + + for (i = 0; i < synth->nbuf; i++) { + + /* +4: add four floats for 16 added bytes */ + + synth->left_ubuf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE + 4); + synth->right_ubuf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE + 4); + + if ((synth->left_ubuf[i] == NULL) || (synth->right_ubuf[i] == NULL)) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + synth->left_buf[i] = (fluid_real_t*) FLUID_ALIGN16BYTE(synth->left_ubuf[i]); + synth->right_buf[i] = (fluid_real_t*) FLUID_ALIGN16BYTE(synth->right_ubuf[i]); + } + + /* Effects audio buffers */ + + synth->fx_left_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); + synth->fx_right_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); + synth->fx_left_ubuf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); + synth->fx_right_ubuf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels); + + if ((synth->fx_left_buf == NULL) || (synth->fx_left_ubuf == NULL) || + (synth->fx_right_buf == NULL) || (synth->fx_right_ubuf == NULL)) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(synth->fx_left_ubuf, 0, 2 * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->fx_left_buf, 0, 2 * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->fx_right_ubuf, 0, 2 * sizeof(fluid_real_t*)); + FLUID_MEMSET(synth->fx_right_buf, 0, 2 * sizeof(fluid_real_t*)); + + for (i = 0; i < synth->effects_channels; i++) { + + /* +4: add four floats for 16 added bytes */ + synth->fx_left_ubuf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE + 4); + synth->fx_right_ubuf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE + 4); + + if ((synth->fx_left_ubuf[i] == NULL) || (synth->fx_right_ubuf[i] == NULL)) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + synth->fx_left_buf[i] = (fluid_real_t*) FLUID_ALIGN16BYTE(synth->fx_left_ubuf[i]); + synth->fx_right_buf[i] = (fluid_real_t*) FLUID_ALIGN16BYTE(synth->fx_right_ubuf[i]); + } + + + synth->cur = FLUID_BUFSIZE; + + /* allocate the reverb module */ + synth->reverb = new_fluid_revmodel(); + if (synth->reverb == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + fluid_synth_set_reverb(synth, + FLUID_REVERB_DEFAULT_ROOMSIZE, + FLUID_REVERB_DEFAULT_DAMP, + FLUID_REVERB_DEFAULT_WIDTH, + FLUID_REVERB_DEFAULT_LEVEL); + + /* allocate the chorus module */ + synth->chorus = new_fluid_chorus(synth->sample_rate); + if (synth->chorus == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + /* FIXME */ + synth->start = fluid_curtime(); + + return synth; + + error_recovery: + delete_fluid_synth(synth); + return NULL; +} + +/* + * delete_fluid_synth + */ +int +delete_fluid_synth(fluid_synth_t* synth) +{ + int i, k; + fluid_list_t *list; + fluid_sfont_t* sfont; + fluid_bank_offset_t* bank_offset; + fluid_sfloader_t* loader; + + if (synth == NULL) { + return FLUID_OK; + } + + fluid_profiling_print(); + + synth->state = FLUID_SYNTH_STOPPED; + + /* delete all the SoundFonts */ + for (list = synth->sfont; list; list = fluid_list_next(list)) { + sfont = (fluid_sfont_t*) fluid_list_get(list); + delete_fluid_sfont(sfont); + } + + delete_fluid_list(synth->sfont); + + /* and the SoundFont offsets */ + for (list = synth->bank_offsets; list; list = fluid_list_next(list)) { + bank_offset = (fluid_bank_offset_t*) fluid_list_get(list); + FLUID_FREE(bank_offset); + } + + delete_fluid_list(synth->bank_offsets); + + + /* delete all the SoundFont loaders */ + + for (list = synth->loaders; list; list = fluid_list_next(list)) { + loader = (fluid_sfloader_t*) fluid_list_get(list); + fluid_sfloader_delete(loader); + } + + delete_fluid_list(synth->loaders); + + + if (synth->channel != NULL) { + for (i = 0; i < synth->midi_channels; i++) { + if (synth->channel[i] != NULL) { + delete_fluid_channel(synth->channel[i]); + } + } + FLUID_FREE(synth->channel); + } + + if (synth->voice != NULL) { + for (i = 0; i < synth->nvoice; i++) { + if (synth->voice[i] != NULL) { + delete_fluid_voice(synth->voice[i]); + } + } + FLUID_FREE(synth->voice); + } + + /* free all the sample buffers */ + if (synth->left_ubuf != NULL) { + for (i = 0; i < synth->nbuf; i++) { + if (synth->left_ubuf[i] != NULL) { + FLUID_FREE(synth->left_ubuf[i]); + } + } + FLUID_FREE(synth->left_ubuf); + } + + if (synth->right_ubuf != NULL) { + for (i = 0; i < synth->nbuf; i++) { + if (synth->right_ubuf[i] != NULL) { + FLUID_FREE(synth->right_ubuf[i]); + } + } + FLUID_FREE(synth->right_ubuf); + } + + if (synth->fx_left_ubuf != NULL) { + for (i = 0; i < 2; i++) { + if (synth->fx_left_ubuf[i] != NULL) { + FLUID_FREE(synth->fx_left_ubuf[i]); + } + } + FLUID_FREE(synth->fx_left_ubuf); + } + + /* release the reverb module */ + if (synth->reverb != NULL) { + delete_fluid_revmodel(synth->reverb); + } + + /* release the chorus module */ + if (synth->chorus != NULL) { + delete_fluid_chorus(synth->chorus); + } + + /* free the tunings, if any */ + if (synth->tuning != NULL) { + for (i = 0; i < 128; i++) { + if (synth->tuning[i] != NULL) { + for (k = 0; k < 128; k++) { + if (synth->tuning[i][k] != NULL) { + FLUID_FREE(synth->tuning[i][k]); + } + } + FLUID_FREE(synth->tuning[i]); + } + } + FLUID_FREE(synth->tuning); + } + +#ifdef LADSPA + /* Release the LADSPA Fx unit */ + fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); + FLUID_FREE(synth->LADSPA_FxUnit); +#endif + + fluid_mutex_destroy(synth->busy); + + FLUID_FREE(synth); + + return FLUID_OK; +} + +/* + * fluid_synth_error + * + * The error messages are not thread-save, yet. They are still stored + * in a global message buffer (see fluid_sys.c). + * */ +char* +fluid_synth_error(fluid_synth_t* synth) +{ + return fluid_error(); +} + +/* + * fluid_synth_noteon + */ +int +fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) +{ + fluid_channel_t* channel; + int r = FLUID_FAILED; + + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + /* notes with velocity zero go to noteoff */ + if (vel == 0) { + return fluid_synth_noteoff(synth, chan, key); + } + + channel = synth->channel[chan]; + + /* make sure this channel has a preset */ + if (channel->preset == NULL) { + if (synth->verbose) { + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s", + chan, key, vel, 0, + (float) synth->ticks / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, 0, "channel has no preset"); + } + return FLUID_FAILED; + } + + /* If there is another voice process on the same channel and key, + advance it to the release phase. */ + fluid_synth_release_voice_on_same_note(synth, chan, key); + + return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel); +} + +/* + * fluid_synth_noteoff + */ +int +fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) +{ + int i; + fluid_voice_t* voice; + int status = FLUID_FAILED; +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (_ON(voice) && (voice->chan == chan) && (voice->key == key)) { + if (synth->verbose) { + int used_voices = 0; + int k; + for (k = 0; k < synth->polyphony; k++) { + if (!_AVAILABLE(synth->voice[k])) { + used_voices++; + } + } + FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", + voice->chan, voice->key, 0, voice->id, + (float) (voice->start_time + voice->ticks) / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + (float) voice->ticks / 44100.0f, + used_voices); + } /* if verbose */ + fluid_voice_noteoff(voice); + status = FLUID_OK; + } /* if voice on */ + } /* for all voices */ + return status; +} + +/* + * fluid_synth_damp_voices + */ +int +fluid_synth_damp_voices(fluid_synth_t* synth, int chan) +{ + int i; + fluid_voice_t* voice; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if ((voice->chan == chan) && _SUSTAINED(voice)) { +/* printf("turned off sustained note: chan=%d, key=%d, vel=%d\n", voice->chan, voice->key, voice->vel); */ + fluid_voice_noteoff(voice); + } + } + + return FLUID_OK; +} + +/* + * fluid_synth_cc + */ +int +fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) +{ +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + if ((num < 0) || (num >= 128)) { + FLUID_LOG(FLUID_WARN, "Ctrl out of range"); + return FLUID_FAILED; + } + if ((val < 0) || (val >= 128)) { + FLUID_LOG(FLUID_WARN, "Value out of range"); + return FLUID_FAILED; + } + + if (synth->verbose) { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + } + + /* set the controller value in the channel */ + fluid_channel_cc(synth->channel[chan], num, val); + + return FLUID_OK; +} + +/* + * fluid_synth_cc + */ +int +fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) +{ + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + if ((num < 0) || (num >= 128)) { + FLUID_LOG(FLUID_WARN, "Ctrl out of range"); + return FLUID_FAILED; + } + + *pval = synth->channel[chan]->cc[num]; + return FLUID_OK; +} + +/* + * fluid_synth_all_notes_off + * + * put all notes on this channel into released state. + */ +int +fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) +{ + int i; + fluid_voice_t* voice; + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (_PLAYING(voice) && (voice->chan == chan)) { + fluid_voice_noteoff(voice); + } + } + return FLUID_OK; +} + +/* + * fluid_synth_all_sounds_off + * + * immediately stop all notes on this channel. + */ +int +fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) +{ + int i; + fluid_voice_t* voice; + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (_PLAYING(voice) && (voice->chan == chan)) { + fluid_voice_off(voice); + } + } + return FLUID_OK; +} + +/* + * fluid_synth_system_reset + * + * Purpose: + * Respond to the MIDI command 'system reset' (0xFF, big red 'panic' button) + */ +int +fluid_synth_system_reset(fluid_synth_t* synth) +{ + int i; + fluid_voice_t* voice; + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (_PLAYING(voice)) { + fluid_voice_off(voice); + } + } + + for (i = 0; i < synth->midi_channels; i++) { + fluid_channel_reset(synth->channel[i]); + } + + fluid_chorus_reset(synth->chorus); + fluid_revmodel_reset(synth->reverb); + + return FLUID_OK; +} + +/* + * fluid_synth_modulate_voices + * + * tell all synthesis processes on this channel to update their + * synthesis parameters after a control change. + */ +int +fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl) +{ + int i; + fluid_voice_t* voice; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) { + fluid_voice_modulate(voice, is_cc, ctrl); + } + } + return FLUID_OK; +} + +/* + * fluid_synth_modulate_voices_all + * + * Tell all synthesis processes on this channel to update their + * synthesis parameters after an all control off message (i.e. all + * controller have been reset to their default value). + */ +int +fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan) +{ + int i; + fluid_voice_t* voice; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) { + fluid_voice_modulate_all(voice); + } + } + return FLUID_OK; +} + +/* + * fluid_synth_pitch_bend + */ +int +fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) +{ + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if (synth->verbose) { + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + } + + /* set the pitch-bend value in the channel */ + fluid_channel_pitch_bend(synth->channel[chan], val); + + return FLUID_OK; +} + +/* + * fluid_synth_pitch_bend + */ +int +fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) +{ + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + *ppitch_bend = synth->channel[chan]->pitch_bend; + return FLUID_OK; +} + +/* + * Fluid_synth_pitch_wheel_sens + */ +int +fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) +{ + + /* check the ranges of the arguments */ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if (synth->verbose) { + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + } + + /* set the pitch-bend value in the channel */ + fluid_channel_pitch_wheel_sens(synth->channel[chan], val); + + return FLUID_OK; +} + +/* + * fluid_synth_get_pitch_wheel_sens + * + * Note : this function was added after version 1.0 API freeze. + * So its API is not in the synth.h file. It should be added in some later + * version of fluidsynth. Maybe v2.0 ? -- Antoine Schmitt May 2003 + */ + +int +fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) +{ + + // check the ranges of the arguments + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + // get the pitch-bend value in the channel + *pval = synth->channel[chan]->pitch_wheel_sensitivity; + + return FLUID_OK; +} + +/* + * fluid_synth_get_preset + */ +fluid_preset_t* +fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, + unsigned int banknum, unsigned int prognum) +{ + fluid_preset_t* preset = NULL; + fluid_sfont_t* sfont = NULL; + fluid_list_t* list = synth->sfont; + int offset; + + sfont = fluid_synth_get_sfont_by_id(synth, sfontnum); + + if (sfont != NULL) { + offset = fluid_synth_get_bank_offset(synth, sfontnum); + preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); + if (preset != NULL) { + return preset; + } + } + return NULL; +} + +/* + * fluid_synth_get_preset2 + */ +fluid_preset_t* +fluid_synth_get_preset2(fluid_synth_t* synth, char* sfont_name, + unsigned int banknum, unsigned int prognum) +{ + fluid_preset_t* preset = NULL; + fluid_sfont_t* sfont = NULL; + int offset; + + sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); + + if (sfont != NULL) { + offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); + preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); + if (preset != NULL) { + return preset; + } + } + return NULL; +} + +fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, + unsigned int banknum, + unsigned int prognum) +{ + fluid_preset_t* preset = NULL; + fluid_sfont_t* sfont = NULL; + fluid_list_t* list = synth->sfont; + int offset; + + while (list) { + + sfont = (fluid_sfont_t*) fluid_list_get(list); + offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); + preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); + + if (preset != NULL) { + preset->sfont = sfont; /* FIXME */ + return preset; + } + + list = fluid_list_next(list); + + } + return NULL; +} + + +/* + * fluid_synth_program_change + */ +int +fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) +{ + fluid_preset_t* preset = NULL; + fluid_channel_t* channel; + unsigned int banknum; + unsigned int sfont_id; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + if ((prognum >= 0) && (prognum < FLUID_NUM_PROGRAMS) && + (chan >= 0) && (chan < synth->midi_channels)) { + + channel = synth->channel[chan]; + banknum = fluid_channel_get_banknum(channel); + + /* inform the channel of the new program number */ + fluid_channel_set_prognum(channel, prognum); + + if (synth->verbose) { + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + } + + /* special handling of channel 10 (or 9 counting from 0). channel + 10 is the percussion channel. */ + if (channel->channum == 9) { + + /* try to search the drum instrument first */ + preset = fluid_synth_find_preset(synth, banknum | DRUM_INST_MASK, prognum); + + /* if that fails try to search the melodic instrument */ + if (preset == NULL) { + preset = fluid_synth_find_preset(synth, banknum, prognum); + } + + } else { + preset = fluid_synth_find_preset(synth, banknum, prognum); + } + + sfont_id = preset? fluid_sfont_get_id(preset->sfont) : 0; + fluid_channel_set_sfontnum(channel, sfont_id); + fluid_channel_set_preset(channel, preset); + + return FLUID_OK; + } + + FLUID_LOG(FLUID_ERR, "Index out of range (chan=%d, prog=%d)", chan, prognum); + return FLUID_FAILED; +} + +/* + * fluid_synth_bank_select + */ +int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) +{ + if ((chan >= 0) && (chan < synth->midi_channels)) { + fluid_channel_set_banknum(synth->channel[chan], bank); + return FLUID_OK; + } + return FLUID_FAILED; +} + + +/* + * fluid_synth_sfont_select + */ +int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) +{ + if ((chan >= 0) && (chan < synth->midi_channels)) { + fluid_channel_set_sfontnum(synth->channel[chan], sfont_id); + return FLUID_OK; + } + return FLUID_FAILED; +} + +/* + * fluid_synth_get_program + */ +int +fluid_synth_get_program(fluid_synth_t* synth, int chan, + unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) +{ + fluid_channel_t* channel; + if ((chan >= 0) && (chan < synth->midi_channels)) { + channel = synth->channel[chan]; + *sfont_id = fluid_channel_get_sfontnum(channel); + *bank_num = fluid_channel_get_banknum(channel); + *preset_num = fluid_channel_get_prognum(channel); + return FLUID_OK; + } + return FLUID_FAILED; +} + +/* + * fluid_synth_program_select + */ +int fluid_synth_program_select(fluid_synth_t* synth, + int chan, + unsigned int sfont_id, + unsigned int bank_num, + unsigned int preset_num) +{ + fluid_preset_t* preset = NULL; + fluid_channel_t* channel; + + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); + return FLUID_FAILED; + } + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + return FLUID_FAILED; + } + + /* inform the channel of the new bank and program number */ + fluid_channel_set_sfontnum(channel, sfont_id); + fluid_channel_set_banknum(channel, bank_num); + fluid_channel_set_prognum(channel, preset_num); + + fluid_channel_set_preset(channel, preset); + + return FLUID_OK; +} + +/* + * fluid_synth_program_select2 + */ +int fluid_synth_program_select2(fluid_synth_t* synth, + int chan, + char* sfont_name, + unsigned int bank_num, + unsigned int preset_num) +{ + fluid_preset_t* preset = NULL; + fluid_channel_t* channel; + fluid_sfont_t* sfont = NULL; + int offset; + + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); + return FLUID_FAILED; + } + channel = synth->channel[chan]; + + sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); + if (sfont == NULL) { + FLUID_LOG(FLUID_ERR, "Could not find SoundFont %s", sfont_name); + return FLUID_FAILED; + } + + offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); + preset = fluid_sfont_get_preset(sfont, bank_num - offset, preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %s", + bank_num, preset_num, sfont_name); + return FLUID_FAILED; + } + + /* inform the channel of the new bank and program number */ + fluid_channel_set_sfontnum(channel, fluid_sfont_get_id(sfont)); + fluid_channel_set_banknum(channel, bank_num); + fluid_channel_set_prognum(channel, preset_num); + + fluid_channel_set_preset(channel, preset); + + return FLUID_OK; +} + +/* + * fluid_synth_update_presets + */ +void fluid_synth_update_presets(fluid_synth_t* synth) +{ + int chan; + fluid_channel_t* channel; + + for (chan = 0; chan < synth->midi_channels; chan++) { + channel = synth->channel[chan]; + fluid_channel_set_preset(channel, + fluid_synth_get_preset(synth, + fluid_channel_get_sfontnum(channel), + fluid_channel_get_banknum(channel), + fluid_channel_get_prognum(channel))); + } +} + + +/* + * fluid_synth_update_gain + */ +int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) +{ + fluid_synth_set_gain(synth, (float) value); + return 0; +} + +/* + * fluid_synth_set_gain + */ +void fluid_synth_set_gain(fluid_synth_t* synth, float gain) +{ + int i; + + fluid_clip(gain, 0.0f, 10.0f); + synth->gain = gain; + + for (i = 0; i < synth->polyphony; i++) { + fluid_voice_t* voice = synth->voice[i]; + if (_PLAYING(voice)) { + fluid_voice_set_gain(voice, gain); + } + } +} + +/* + * fluid_synth_get_gain + */ +float fluid_synth_get_gain(fluid_synth_t* synth) +{ + return synth->gain; +} + +/* + * fluid_synth_update_polyphony + */ +int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) +{ + fluid_synth_set_polyphony(synth, value); + return 0; +} + +/* + * fluid_synth_set_polyphony + */ +int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) +{ + int i; + + if (polyphony < 1 || polyphony > synth->nvoice) { + return FLUID_FAILED; + } + + /* turn off any voices above the new limit */ + for (i = polyphony; i < synth->nvoice; i++) { + fluid_voice_t* voice = synth->voice[i]; + if (_PLAYING(voice)) { + fluid_voice_off(voice); + } + } + + synth->polyphony = polyphony; + + return FLUID_OK; +} + +/* + * fluid_synth_get_polyphony + */ +int fluid_synth_get_polyphony(fluid_synth_t* synth) +{ + return synth->polyphony; +} + +/* + * fluid_synth_get_internal_buffer_size + */ +int fluid_synth_get_internal_bufsize(fluid_synth_t* synth) +{ + return FLUID_BUFSIZE; +} + +/* + * fluid_synth_program_reset + * + * Resend a bank select and a program change for every channel. This + * function is called mainly after a SoundFont has been loaded, + * unloaded or reloaded. */ +int +fluid_synth_program_reset(fluid_synth_t* synth) +{ + int i; + /* try to set the correct presets */ + for (i = 0; i < synth->midi_channels; i++){ + fluid_synth_program_change(synth, i, fluid_channel_get_prognum(synth->channel[i])); + } + return FLUID_OK; +} + +/* + * fluid_synth_set_reverb_preset + */ +int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) +{ + int i = 0; + while (revmodel_preset[i].name != NULL) { + if (i == num) { + fluid_revmodel_setroomsize(synth->reverb, revmodel_preset[i].roomsize); + fluid_revmodel_setdamp(synth->reverb, revmodel_preset[i].damp); + fluid_revmodel_setwidth(synth->reverb, revmodel_preset[i].width); + fluid_revmodel_setlevel(synth->reverb, revmodel_preset[i].level); + return FLUID_OK; + } + i++; + } + return FLUID_FAILED; +} + +/* + * fluid_synth_set_reverb + */ +void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, + double width, double level) +{ +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + fluid_revmodel_setroomsize(synth->reverb, roomsize); + fluid_revmodel_setdamp(synth->reverb, damping); + fluid_revmodel_setwidth(synth->reverb, width); + fluid_revmodel_setlevel(synth->reverb, level); +} + +/* + * fluid_synth_set_chorus + */ +void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, + double speed, double depth_ms, int type) +{ +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + fluid_chorus_set_nr(synth->chorus, nr); + fluid_chorus_set_level(synth->chorus, (fluid_real_t)level); + fluid_chorus_set_speed_Hz(synth->chorus, (fluid_real_t)speed); + fluid_chorus_set_depth_ms(synth->chorus, (fluid_real_t)depth_ms); + fluid_chorus_set_type(synth->chorus, type); + fluid_chorus_update(synth->chorus); +} + +/****************************************************** + +#define COMPRESS 1 +#define COMPRESS_X1 4.0 +#define COMPRESS_Y1 0.6 +#define COMPRESS_X2 10.0 +#define COMPRESS_Y2 1.0 + + len2 = 2 * len; + alpha1 = COMPRESS_Y1 / COMPRESS_X1; + alpha2 = (COMPRESS_Y2 - COMPRESS_Y1) / (COMPRESS_X2 - COMPRESS_X1); + if (COMPRESS_X1 == COMPRESS_Y1) { + for (j = 0; j < len2; j++) { + if (buf[j] > COMPRESS_X1) { + if (buf[j] > COMPRESS_X2) { + buf[j] = COMPRESS_Y2; + } else { + buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); + } + } else if (buf[j] < -COMPRESS_X1) { + if (buf[j] < -COMPRESS_X2) { + buf[j] = -COMPRESS_Y2; + } else { + buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); + } + } + } + } else { + for (j = 0; j < len2; j++) { + if ((buf[j] >= -COMPRESS_X1) && (buf[j] <= COMPRESS_X1)) { + buf[j] *= alpha1; + } else if (buf[j] > COMPRESS_X1) { + if (buf[j] > COMPRESS_X2) { + buf[j] = COMPRESS_Y2; + } else { + buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); + } + } else { + if (buf[j] < -COMPRESS_X2) { + buf[j] = -COMPRESS_Y2; + } else { + buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); + } + } + } + } + +***************************************************/ + +/* + * fluid_synth_nwrite_float + */ +int +fluid_synth_nwrite_float(fluid_synth_t* synth, int len, + float** left, float** right, + float** fx_left, float** fx_right) +{ + fluid_real_t** left_in = synth->left_buf; + fluid_real_t** right_in = synth->right_buf; + fluid_real_t** fx_left_in = synth->fx_left_buf; + fluid_real_t** fx_right_in = synth->fx_right_buf; + double time = fluid_utime(); + int i, num, available, count, bytes; + + /* make sure we're playing */ + if (synth->state != FLUID_SYNTH_PLAYING) { + return 0; + } + + /* First, take what's still available in the buffer */ + count = 0; + num = synth->cur; + if (synth->cur < FLUID_BUFSIZE) { + available = FLUID_BUFSIZE - synth->cur; + + num = (available > len)? len : available; + bytes = num * sizeof(float); + + for (i = 0; i < synth->audio_channels; i++) { + FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes); + FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes); + } + for (i = 0; i < synth->effects_channels; i++) { + FLUID_MEMCPY(fx_left[i], fx_left_in[i] + synth->cur, bytes); + FLUID_MEMCPY(fx_right[i], fx_right_in[i] + synth->cur, bytes); + } + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, run one_block() and copy till we have 'len' samples */ + while (count < len) { + fluid_synth_one_block(synth, 1); + + num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE; + bytes = num * sizeof(float); + + for (i = 0; i < synth->audio_channels; i++) { + FLUID_MEMCPY(left[i] + count, left_in[i], bytes); + FLUID_MEMCPY(right[i] + count, right_in[i], bytes); + } + for (i = 0; i < synth->effects_channels; i++) { + FLUID_MEMCPY(fx_left[i] + count, fx_left_in[i], bytes); + FLUID_MEMCPY(fx_right[i] + count, fx_right_in[i], bytes); + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + synth->cpu_load = 0.5 * (synth->cpu_load + + time * synth->sample_rate / len / 10000.0); + +/* printf("CPU: %.2f\n", synth->cpu_load); */ + + return 0; +} + +int fluid_synth_process(fluid_synth_t* synth, int len, + int nin, float** in, + int nout, float** out) +{ + return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1); +} + + +/* + * fluid_synth_write_float + */ +int +fluid_synth_write_float(fluid_synth_t* synth, int len, + void* lout, int loff, int lincr, + void* rout, int roff, int rincr) +{ + int i, j, k, l; + float* left_out = (float*) lout; + float* right_out = (float*) rout; + fluid_real_t* left_in = synth->left_buf[0]; + fluid_real_t* right_in = synth->right_buf[0]; + double time = fluid_utime(); + + /* make sure we're playing */ + if (synth->state != FLUID_SYNTH_PLAYING) { + return 0; + } + + l = synth->cur; + + for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) { + /* fill up the buffers as needed */ + if (l == FLUID_BUFSIZE) { + fluid_synth_one_block(synth, 0); + l = 0; + } + + left_out[j] = (float) left_in[l]; + right_out[k] = (float) right_in[l]; + } + + synth->cur = l; + + time = fluid_utime() - time; + synth->cpu_load = 0.5 * (synth->cpu_load + + time * synth->sample_rate / len / 10000.0); + +/* printf("CPU: %.2f\n", synth->cpu_load); */ + + return 0; +} + +/* + * fluid_synth_write_s16 + */ +int +fluid_synth_write_s16(fluid_synth_t* synth, int len, + void* lout, int loff, int lincr, + void* rout, int roff, int rincr) +{ + int i, j, k, cur; + signed short* left_out = (signed short*) lout; + signed short* right_out = (signed short*) rout; + fluid_real_t* left_in = synth->left_buf[0]; + fluid_real_t* right_in = synth->right_buf[0]; + double prof_ref = fluid_profile_ref(); + fluid_real_t left_sample; + fluid_real_t right_sample; + double time = fluid_utime(); + + /* make sure we're playing */ + if (synth->state != FLUID_SYNTH_PLAYING) { + return 0; + } + + cur = synth->cur; + + for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) { + + /* fill up the buffers as needed */ + if (cur == FLUID_BUFSIZE) { + + double prof_ref_on_block = fluid_profile_ref(); + + fluid_synth_one_block(synth, 0); + cur = 0; + + fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block); + } + + left_sample=left_in[cur]* 32767.0f; + right_sample=right_in[cur] * 32767.0f; + + /* digital clipping */ + if (left_sample > 32767.0f) { + left_sample = 32767; + } + if (left_sample < -32768.0f) { + left_sample = -32768; + } + if (right_sample > 32767.0f) { + right_sample = 32767; + } + if (right_sample < -32768.0f) { + right_sample = -32768; + } + left_out[j] = (signed short) left_sample; + right_out[k] = (signed short) right_sample; + } + + synth->cur = cur; + + fluid_profile(FLUID_PROF_WRITE_S16, prof_ref); + + + time = fluid_utime() - time; + synth->cpu_load = 0.5 * (synth->cpu_load + + time * synth->sample_rate / len / 10000.0); + +/* printf("CPU: %.2f\n", synth->cpu_load); */ + + return 0; +} + + +/* + * fluid_synth_one_block + */ +int +fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) +{ + int i, auchan; + fluid_voice_t* voice; + fluid_real_t* left_buf; + fluid_real_t* right_buf; + fluid_real_t* reverb_buf; + fluid_real_t* chorus_buf; + int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); + double prof_ref = fluid_profile_ref(); + +/* fluid_mutex_lock(synth->busy); /\* Here comes the audio thread. Lock the synth. *\/ */ + + fluid_check_fpe("??? Just starting up ???"); + + /* clean the audio buffers */ + for (i = 0; i < synth->nbuf; i++) { + FLUID_MEMSET(synth->left_buf[i], 0, byte_size); + FLUID_MEMSET(synth->right_buf[i], 0, byte_size); + } + for (i = 0; i < synth->effects_channels; i++) { + FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size); + FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size); + } + + /* Set up the reverb / chorus buffers only, when the effect is + * enabled on synth level. Nonexisting buffers are detected in the + * DSP loop. Not sending the reverb / chorus signal saves some time + * in that case. */ + if (synth->with_reverb) { + reverb_buf = synth->fx_left_buf[0]; + } else { + reverb_buf = NULL; + } + + if (synth->with_chorus) { + chorus_buf = synth->fx_left_buf[1]; + } else { + chorus_buf = NULL; + } + + + fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref); + + /* call all playing synthesis processes */ + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + + if (_PLAYING(voice)) { + double prof_ref_voice = fluid_profile_ref(); + + /* The output associated with a MIDI channel is wrapped around + * using the number of audio groups as modulo divider. This is + * typically the number of output channels on the 'sound card', + * as long as the LADSPA Fx unit is not used. In case of LADSPA + * unit, think of it as subgroups on a mixer. + * + * For example: Assume that the number of groups is set to 2. + * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2, + * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI + * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to + * output 2, 3, 6, 9, 12 etc to output 3. + */ + auchan = fluid_channel_get_num(fluid_voice_get_channel(voice)); + auchan %= synth->audio_groups; + left_buf = synth->left_buf[auchan]; + right_buf = synth->right_buf[auchan]; + + fluid_voice_write(voice, left_buf, right_buf, reverb_buf, chorus_buf); + + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice); + } + } + fluid_check_fpe("Synthesis processes"); + + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref); + + /* if multi channel output, don't mix the output of the chorus and + reverb in the final output. The effects outputs are send + separately. */ + + if (do_not_mix_fx_to_out) { + + /* send to reverb */ + if (reverb_buf) { + fluid_revmodel_processreplace(synth->reverb, reverb_buf, + synth->fx_left_buf[0], synth->fx_right_buf[0]); + fluid_check_fpe("Reverb"); + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref); + + /* send to chorus */ + if (chorus_buf) { + fluid_chorus_processreplace(synth->chorus, chorus_buf, + synth->fx_left_buf[1], synth->fx_right_buf[1]); + fluid_check_fpe("Chorus"); + } + + } else { + + /* send to reverb */ + if (reverb_buf) { + fluid_revmodel_processmix(synth->reverb, reverb_buf, + synth->left_buf[0], synth->right_buf[0]); + fluid_check_fpe("Reverb"); + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref); + + /* send to chorus */ + if (chorus_buf) { + fluid_chorus_processmix(synth->chorus, chorus_buf, + synth->left_buf[0], synth->right_buf[0]); + fluid_check_fpe("Chorus"); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref); + +#ifdef LADSPA + /* Run the signal through the LADSPA Fx unit */ + fluid_LADSPA_run(synth->LADSPA_FxUnit, synth->left_buf, synth->right_buf, synth->fx_left_buf, synth->fx_right_buf); + fluid_check_fpe("LADSPA"); +#endif + + synth->ticks += FLUID_BUFSIZE; + + /* Testcase, that provokes a denormal floating point error */ +#if 0 + {float num=1;while (num != 0){num*=0.5;};}; +#endif + fluid_check_fpe("??? Remainder of synth_one_block ???"); + +/* fluid_mutex_unlock(synth->busy); /\* Allow other threads to touch the synth *\/ */ + + return 0; +} + + +/* + * fluid_synth_free_voice_by_kill + * + * selects a voice for killing. the selection algorithm is a refinement + * of the algorithm previously in fluid_synth_alloc_voice. + */ +fluid_voice_t* +fluid_synth_free_voice_by_kill(fluid_synth_t* synth) +{ + int i; + fluid_real_t best_prio = 999999.; + fluid_real_t this_voice_prio; + fluid_voice_t* voice; + int best_voice_index=-1; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + + voice = synth->voice[i]; + + /* safeguard against an available voice. */ + if (_AVAILABLE(voice)) { + return voice; + } + + /* Determine, how 'important' a voice is. + * Start with an arbitrary number */ + this_voice_prio = 10000.; + + /* Is this voice on the drum channel? + * Then it is very important. + * Also, forget about the released-note condition: + * Typically, drum notes are triggered only very briefly, they run most + * of the time in release phase. + */ + if (voice->chan == 9){ + this_voice_prio += 4000; + + } else if (_RELEASED(voice)){ + /* The key for this voice has been released. Consider it much less important + * than a voice, which is still held. + */ + this_voice_prio -= 2000.; + } + + if (_SUSTAINED(voice)){ + /* The sustain pedal is held down on this channel. + * Consider it less important than non-sustained channels. + * This decision is somehow subjective. But usually the sustain pedal + * is used to play 'more-voices-than-fingers', so it shouldn't hurt + * if we kill one voice. + */ + this_voice_prio -= 1000; + } + + /* We are not enthusiastic about releasing voices, which have just been started. + * Otherwise hitting a chord may result in killing notes belonging to that very same + * chord. + * So subtract the age of the voice from the priority - an older voice is just a little + * bit less important than a younger voice. + * This is a number between roughly 0 and 100.*/ + this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice)); + + /* take a rough estimate of loudness into account. Louder voices are more important. */ + if (voice->volenv_section != FLUID_VOICE_ENVATTACK){ + this_voice_prio += voice->volenv_val * 1000.; + } + + /* check if this voice has less priority than the previous candidate. */ + if (this_voice_prio < best_prio) + best_voice_index = i, + best_prio = this_voice_prio; + } + + if (best_voice_index < 0) { + return NULL; + } + + voice = synth->voice[best_voice_index]; + fluid_voice_off(voice); + + return voice; +} + +/* + * fluid_synth_alloc_voice + */ +fluid_voice_t* +fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) +{ + int i, k; + fluid_voice_t* voice = NULL; + fluid_channel_t* channel = NULL; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + /* check if there's an available synthesis process */ + for (i = 0; i < synth->polyphony; i++) { + if (_AVAILABLE(synth->voice[i])) { + voice = synth->voice[i]; + break; + } + } + + /* No success yet? Then stop a running voice. */ + if (voice == NULL) { + voice = fluid_synth_free_voice_by_kill(synth); + } + + if (voice == NULL) { + FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); + return NULL; + } + + if (synth->verbose) { + k = 0; + for (i = 0; i < synth->polyphony; i++) { + if (!_AVAILABLE(synth->voice[i])) { + k++; + } + } + + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", + chan, key, vel, synth->storeid, + (float) synth->ticks / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, + k); + } + + if (chan >= 0) { + channel = synth->channel[chan]; + } + + if (fluid_voice_init(voice, sample, channel, key, vel, + synth->storeid, synth->ticks, synth->gain) != FLUID_OK) { + FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); + return NULL; + } + + /* add the default modulators to the synthesis process. */ + fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */ + fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */ + fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */ + fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */ + fluid_voice_add_mod(voice, &default_att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.5 */ + fluid_voice_add_mod(voice, &default_pan_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.6 */ + fluid_voice_add_mod(voice, &default_expr_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.7 */ + fluid_voice_add_mod(voice, &default_reverb_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.8 */ + fluid_voice_add_mod(voice, &default_chorus_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.9 */ + fluid_voice_add_mod(voice, &default_pitch_bend_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.10 */ + + return voice; +} + +/* + * fluid_synth_kill_by_exclusive_class + */ +void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* new_voice) +{ + /** Kill all voices on a given channel, which belong into + excl_class. This function is called by a SoundFont's preset in + response to a noteon event. If one noteon event results in + several voice processes (stereo samples), ignore_ID must name + the voice ID of the first generated voice (so that it is not + stopped). The first voice uses ignore_ID=-1, which will + terminate all voices on a channel belonging into the exclusive + class excl_class. + */ + + int i; + int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS); + + /* Check if the voice belongs to an exclusive class. In that case, + previous notes from the same class are released. */ + + /* Excl. class 0: No exclusive class */ + if (excl_class == 0) { + return; + } + + // FLUID_LOG(FLUID_INFO, "Voice belongs to exclusive class (class=%d, ignore_id=%d)", excl_class, ignore_ID); + + /* Kill all notes on the same channel with the same exclusive class */ + + for (i = 0; i < synth->polyphony; i++) { + fluid_voice_t* existing_voice = synth->voice[i]; + + /* Existing voice does not play? Leave it alone. */ + if (!_PLAYING(existing_voice)) { + continue; + } + + /* An exclusive class is valid for a whole channel (or preset). + * Is the voice on a different channel? Leave it alone. */ + if (existing_voice->chan != new_voice->chan) { + continue; + } + + /* Existing voice has a different (or no) exclusive class? Leave it alone. */ + if ((int)_GEN(existing_voice, GEN_EXCLUSIVECLASS) != excl_class) { + continue; + } + + /* Existing voice is a voice process belonging to this noteon + * event (for example: stereo sample)? Leave it alone. */ + if (fluid_voice_get_id(existing_voice) == fluid_voice_get_id(new_voice)) { + continue; + } + + // FLUID_LOG(FLUID_INFO, "Releasing previous voice of exclusive class (class=%d, id=%d)", + // (int)_GEN(existing_voice, GEN_EXCLUSIVECLASS), (int)fluid_voice_get_id(existing_voice)); + + fluid_voice_kill_excl(existing_voice); + }; +}; + +/* + * fluid_synth_start_voice + */ +void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) +{ +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + /* Find the exclusive class of this voice. If set, kill all voices + * that match the exclusive class and are younger than the first + * voice process created by this noteon event. */ + fluid_synth_kill_by_exclusive_class(synth, voice); + + /* Start the new voice */ + + fluid_voice_start(voice); +} + +/* + * fluid_synth_add_sfloader + */ +void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) +{ + synth->loaders = fluid_list_prepend(synth->loaders, loader); +} + + +/* + * fluid_synth_sfload + */ +int +fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets) +{ + fluid_sfont_t* sfont; + fluid_list_t* list; + fluid_sfloader_t* loader; + +#if defined(MACOS9) + fluid_synth_sfunload_macos9(synth); +#endif + + if (filename == NULL) { + FLUID_LOG(FLUID_ERR, "Invalid filename"); + return FLUID_FAILED; + } + + for (list = synth->loaders; list; list = fluid_list_next(list)) { + loader = (fluid_sfloader_t*) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if (sfont != NULL) { + + sfont->id = ++synth->sfont_id; + + /* insert the sfont as the first one on the list */ + synth->sfont = fluid_list_prepend(synth->sfont, sfont); + + /* reset the presets for all channels */ + if (reset_presets) { + fluid_synth_program_reset(synth); + } + + return (int) sfont->id; + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + return -1; +} + +/* + * fluid_synth_sfunload_callback + */ +static int fluid_synth_sfunload_callback(void* data, unsigned int msec) +{ + fluid_sfont_t* sfont = (fluid_sfont_t*) data; + int r = delete_fluid_sfont(sfont); + if (r == 0) { + FLUID_LOG(FLUID_DBG,"Unloaded SoundFont"); + } + return r != 0; +} + +/* + * fluid_synth_sfunload_macos9 + */ +void fluid_synth_sfunload_macos9(fluid_synth_t* synth) +{ +#if defined(MACOS9) + fluid_list_t *list, *next; + fluid_sfont_t* sfont; + + list = synth->unloading; + while (list) { + next = fluid_list_next(list); + sfont = (fluid_sfont_t*) fluid_list_get(list); + if (delete_fluid_sfont(sfont) == 0) { + synth->unloading = fluid_list_remove(synth->unloading, sfont); + } + list = next; + } +#endif +} + +/* + * fluid_synth_sfunload + */ +int +fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) +{ + fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, id); + +#if defined(MACOS9) + fluid_synth_sfunload_macos9(synth); +#endif + + if (!sfont) { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + return FLUID_FAILED; + } + + /* remove the SoundFont from the list */ + synth->sfont = fluid_list_remove(synth->sfont, sfont); + + /* reset the presets for all channels */ + if (reset_presets) { + fluid_synth_program_reset(synth); + } else { + fluid_synth_update_presets(synth); + } + + if (delete_fluid_sfont(sfont) != 0) { +#if defined(MACOS9) + synth->unloading = fluid_list_prepend(synth->unloading, sfont); +#else + /* spin off a timer thread to unload the sfont later */ + new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, 1, 1); +#endif + } + + return FLUID_OK; +} + +/* fluid_synth_sfreload + * + */ +int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) +{ + char filename[1024]; + fluid_sfont_t* sfont; + int index = 0; + fluid_list_t *list; + fluid_sfloader_t* loader; + + + sfont = fluid_synth_get_sfont_by_id(synth, id); + if (!sfont) { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + return FLUID_FAILED; + } + + /* find the index of the SoundFont */ + list = synth->sfont; + while (list) { + if (sfont == (fluid_sfont_t*) fluid_list_get(list)) { + break; + } + list = fluid_list_next(list); + index++; + } + + /* keep a copy of the SoundFont's filename */ + FLUID_STRCPY(filename, fluid_sfont_get_name(sfont)); + + if (fluid_synth_sfunload(synth, id, 0) != FLUID_OK) { + return FLUID_FAILED; + } + + for (list = synth->loaders; list; list = fluid_list_next(list)) { + loader = (fluid_sfloader_t*) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if (sfont != NULL) { + + sfont->id = id; + + /* insert the sfont at the same index */ + synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); + + /* reset the presets for all channels */ + fluid_synth_update_presets(synth); + + return sfont->id; + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + return -1; +} + + +/* + * fluid_synth_add_sfont + */ +int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +{ + sfont->id = ++synth->sfont_id; + + /* insert the sfont as the first one on the list */ + synth->sfont = fluid_list_prepend(synth->sfont, sfont); + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + + return sfont->id; +} + + +/* + * fluid_synth_remove_sfont + */ +void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +{ + int sfont_id = fluid_sfont_get_id(sfont); + + synth->sfont = fluid_list_remove(synth->sfont, sfont); + + /* remove a possible bank offset */ + fluid_synth_remove_bank_offset(synth, sfont_id); + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); +} + + +/* fluid_synth_sfcount + * + * Returns the number of loaded SoundFonts + */ +int +fluid_synth_sfcount(fluid_synth_t* synth) +{ + return fluid_list_size(synth->sfont); +} + +/* fluid_synth_get_sfont + * + * Returns SoundFont num + */ +fluid_sfont_t* +fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) +{ + return (fluid_sfont_t*) fluid_list_get(fluid_list_nth(synth->sfont, num)); +} + +/* fluid_synth_get_sfont_by_id + * + */ +fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) +{ + fluid_list_t* list = synth->sfont; + fluid_sfont_t* sfont; + + while (list) { + sfont = (fluid_sfont_t*) fluid_list_get(list); + if (fluid_sfont_get_id(sfont) == id) { + return sfont; + } + list = fluid_list_next(list); + } + return NULL; +} + +/* fluid_synth_get_sfont_by_name + * + */ +fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) +{ + fluid_list_t* list = synth->sfont; + fluid_sfont_t* sfont; + + while (list) { + sfont = (fluid_sfont_t*) fluid_list_get(list); + if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) { + return sfont; + } + list = fluid_list_next(list); + } + return NULL; +} + +/* + * fluid_synth_get_channel_preset + */ +fluid_preset_t* +fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan) +{ + if ((chan >= 0) && (chan < synth->midi_channels)) { + return fluid_channel_get_preset(synth->channel[chan]); + } + + return NULL; +} + +/* + * fluid_synth_get_voicelist + */ +void +fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int ID) +{ + int i; + int count = 0; + for (i = 0; i < synth->polyphony; i++) { + fluid_voice_t* voice = synth->voice[i]; + if (count >= bufsize) { + return; + } + + if (_PLAYING(voice) && ((int)voice->id == ID || ID < 0)) { + buf[count++] = voice; + } + } + if (count >= bufsize) { + return; + } + buf[count++] = NULL; +} + +/* Purpose: + * Turns on / off the reverb unit in the synth */ +void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) +{ + synth->with_reverb = on; +} + +/* Purpose: + * Turns on / off the chorus unit in the synth */ +void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) +{ + synth->with_chorus = on; +} + +/* Purpose: + * Reports the current setting of the chorus unit. */ +int fluid_synth_get_chorus_nr(fluid_synth_t* synth) +{ + return fluid_chorus_get_nr(synth->chorus); +} + +double fluid_synth_get_chorus_level(fluid_synth_t* synth) +{ + return (double)fluid_chorus_get_level(synth->chorus); +} + +double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) +{ + return (double)fluid_chorus_get_speed_Hz(synth->chorus); +} + +double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) +{ + return (double)fluid_chorus_get_depth_ms(synth->chorus); +} + +int fluid_synth_get_chorus_type(fluid_synth_t* synth) +{ + return fluid_chorus_get_type(synth->chorus); +} + +/* Purpose: + * Returns the current settings_old of the reverb unit */ +double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) +{ + return (double)fluid_revmodel_getroomsize(synth->reverb); +} + +double fluid_synth_get_reverb_damp(fluid_synth_t* synth) +{ + return (double) fluid_revmodel_getdamp(synth->reverb); +} + +double fluid_synth_get_reverb_level(fluid_synth_t* synth) +{ + return (double) fluid_revmodel_getlevel(synth->reverb); +} + +double fluid_synth_get_reverb_width(fluid_synth_t* synth) +{ + return (double) fluid_revmodel_getwidth(synth->reverb); +} + +/* Purpose: + * + * If the same note is hit twice on the same channel, then the older + * voice process is advanced to the release stage. Using a mechanical + * MIDI controller, the only way this can happen is when the sustain + * pedal is held. In this case the behaviour implemented here is + * natural for many instruments. Note: One noteon event can trigger + * several voice processes, for example a stereo sample. Don't + * release those... + */ +void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key){ + int i; + fluid_voice_t* voice; + +/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ +/* fluid_mutex_unlock(synth->busy); */ + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (_PLAYING(voice) + && (voice->chan == chan) + && (voice->key == key) + && (fluid_voice_get_id(voice) != synth->noteid)) { + fluid_voice_noteoff(voice); + } + } +} + +/* Purpose: + * Sets the interpolation method to use on channel chan. + * If chan is < 0, then set the interpolation method on all channels. + */ +int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method){ + int i; + for (i = 0; i < synth->midi_channels; i++) { + if (synth->channel[i] == NULL){ + FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); + return FLUID_FAILED; + }; + if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan){ + fluid_channel_set_interp_method(synth->channel[i], interp_method); + }; + }; + return FLUID_OK; +}; + +/* Purpose: + * Returns the number of allocated midi channels + */ +int +fluid_synth_count_midi_channels(fluid_synth_t* synth) +{ + return synth->midi_channels; +} + +/* Purpose: + * Returns the number of allocated audio channels + */ +int +fluid_synth_count_audio_channels(fluid_synth_t* synth) +{ + return synth->audio_channels; +} + +/* Purpose: + * Returns the number of allocated audio channels + */ +int +fluid_synth_count_audio_groups(fluid_synth_t* synth) +{ + return synth->audio_groups; +} + +/* Purpose: + * Returns the number of allocated effects channels + */ +int +fluid_synth_count_effects_channels(fluid_synth_t* synth) +{ + return synth->effects_channels; +} + +double fluid_synth_get_cpu_load(fluid_synth_t* synth) +{ + return synth->cpu_load; +} + +static fluid_tuning_t* +fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) +{ + if ((bank < 0) || (bank >= 128)) { + FLUID_LOG(FLUID_WARN, "Bank number out of range"); + return NULL; + } + if ((prog < 0) || (prog >= 128)) { + FLUID_LOG(FLUID_WARN, "Program number out of range"); + return NULL; + } + if ((synth->tuning == NULL) || + (synth->tuning[bank] == NULL) || + (synth->tuning[bank][prog] == NULL)) { + FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog); + return NULL; + } + return synth->tuning[bank][prog]; +} + +static fluid_tuning_t* +fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name) +{ + if ((bank < 0) || (bank >= 128)) { + FLUID_LOG(FLUID_WARN, "Bank number out of range"); + return NULL; + } + if ((prog < 0) || (prog >= 128)) { + FLUID_LOG(FLUID_WARN, "Program number out of range"); + return NULL; + } + if (synth->tuning == NULL) { + synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); + if (synth->tuning == NULL) { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**)); + } + + if (synth->tuning[bank] == NULL) { + synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128); + if (synth->tuning[bank] == NULL) { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*)); + } + + if (synth->tuning[bank][prog] == NULL) { + synth->tuning[bank][prog] = new_fluid_tuning(name, bank, prog); + if (synth->tuning[bank][prog] == NULL) { + return NULL; + } + } + + if ((fluid_tuning_get_name(synth->tuning[bank][prog]) == NULL) + || (FLUID_STRCMP(fluid_tuning_get_name(synth->tuning[bank][prog]), name) != 0)) { + fluid_tuning_set_name(synth->tuning[bank][prog], name); + } + + return synth->tuning[bank][prog]; +} + +int fluid_synth_create_key_tuning(fluid_synth_t* synth, + int bank, int prog, + char* name, double* pitch) +{ + fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); + if (tuning == NULL) { + return FLUID_FAILED; + } + if (pitch) { + fluid_tuning_set_all(tuning, pitch); + } + return FLUID_OK; +} + + +int fluid_synth_create_octave_tuning(fluid_synth_t* synth, + int bank, int prog, + char* name, double* pitch) +{ + fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); + if (tuning == NULL) { + return FLUID_FAILED; + } + fluid_tuning_set_octave(tuning, pitch); + return FLUID_OK; +} + +int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, + int len, int *key, double* pitch, int apply) +{ + fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); + int i; + + if (tuning == NULL) { + return FLUID_FAILED; + } + + for (i = 0; i < len; i++) { + fluid_tuning_set_pitch(tuning, key[i], pitch[i]); + } + + return FLUID_OK; +} + +int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, + int bank, int prog) +{ + fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); + + if (tuning == NULL) { + return FLUID_FAILED; + } + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]); + + return FLUID_OK; +} + +int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) +{ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + fluid_channel_set_tuning(synth->channel[chan], NULL); + + return FLUID_OK; +} + +void fluid_synth_tuning_iteration_start(fluid_synth_t* synth) +{ + synth->cur_tuning = NULL; +} + +int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) +{ + int b = 0, p = 0; + + if (synth->tuning == NULL) { + return 0; + } + + if (synth->cur_tuning != NULL) { + /* get the next program number */ + b = fluid_tuning_get_bank(synth->cur_tuning); + p = 1 + fluid_tuning_get_prog(synth->cur_tuning); + if (p >= 128) { + p = 0; + b++; + } + } + + while (b < 128) { + if (synth->tuning[b] != NULL) { + while (p < 128) { + if (synth->tuning[b][p] != NULL) { + synth->cur_tuning = synth->tuning[b][p]; + *bank = b; + *prog = p; + return 1; + } + p++; + } + } + p = 0; + b++; + } + + return 0; +} + +int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, + char* name, int len, double* pitch) +{ + fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); + + if (tuning == NULL) { + return FLUID_FAILED; + } + + if (name) { + snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning)); + name[len - 1] = 0; /* make sure the string is null terminated */ + } + if (pitch) { + FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); + } + + return FLUID_OK; +} + +fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth) +{ + return synth->settings; +} + +int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str) +{ + return fluid_settings_setstr(synth->settings, name, str); +} + +int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str) +{ + return fluid_settings_getstr(synth->settings, name, str); +} + +int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val) +{ + return fluid_settings_setnum(synth->settings, name, val); +} + +int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val) +{ + return fluid_settings_getnum(synth->settings, name, val); +} + +int fluid_synth_setint(fluid_synth_t* synth, char* name, int val) +{ + return fluid_settings_setint(synth->settings, name, val); +} + +int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val) +{ + return fluid_settings_getint(synth->settings, name, val); +} + +int +fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) +{ + int i; + fluid_voice_t* voice; + + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if ((param < 0) || (param >= GEN_LAST)) { + FLUID_LOG(FLUID_WARN, "Parameter number out of range"); + return FLUID_FAILED; + } + + fluid_channel_set_gen(synth->channel[chan], param, value, 0); + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) { + fluid_voice_set_param(voice, param, value, 0); + } + } + + return FLUID_OK; +} + +/** Change the value of a generator. This function allows to control + all synthesis parameters in real-time. The changes are additive, + i.e. they add up to the existing parameter value. This function is + similar to sending an NRPN message to the synthesizer. The + function accepts a float as the value of the parameter. The + parameter numbers and ranges are described in the SoundFont 2.01 + specification, paragraph 8.1.3, page 48. See also + 'fluid_gen_type'. + + Using the fluid_synth_set_gen2() function, it is possible to set + the absolute value of a generator. This is an extension to the + SoundFont standard. If 'absolute' is non-zero, the value of the + generator specified in the SoundFont is completely ignored and the + generator is fixed to the value passed as argument. To undo this + behavior, you must call fluid_synth_set_gen2 again, with + 'absolute' set to 0 (and possibly 'value' set to zero). + + If 'normalized' is non-zero, the value is supposed to be + normalized between 0 and 1. Before applying the value, it will be + scaled and shifted to the range defined in the SoundFont + specifications. + + */ +int +fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, + float value, int absolute, int normalized) +{ + int i; + fluid_voice_t* voice; + float v; + + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if ((param < 0) || (param >= GEN_LAST)) { + FLUID_LOG(FLUID_WARN, "Parameter number out of range"); + return FLUID_FAILED; + } + + v = (normalized)? fluid_gen_scale(param, value) : value; + + fluid_channel_set_gen(synth->channel[chan], param, v, absolute); + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) { + fluid_voice_set_param(voice, param, v, absolute); + } + } + + return FLUID_OK; +} + +float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) +{ + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return 0.0; + } + + if ((param < 0) || (param >= GEN_LAST)) { + FLUID_LOG(FLUID_WARN, "Parameter number out of range"); + return 0.0; + } + + return fluid_channel_get_gen(synth->channel[chan], param); +} + +/* The synth needs to know the router for the command line handlers (they only + * supply the synth as argument) + */ +void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router){ + synth->midi_router=router; +}; + +/* Purpose: + * Any MIDI event from the MIDI router arrives here and is handed + * to the appropriate function. + */ +int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) +{ + fluid_synth_t* synth = (fluid_synth_t*) data; + int type = fluid_midi_event_get_type(event); + int chan = fluid_midi_event_get_channel(event); + + switch(type) { + case NOTE_ON: + return fluid_synth_noteon(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_velocity(event)); + + case NOTE_OFF: + return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event)); + + case CONTROL_CHANGE: + return fluid_synth_cc(synth, chan, + fluid_midi_event_get_control(event), + fluid_midi_event_get_value(event)); + + case PROGRAM_CHANGE: + return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event)); + + case PITCH_BEND: + return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event)); + + case MIDI_SYSTEM_RESET: + return fluid_synth_system_reset(synth); + } + return FLUID_FAILED; +} + + +int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, + int audio_chan, int midi_chan, int key, int vel) +{ + int r; + + /* check the ranges of the arguments */ + if ((midi_chan < 0) || (midi_chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if ((key < 0) || (key >= 128)) { + FLUID_LOG(FLUID_WARN, "Key out of range"); + return FLUID_FAILED; + } + + if ((vel <= 0) || (vel >= 128)) { + FLUID_LOG(FLUID_WARN, "Velocity out of range"); + return FLUID_FAILED; + } + + fluid_mutex_lock(synth->busy); /* One at a time, please */ + + synth->storeid = id; + r = fluid_preset_noteon(preset, synth, midi_chan, key, vel); + + fluid_mutex_unlock(synth->busy); + + return r; +} + +int fluid_synth_stop(fluid_synth_t* synth, unsigned int id) +{ + int i; + fluid_voice_t* voice; + int status = FLUID_FAILED; + int count = 0; + + for (i = 0; i < synth->polyphony; i++) { + + voice = synth->voice[i]; + + if (_ON(voice) && (fluid_voice_get_id(voice) == id)) { + count++; + fluid_voice_noteoff(voice); + status = FLUID_OK; + } + } + + return status; +} + +fluid_bank_offset_t* +fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id) +{ + fluid_list_t* list = synth->bank_offsets; + fluid_bank_offset_t* offset; + + while (list) { + + offset = (fluid_bank_offset_t*) fluid_list_get(list); + if (offset->sfont_id == sfont_id) { + return offset; + } + + list = fluid_list_next(list); + } + + return NULL; +} + +int +fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) +{ + fluid_bank_offset_t* bank_offset; + + bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); + + if (bank_offset == NULL) { + bank_offset = FLUID_NEW(fluid_bank_offset_t); + if (bank_offset == NULL) { + return -1; + } + bank_offset->sfont_id = sfont_id; + bank_offset->offset = offset; + synth->bank_offsets = fluid_list_prepend(synth->bank_offsets, bank_offset); + } else { + bank_offset->offset = offset; + } + + return 0; +} + +int +fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) +{ + fluid_bank_offset_t* bank_offset; + + bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); + return (bank_offset == NULL)? 0 : bank_offset->offset; +} + +void +fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id) +{ + fluid_bank_offset_t* bank_offset; + + bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); + if (bank_offset != NULL) { + synth->bank_offsets = fluid_list_remove(synth->bank_offsets, bank_offset); + } +} diff --git a/src/libs/fluidsynth/src/fluid_synth.h b/src/libs/fluidsynth/src/fluid_synth.h new file mode 100644 index 0000000000..615e1b7bf6 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_synth.h @@ -0,0 +1,221 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_SYNTH_H +#define _FLUID_SYNTH_H + + +/*************************************************************** + * + * INCLUDES + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "fluidsynth_priv.h" +#include "fluid_list.h" +#include "fluid_rev.h" +#include "fluid_voice.h" +#include "fluid_chorus.h" +#include "fluid_ladspa.h" +#include "fluid_midi_router.h" +#include "fluid_sys.h" + +/*************************************************************** + * + * DEFINES + */ +#define FLUID_NUM_PROGRAMS 129 +#define DRUM_INST_MASK ((unsigned int)0x80000000) + +#if defined(WITH_FLOAT) +#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT +#else +#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE +#endif + + +/*************************************************************** + * + * ENUM + */ +enum fluid_loop { + FLUID_UNLOOPED = 0, + FLUID_LOOP_DURING_RELEASE = 1, + FLUID_NOTUSED = 2, + FLUID_LOOP_UNTIL_RELEASE = 3 +}; + +enum fluid_synth_status +{ + FLUID_SYNTH_CLEAN, + FLUID_SYNTH_PLAYING, + FLUID_SYNTH_QUIET, + FLUID_SYNTH_STOPPED +}; + + +typedef struct _fluid_bank_offset_t fluid_bank_offset_t; + +struct _fluid_bank_offset_t { + int sfont_id; + int offset; +}; + + +/* + * fluid_synth_t + */ + +struct _fluid_synth_t +{ + /* fluid_settings_old_t settings_old; the old synthesizer settings */ + fluid_settings_t* settings; /** the synthesizer settings */ + int polyphony; /** maximum polyphony */ + char with_reverb; /** Should the synth use the built-in reverb unit? */ + char with_chorus; /** Should the synth use the built-in chorus unit? */ + char verbose; /** Turn verbose mode on? */ + char dump; /** Dump events to stdout to hook up a user interface? */ + double sample_rate; /** The sample rate */ + int midi_channels; /** the number of MIDI channels (>= 16) */ + int audio_channels; /** the number of audio channels (1 channel=left+right) */ + int audio_groups; /** the number of (stereo) 'sub'groups from the synth. + Typically equal to audio_channels. */ + int effects_channels; /** the number of effects channels (= 2) */ + unsigned int state; /** the synthesizer state */ + unsigned int ticks; /** the number of audio samples since the start */ + unsigned int start; /** the start in msec, as returned by system clock */ + + fluid_list_t *loaders; /** the soundfont loaders */ + fluid_list_t* sfont; /** the loaded soundfont */ + unsigned int sfont_id; + fluid_list_t* bank_offsets; /** the offsets of the soundfont banks */ + +#if defined(MACOS9) + fluid_list_t* unloading; /** the soundfonts that need to be unloaded */ +#endif + + double gain; /** master gain */ + fluid_channel_t** channel; /** the channels */ + int num_channels; /** the number of channels */ + int nvoice; /** the length of the synthesis process array */ + fluid_voice_t** voice; /** the synthesis processes */ + unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */ + unsigned int storeid; + int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/ + + fluid_real_t** left_buf; + fluid_real_t** right_buf; + fluid_real_t** fx_left_buf; + fluid_real_t** fx_right_buf; + + fluid_real_t** left_ubuf; /* Stores the unaligned buffers (see new_fluid_synth) */ + fluid_real_t** right_ubuf; /* Stores the unaligned buffers (see new_fluid_synth) */ + fluid_real_t** fx_left_ubuf; /* Stores the unaligned buffers (see new_fluid_synth) */ + fluid_real_t** fx_right_ubuf; /* Stores the unaligned buffers (see new_fluid_synth) */ + + fluid_revmodel_t* reverb; + fluid_chorus_t* chorus; + int cur; /** the current sample in the audio buffers to be output */ + + char outbuf[256]; /** buffer for message output */ + double cpu_load; + + fluid_tuning_t*** tuning; /** 128 banks of 128 programs for the tunings */ + fluid_tuning_t* cur_tuning; /** current tuning in the iteration */ + + fluid_midi_router_t* midi_router; /* The midi router. Could be done nicer. */ + fluid_mutex_t busy; /* Indicates, whether the audio thread is currently running. + * Note: This simple scheme does -not- provide 100 % protection against + * thread problems, for example from MIDI thread and shell thread + */ +#ifdef LADSPA + fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /** Effects unit for LADSPA support */ +#endif +}; + + +/** returns 1 if the value has been set, 0 otherwise */ +int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str); + +/** returns 1 if the value exists, 0 otherwise */ +int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str); + +/** returns 1 if the value has been set, 0 otherwise */ +int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val); + +/** returns 1 if the value exists, 0 otherwise */ +int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val); + +/** returns 1 if the value has been set, 0 otherwise */ +int fluid_synth_setint(fluid_synth_t* synth, char* name, int val); + +/** returns 1 if the value exists, 0 otherwise */ +int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val); + + +int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); + +int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out); + +fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, + unsigned int sfontnum, + unsigned int banknum, + unsigned int prognum); + +fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, + unsigned int banknum, + unsigned int prognum); + +int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); +int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); +int fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl); +int fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan); +int fluid_synth_damp_voices(fluid_synth_t* synth, int chan); +int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); +void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* voice); +void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key); +void fluid_synth_sfunload_macos9(fluid_synth_t* synth); + +void fluid_synth_print_voice(fluid_synth_t* synth); + +/** This function assures that every MIDI channels has a valid preset + * (NULL is okay). This function is called after a SoundFont is + * unloaded or reloaded. */ +void fluid_synth_update_presets(fluid_synth_t* synth); + + +int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value); +int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value); + +fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id); +void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id); + + + +/* + * misc + */ + +void fluid_synth_settings(fluid_settings_t* settings); + +#endif /* _FLUID_SYNTH_H */ diff --git a/src/libs/fluidsynth/src/fluid_sys.c b/src/libs/fluidsynth/src/fluid_sys.c new file mode 100644 index 0000000000..802839b193 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_sys.c @@ -0,0 +1,1334 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_sys.h" +//#include + +static char fluid_errbuf[512]; /* buffer for error message */ + +static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL]; +static void* fluid_log_user_data[LAST_LOG_LEVEL]; +static int fluid_log_initialized = 0; + +static char* fluid_libname = "fluidsynth"; + + +void fluid_sys_config() +{ + fluid_log_config(); + fluid_time_config(); +} + + +unsigned int fluid_debug_flags = 0; + +#if DEBUG +/* + * fluid_debug + */ +int fluid_debug(int level, char * fmt, ...) +{ + if (fluid_debug_flags & level) { + fluid_log_function_t fun; + va_list args; + + va_start (args, fmt); + vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); + va_end (args); + + fun = fluid_log_function[FLUID_DBG]; + if (fun != NULL) { + (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]); + } + } + return 0; +} +#endif + +/* + * fluid_set_log_function + */ +fluid_log_function_t +fluid_set_log_function(int level, fluid_log_function_t fun, void* data) +{ + fluid_log_function_t old = NULL; + + if ((level >= 0) && (level < LAST_LOG_LEVEL)) { + old = fluid_log_function[level]; + fluid_log_function[level] = fun; + fluid_log_user_data[level] = data; + } + return old; +} + +/* + * fluid_default_log_function + */ +void +fluid_default_log_function(int level, char* message, void* data) +{ + FILE* out; + +#if defined(WIN32) + out = stdout; +#else + out = stderr; +#endif + + if (fluid_log_initialized == 0) { + fluid_log_config(); + } + + switch (level) { + case FLUID_PANIC: + FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); + break; + case FLUID_ERR: + FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); + break; + case FLUID_WARN: + FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); + break; + case FLUID_INFO: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + case FLUID_DBG: +#if DEBUG + FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); +#endif + break; + default: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + } + fflush(out); +} + +/* + * fluid_init_log + */ +void +fluid_log_config(void) +{ + if (fluid_log_initialized == 0) { + + fluid_log_initialized = 1; + + if (fluid_log_function[FLUID_PANIC] == NULL) { + fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL); + } + + if (fluid_log_function[FLUID_ERR] == NULL) { + fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL); + } + + if (fluid_log_function[FLUID_WARN] == NULL) { + fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL); + } + + if (fluid_log_function[FLUID_INFO] == NULL) { + fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL); + } + + if (fluid_log_function[FLUID_DBG] == NULL) { + fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); + } + } +} + +/* + * fluid_log + */ +int +fluid_log(int level, char* fmt, ...) +{ + fluid_log_function_t fun = NULL; + + va_list args; + va_start (args, fmt); + vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); + va_end (args); + + if ((level >= 0) && (level < LAST_LOG_LEVEL)) { + fun = fluid_log_function[level]; + if (fun != NULL) { + (*fun)(level, fluid_errbuf, fluid_log_user_data[level]); + } + } + return FLUID_FAILED; +} + + +/* + * fluid_error + */ +char* +fluid_error() +{ + return fluid_errbuf; +} + + +/* + * + * fluid_is_midifile + */ +int +fluid_is_midifile(char* filename) +{ + FILE* fp = fopen(filename, "rb"); + char id[4]; + + if (fp == NULL) { + return 0; + } + if (fread((void*) id, 1, 4, fp) != 4) { + fclose(fp); + return 0; + } + fclose(fp); + + return strncmp(id, "MThd", 4) == 0; +} + +/* + * fluid_is_soundfont + * + */ +int +fluid_is_soundfont(char* filename) +{ + FILE* fp = fopen(filename, "rb"); + char id[4]; + + if (fp == NULL) { + return 0; + } + if (fread((void*) id, 1, 4, fp) != 4) { + fclose(fp); + return 0; + } + fclose(fp); + + return strncmp(id, "RIFF", 4) == 0; +} + +#if defined(WIN32) + +/*=============================================================*/ +/* */ +/* Win32 */ +/* */ +/*=============================================================*/ + +/*************************************************************** + * + * Timer + * + */ + +struct _fluid_timer_t +{ + long msec; + fluid_timer_callback_t callback; + void* data; + HANDLE thread; + DWORD thread_id; + int cont; + int auto_destroy; +}; + +static int fluid_timer_count = 0; +DWORD WINAPI fluid_timer_run(LPVOID data); + +fluid_timer_t* +new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, + int new_thread, int auto_destroy) +{ + fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); + if (timer == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + timer->cont = 1; + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->thread = 0; + timer->auto_destroy = auto_destroy; + + if (new_thread) { + timer->thread = CreateThread(NULL, 0, fluid_timer_run, (LPVOID) timer, 0, &timer->thread_id); + if (timer->thread == NULL) { + FLUID_LOG(FLUID_ERR, "Couldn't create timer thread"); + FLUID_FREE(timer); + return NULL; + } + SetThreadPriority(timer->thread, THREAD_PRIORITY_TIME_CRITICAL); + } else { + fluid_timer_run((LPVOID) timer); + } + return timer; +} + +DWORD WINAPI +fluid_timer_run(LPVOID data) +{ + int count = 0; + int cont = 1; + long start; + long delay; + fluid_timer_t* timer; + timer = (fluid_timer_t*) data; + + if ((timer == NULL) || (timer->callback == NULL)) { + return 0; + } + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + + /* keep track of the start time for absolute positioning */ + start = fluid_curtime(); + + while (cont) { + + /* do whatever we have to do */ + cont = (*timer->callback)(timer->data, fluid_curtime() - start); + + count++; + + /* to avoid incremental time errors, I calculate the delay between + two callbacks bringing in the "absolute" time (count * + timer->msec) */ + delay = (count * timer->msec) - (fluid_curtime() - start); + if (delay > 0) { + Sleep(delay); + } + + cont &= timer->cont; + } + + FLUID_LOG(FLUID_DBG, "Timer thread finished"); + + if (timer->auto_destroy) { + FLUID_FREE(timer); + } + + ExitThread(0); + return 0; +} + +int +delete_fluid_timer(fluid_timer_t* timer) +{ + timer->cont = 0; + fluid_timer_join(timer); + FLUID_FREE(timer); + return FLUID_OK; +} + +int +fluid_timer_join(fluid_timer_t* timer) +{ + DWORD wait_result; + if (timer->thread == 0) { + return FLUID_OK; + } + wait_result = WaitForSingleObject(timer->thread, INFINITE); + return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED; +} + + +/*************************************************************** + * + * Time + */ + +double rdtsc(void); +double fluid_estimate_cpu_frequency(void); + +static double fluid_cpu_frequency = -1.0; + +void fluid_time_config(void) +{ + if (fluid_cpu_frequency < 0.0) { + fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0; + } +} + +double fluid_utime(void) +{ + return (rdtsc() / fluid_cpu_frequency); +} + +double rdtsc(void) +{ + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return (double) t.QuadPart; +} + +double fluid_estimate_cpu_frequency(void) +{ +#if 0 + LONGLONG start, stop, ticks; + unsigned int before, after, delta; + double freq; + + start = rdtsc(); + stop = start; + before = fluid_curtime(); + after = before; + + while (1) { + if (after - before > 1000) { + break; + } + after = fluid_curtime(); + stop = rdtsc(); + } + + delta = after - before; + ticks = stop - start; + + freq = 1000 * ticks / delta; + + return freq; + +#else + unsigned int before, after; + LARGE_INTEGER start, stop; + + before = fluid_curtime(); + QueryPerformanceCounter(&start); + + Sleep(1000); + + after = fluid_curtime(); + QueryPerformanceCounter(&stop); + + return (double) 1000 * (stop.QuadPart - start.QuadPart) / (after - before); +#endif +} + + + +#elif defined(MACOS9) +/*=============================================================*/ +/* */ +/* MacOS 9 */ +/* */ +/*=============================================================*/ + + +/*************************************************************** + * + * Timer + */ + +struct _fluid_timer_t +{ + TMTask myTmTask; + long msec; + unsigned int start; + unsigned int count; + int isInstalled; + fluid_timer_callback_t callback; + void* data; + int auto_destroy; +}; + +static TimerUPP myTimerUPP; + +void +_timerCallback(fluid_timer_t *timer) +{ + int cont; + cont = (*timer->callback)(timer->data, fluid_curtime() - timer->start); + if (cont) { + PrimeTime((QElemPtr)timer, timer->msec); + } else { + timer->isInstalled = 0; + } + timer->count++; +} + +fluid_timer_t* +new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, + int new_thread, int auto_destroy) +{ + fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); + if (timer == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + if (!myTimerUPP) + myTimerUPP = NewTimerProc(_timerCallback); + + /* setup tmtask */ + timer->myTmTask.tmAddr = myTimerUPP; + timer->myTmTask.qLink = NULL; + timer->myTmTask.qType = 0; + timer->myTmTask.tmCount = 0L; + timer->myTmTask.tmWakeUp = 0L; + timer->myTmTask.tmReserved = 0L; + + timer->callback = callback; + + timer->msec = msec; + timer->data = data; + timer->start = fluid_curtime(); + timer->isInstalled = 1; + timer->count = 0; + timer->auto_destroy = auto_destroy; + + InsXTime((QElemPtr)timer); + PrimeTime((QElemPtr)timer, msec); + + return timer; +} + +int +delete_fluid_timer(fluid_timer_t* timer) +{ + if (timer->isInstalled) { + RmvTime((QElemPtr)timer); + } + FLUID_FREE(timer); + return FLUID_OK; +} + +int +fluid_timer_join(fluid_timer_t* timer) +{ + if (timer->isInstalled) { + int count = timer->count; + /* wait until count has incremented */ + while (count == timer->count) {} + } + return FLUID_OK; +} + +/*************************************************************** + * + * Time + */ +#define kTwoPower32 (4294967296.0) /* 2^32 */ + +void fluid_time_config(void) +{ +} + +unsigned int fluid_curtime() +{ + /* could be optimized by not going though a double */ + UnsignedWide uS; + double mSf; + unsigned int ms; + + Microseconds(&uS); + + mSf = ((((double) uS.hi) * kTwoPower32) + uS.lo)/1000.0f; + + ms = mSf; + + return (ms); +} + +#elif __BEOS__ + +struct _fluid_timer_t +{ + long msec; + fluid_timer_callback_t callback; + void* data; + thread_id thread; + sem_id sem; +}; + +static int32 +fluid_thread_start(void *data) +{ + fluid_timer_t *timer = (fluid_timer_t *)data; + bigtime_t timestamp = system_time(); + while (true) { + if ((*timer->callback)(timer->data, timer->msec) == 0) { + delete_sem(timer->sem); + } + timestamp += timer->msec * 1000LL; + if (acquire_sem_etc(timer->sem, 1, B_ABSOLUTE_TIMEOUT, timestamp) != B_TIMED_OUT) { + break; + } + } + return B_OK; +} + +fluid_timer_t* +new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, + int new_thread, int auto_destroy) +{ + fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); + if (timer == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->sem = create_sem(0, "synth_sem"); + if (timer->sem < B_OK) { + FLUID_LOG(FLUID_ERR, "Failed to create the timer sem"); + FLUID_FREE(timer); + return NULL; + } + timer->thread = spawn_thread(fluid_thread_start, "synth_thread", B_REAL_TIME_DISPLAY_PRIORITY, timer); + if (timer->thread < B_OK) { + FLUID_LOG(FLUID_ERR, "Failed to create the timer thread"); + delete_sem(timer->sem); + FLUID_FREE(timer); + return NULL; + } + return timer; +} + +int +delete_fluid_timer(fluid_timer_t* timer) +{ + fluid_timer_join(timer); + FLUID_FREE(timer); + return FLUID_OK; +} + +int +fluid_timer_join(fluid_timer_t* timer) +{ + status_t status; + if (timer->thread < 0) + return FLUID_OK; + + delete_sem(timer->sem); + wait_for_thread(timer->thread, &status); + return FLUID_OK; +} + +void fluid_time_config(void) +{ +} + +unsigned int fluid_curtime() +{ + return system_time() / 1000; +} + +double fluid_utime(void) +{ + return system_time(); +} + + +#else + +/*=============================================================*/ +/* */ +/* POSIX */ +/* */ +/*=============================================================*/ + + +/*************************************************************** + * + * Timer + */ + +struct _fluid_timer_t +{ + long msec; + fluid_timer_callback_t callback; + void* data; + pthread_t thread; + int cont; + int auto_destroy; +}; + +void* +fluid_timer_start(void *data) +{ + int count = 0; + int cont = 1; + long start; + long delay; + fluid_timer_t* timer; + timer = (fluid_timer_t*) data; + + /* keep track of the start time for absolute positioning */ + start = fluid_curtime(); + + while (cont) { + + /* do whatever we have to do */ + cont = (*timer->callback)(timer->data, fluid_curtime() - start); + + count++; + + /* to avoid incremental time errors, calculate the delay between + two callbacks bringing in the "absolute" time (count * + timer->msec) */ + delay = (count * timer->msec) - (fluid_curtime() - start); + if (delay > 0) { + usleep(delay * 1000); + } + + cont &= timer->cont; + } + + FLUID_LOG(FLUID_DBG, "Timer thread finished"); + if (timer->thread != 0) { + pthread_exit(NULL); + } + + if (timer->auto_destroy) { + FLUID_FREE(timer); + } + + return NULL; +} + +fluid_timer_t* +new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, + int new_thread, int auto_destroy) +{ + pthread_attr_t *attr = NULL; + pthread_attr_t rt_attr; + int sched = SCHED_FIFO; + struct sched_param priority; + int err; + + fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); + if (timer == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->cont = 1; + timer->thread = 0; + timer->auto_destroy = auto_destroy; + + err = pthread_attr_init(&rt_attr); + if (err == 0) { + err = pthread_attr_setschedpolicy(&rt_attr, SCHED_FIFO); + if (err == 0) { + priority.sched_priority = 10; + err = pthread_attr_setschedparam(&rt_attr, &priority); + if (err == 0) { + attr = &rt_attr; + } + } + } + + if (new_thread) { + err = pthread_create(&timer->thread, attr, fluid_timer_start, (void*) timer); + if (err == 0) { + FLUID_LOG(FLUID_DBG, "The timer thread was created with real-time priority"); + } else { + /* Create the thread with default attributes */ + err = pthread_create(&timer->thread, NULL, fluid_timer_start, (void*) timer); + if (err != 0) { + FLUID_LOG(FLUID_ERR, "Failed to create the timer thread"); + FLUID_FREE(timer); + return NULL; + } else { + FLUID_LOG(FLUID_DBG, "The timer thread does not have real-time priority"); + } + } + } else { + fluid_timer_start((void*) timer); + } + return timer; +} + +int +delete_fluid_timer(fluid_timer_t* timer) +{ + timer->cont = 0; + fluid_timer_join(timer); + FLUID_LOG(FLUID_DBG, "Joined player thread"); + FLUID_FREE(timer); + return FLUID_OK; +} + +int +fluid_timer_join(fluid_timer_t* timer) +{ + int err = 0; + + if (timer->thread != 0) { + err = pthread_join(timer->thread, NULL); + } + FLUID_LOG(FLUID_DBG, "Joined player thread"); + return (err == 0)? FLUID_OK : FLUID_FAILED; +} + + +/*************************************************************** + * + * Time + */ + +static double fluid_cpu_frequency = -1.0; + +double rdtsc(void); +double fluid_estimate_cpu_frequency(void); + +void fluid_time_config(void) +{ + if (fluid_cpu_frequency < 0.0) { + fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0; + } +} + +unsigned int fluid_curtime() +{ + struct timeval now; + gettimeofday(&now, NULL); + return now.tv_sec * 1000 + now.tv_usec / 1000; +} + +double fluid_utime(void) +{ + return (rdtsc() / fluid_cpu_frequency); +} + +#if !defined(__i386__) + +double rdtsc(void) +{ + return 0.0; +} + +double fluid_estimate_cpu_frequency(void) +{ + return 1.0; +} + +#else + +double rdtsc(void) +{ + unsigned int a, b; + + __asm__ ("rdtsc" : "=a" (a), "=d" (b)); + return (double)b * (double)0x10000 * (double)0x10000 + a; +} + +double fluid_estimate_cpu_frequency(void) +{ + double start, stop; + unsigned int a0, b0, a1, b1; + unsigned int before, after; + + before = fluid_curtime(); + __asm__ ("rdtsc" : "=a" (a0), "=d" (b0)); + + sleep(1); + + after = fluid_curtime(); + __asm__ ("rdtsc" : "=a" (a1), "=d" (b1)); + + + start = (double)b0 * (double)0x10000 * (double)0x10000 + a0; + stop = (double)b1 * (double)0x10000 * (double)0x10000 + a1; + + return 1000 * (stop - start) / (after - before); +} +#endif + + +#if 0 + +/*************************************************************** + * + * Floating point exceptions + * + * The floating point exception functions were taken from Ircam's + * jMax source code. http://www.ircam.fr/jmax + * + * FIXME: check in config for i386 machine + * + * Currently not used. I leave the code here in case we want to pick + * this up again some time later. + */ + +/* Exception flags */ +#define _FPU_STATUS_IE 0x001 /* Invalid Operation */ +#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */ +#define _FPU_STATUS_ZE 0x004 /* Zero Divide */ +#define _FPU_STATUS_OE 0x008 /* Overflow */ +#define _FPU_STATUS_UE 0x010 /* Underflow */ +#define _FPU_STATUS_PE 0x020 /* Precision */ +#define _FPU_STATUS_SF 0x040 /* Stack Fault */ +#define _FPU_STATUS_ES 0x080 /* Error Summary Status */ + +/* Macros for accessing the FPU status word. */ + +/* get the FPU status */ +#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw)) + +/* clear the FPU status */ +#define _FPU_CLR_SW() __asm__ ("fnclex" : : ) + +/* Purpose: + * Checks, if the floating point unit has produced an exception in the meantime. + */ +unsigned int fluid_check_fpe_i386(char* explanation) +{ + unsigned int s; + + _FPU_GET_SW(s); + _FPU_CLR_SW(); + + if ((s & _FPU_STATUS_IE) + || (s & _FPU_STATUS_DE) + || (s & _FPU_STATUS_ZE) + || (s & _FPU_STATUS_OE) + || (s & _FPU_STATUS_UE)) { + FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation, + (s & _FPU_STATUS_IE) ? "Invalid operation " : "", + (s & _FPU_STATUS_DE) ? "Denormal number " : "", + (s & _FPU_STATUS_ZE) ? "Zero divide " : "", + (s & _FPU_STATUS_OE) ? "Overflow " : "", + (s & _FPU_STATUS_UE) ? "Underflow " : ""); + } + + return s; +} + +#endif + + +#endif + + +/*************************************************************** + * + * Profiling (Linux, i586 only) + * + */ + +#if WITH_PROFILING + +fluid_profile_data_t fluid_profile_data[] = +{ + { FLUID_PROF_WRITE_S16, "fluid_synth_write_s16 ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0}, + { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0} +}; + + +void fluid_profiling_print(void) +{ + int i; + + printf("fluid_profiling_print\n"); + + FLUID_LOG(FLUID_INFO, "Estimated CPU frequency: %.0f MHz", fluid_cpu_frequency); + FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); + + for (i = 0; i < FLUID_PROF_LAST; i++) { + if (fluid_profile_data[i].count > 0) { + FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f", + fluid_profile_data[i].description, + fluid_profile_data[i].min, + fluid_profile_data[i].total / fluid_profile_data[i].count, + fluid_profile_data[i].max); + } else { + FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description); + } + } +} + + +#endif /* WITH_PROFILING */ + + + +/*************************************************************** + * + * Threads + * + */ + +#if defined(MACOS9) +/* Not implemented */ +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { return NULL; } +int delete_fluid_thread(fluid_thread_t* thread) { return 0; } +int fluid_thread_join(fluid_thread_t* thread) { return 0; } + +#elif defined(WIN32) + +struct _fluid_thread_t { + HANDLE thread; + DWORD thread_id; + fluid_thread_func_t func; + void* data; + int detached; +}; + +static DWORD WINAPI fluid_thread_start(LPVOID data) +{ + fluid_thread_t* thread = (fluid_thread_t*) data; + + thread->func(thread->data); + + if (thread->detached) { + FLUID_FREE(thread); + } + + return 0; +} + + +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) +{ + fluid_thread_t* thread; + + if (func == NULL) { + FLUID_LOG(FLUID_ERR, "Invalid thread function"); + return NULL; + } + + thread = FLUID_NEW(fluid_thread_t); + if (thread == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + thread->data = data; + thread->func = func; + thread->detached = detach; + + thread->thread = CreateThread(NULL, 0, fluid_thread_start, (LPVOID) thread, + 0, &thread->thread_id); + if (thread->thread == NULL) { + FLUID_LOG(FLUID_ERR, "Couldn't create the thread"); + FLUID_FREE(thread); + return NULL; + } + + return thread; +} + +int delete_fluid_thread(fluid_thread_t* thread) +{ + FLUID_FREE(thread); + return FLUID_OK; +} + + +int fluid_thread_join(fluid_thread_t* thread) +{ + DWORD wait_result; + if (thread->thread == 0) { + return FLUID_OK; + } + wait_result = WaitForSingleObject(thread->thread, INFINITE); + return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED; +} + +#elif defined(__BEOS__) +/* Not implemented */ +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { return NULL; } +int delete_fluid_thread(fluid_thread_t* thread) { return 0; } +int fluid_thread_join(fluid_thread_t* thread) { return 0; } + +#else + + +struct _fluid_thread_t { + pthread_t pthread; + fluid_thread_func_t func; + void* data; + int detached; +}; + +static void* fluid_thread_start(void *data) +{ + fluid_thread_t* thread = (fluid_thread_t*) data; + + thread->func(thread->data); + + if (thread->detached) { + FLUID_FREE(thread); + } + + return NULL; +} + +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) +{ + fluid_thread_t* thread; + pthread_attr_t attr; + + if (func == NULL) { + FLUID_LOG(FLUID_ERR, "Invalid thread function"); + return NULL; + } + + thread = FLUID_NEW(fluid_thread_t); + if (thread == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + thread->data = data; + thread->func = func; + thread->detached = detach; + + pthread_attr_init(&attr); + + if (detach) { + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + if (pthread_create(&thread->pthread, &attr, fluid_thread_start, thread)) { + FLUID_LOG(FLUID_ERR, "Failed to create the thread"); + FLUID_FREE(thread); + return NULL; + } + + return thread; +} + +int delete_fluid_thread(fluid_thread_t* thread) +{ + FLUID_FREE(thread); + return FLUID_OK; +} + +int fluid_thread_join(fluid_thread_t* thread) +{ + int err = 0; + + if (thread->pthread != 0) { + err = pthread_join(thread->pthread, NULL); + } + return (err == 0)? FLUID_OK : FLUID_FAILED; +} + +#endif + + + +/*************************************************************** + * + * Sockets + * + */ + + +#if defined(MACINTOSH) +/* Not implemented */ + + +#elif defined(WIN32) + +#if 0 +typedef unsigned int socklen_t; + +#define fluid_socket_read(_S,_B,_L) recv(_S,_B,_L,0) +#define fluid_socket_write(_S,_B,_L) send(_S,_B,_L,0) + +void fluid_socket_close(fluid_socket_t sock) +{ + int r; + char buf[1024]; + if (sock != INVALID_SOCKET) { + shutdown(sock, 0x02); + while (1) { + r = recv(sock, buf, 1024, 0); + if ((r == 0) || (r == SOCKET_ERROR)) { + break; + } + } + closesocket(sock); + } +} +#endif + + +#else +#define fluid_socket_read(_S,_B,_L) read(_S,_B,_L) +#define fluid_socket_write(_S,_B,_L) write(_S,_B,_L) +#define SOCKET_ERROR -1 + +void fluid_socket_close(fluid_socket_t sock) +{ + if (sock != INVALID_SOCKET) { + close(sock); + } +} + +#endif + +#if !defined(MACINTOSH) && !defined(WIN32) && !defined(__BEOS__) + + +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock) +{ + return sock; +} + +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) +{ + return sock; +} + + + +struct _fluid_server_socket_t { + fluid_socket_t socket; + fluid_thread_t* thread; + int cont; + fluid_server_func_t func; + void* data; +}; + + +static void fluid_server_socket_run(void* data) +{ + fluid_server_socket_t* server_socket = (fluid_server_socket_t*) data; + fluid_socket_t client_socket; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + FLUID_LOG(FLUID_DBG, "Server listening for connections"); + + while (server_socket->cont) { + + client_socket = accept(server_socket->socket, (struct sockaddr*) &addr, &addrlen); + + FLUID_LOG(FLUID_DBG, "New client connection"); + + if (client_socket == INVALID_SOCKET) { + if (server_socket->cont) { + FLUID_LOG(FLUID_ERR, "Failed to accept connection"); + } + server_socket->cont = 0; + return; + } else { + int r; + r = (*server_socket->func)(server_socket->data, client_socket, inet_ntoa(addr.sin_addr)); + if (r != 0) { + fluid_socket_close(client_socket); + } + } + } + + FLUID_LOG(FLUID_DBG, "Server closing"); +} + +fluid_server_socket_t* +new_fluid_server_socket(int port, fluid_server_func_t func, void* data) +{ + fluid_server_socket_t* server_socket; + struct sockaddr_in addr; + fluid_socket_t sock; + + if (func == NULL) { + FLUID_LOG(FLUID_ERR, "Invalid callback function"); + return NULL; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + FLUID_LOG(FLUID_ERR, "Failed to create server socket"); + return NULL; + } + + FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(sock, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { + FLUID_LOG(FLUID_ERR, "Failed to bind server socket"); + fluid_socket_close(sock); + return NULL; + } + + if (listen(sock, 10) == SOCKET_ERROR) { + FLUID_LOG(FLUID_ERR, "Failed listen on server socket"); + fluid_socket_close(sock); + return NULL; + } + + server_socket = FLUID_NEW(fluid_server_socket_t); + if (server_socket == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + fluid_socket_close(sock); + return NULL; + } + + server_socket->socket = sock; + server_socket->func = func; + server_socket->data = data; + server_socket->cont = 1; + + server_socket->thread = new_fluid_thread(fluid_server_socket_run, server_socket, 0); + if (server_socket->thread == NULL) { + FLUID_FREE(server_socket); + fluid_socket_close(sock); + return NULL; + } + + return server_socket; +} + +int delete_fluid_server_socket(fluid_server_socket_t* server_socket) +{ + server_socket->cont = 0; + if (server_socket->socket != INVALID_SOCKET) { + fluid_socket_close(server_socket->socket); + } + if (server_socket->thread) { + delete_fluid_thread(server_socket->thread); + } + FLUID_FREE(server_socket); + return FLUID_OK; +} + +int fluid_server_socket_join(fluid_server_socket_t* server_socket) +{ + return fluid_thread_join(server_socket->thread); +} + +#endif diff --git a/src/libs/fluidsynth/src/fluid_sys.h b/src/libs/fluidsynth/src/fluid_sys.h new file mode 100644 index 0000000000..203dfc5e1f --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_sys.h @@ -0,0 +1,305 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/** + + This header contains a bunch of (mostly) system and machine + dependent functions: + + - timers + - current time in milliseconds and microseconds + - debug logging + - profiling + - memory locking + - checking for floating point exceptions + + */ + +#ifndef _FLUID_SYS_H +#define _FLUID_SYS_H + +#include "fluidsynth_priv.h" + + +void fluid_sys_config(void); +void fluid_log_config(void); +void fluid_time_config(void); + +/** + + Additional debugging system, separate from the log system. This + allows to print selected debug messages of a specific subsystem. + + */ + +extern unsigned int fluid_debug_flags; + +#if DEBUG + +enum fluid_debug_level { + FLUID_DBG_DRIVER = 1 +}; + +int fluid_debug(int level, char * fmt, ...); + +#else +#define fluid_debug +#endif + + +/** fluid_curtime() returns the current time in milliseconds. This time + should only be used in relative time measurements. */ + +/** fluid_utime() returns the time in micro seconds. this time should + only be used to measure duration (relative times). */ + +#if defined(WIN32) +#define fluid_curtime() GetTickCount() + +double fluid_utime(void); + +#elif defined(MACOS9) +#include +#include + +unsigned int fluid_curtime(); +#define fluid_utime() 0.0 + +#elif defined(__BEOS__) + +#include +unsigned int fluid_curtime(void); +double fluid_utime(void); + +#else + +unsigned int fluid_curtime(void); +double fluid_utime(void); + +#endif + + + +/** + Timers + + */ + +/* if the callback function returns 1 the timer will continue; if it + returns 0 it will stop */ +typedef int (*fluid_timer_callback_t)(void* data, unsigned int msec); + +typedef struct _fluid_timer_t fluid_timer_t; + +fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, + void* data, int new_thread, int auto_destroy); + +int delete_fluid_timer(fluid_timer_t* timer); +int fluid_timer_join(fluid_timer_t* timer); +int fluid_timer_stop(fluid_timer_t* timer); + +/** + + Muteces + +*/ + +#if defined(MACOS9) +typedef int fluid_mutex_t; +#define fluid_mutex_init(_m) { (_m) = 0; } +#define fluid_mutex_destroy(_m) +#define fluid_mutex_lock(_m) +#define fluid_mutex_unlock(_m) + +#elif defined(WIN32) +typedef HANDLE fluid_mutex_t; +#define fluid_mutex_init(_m) { (_m) = CreateMutex(NULL, 0, NULL); } +#define fluid_mutex_destroy(_m) if (_m) { CloseHandle(_m); } +#define fluid_mutex_lock(_m) WaitForSingleObject(_m, INFINITE) +#define fluid_mutex_unlock(_m) ReleaseMutex(_m) + +#else +#ifndef HAIKU_TARGET_PLATFORM_HAIKU +typedef sem_id fluid_mutex_t; +#define fluid_mutex_init(_m) { (_m) = create_sem(1, "fs_sem"); } +#define fluid_mutex_destroy(_m) delete_sem(_m); +#define fluid_mutex_lock(_m) acquire_sem(_m); +#define fluid_mutex_unlock(_m) release_sem(_m); + +#else +typedef pthread_mutex_t fluid_mutex_t; +#define fluid_mutex_init(_m) pthread_mutex_init(&(_m), NULL) +#define fluid_mutex_destroy(_m) pthread_mutex_destroy(&(_m)) +#define fluid_mutex_lock(_m) pthread_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) pthread_mutex_unlock(&(_m)) +#endif +#endif + +/** + Threads + +*/ + +typedef struct _fluid_thread_t fluid_thread_t; +typedef void (*fluid_thread_func_t)(void* data); + +/** When detached, 'join' does not work and the thread destroys itself + when finished. */ +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach); +int delete_fluid_thread(fluid_thread_t* thread); +int fluid_thread_join(fluid_thread_t* thread); + + +/** + Sockets + +*/ + + +/** The function should return 0 if no error occured, non-zero + otherwise. If the function return non-zero, the socket will be + closed by the server. */ +typedef int (*fluid_server_func_t)(void* data, fluid_socket_t client_socket, char* addr); + + +fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data); +int delete_fluid_server_socket(fluid_server_socket_t* sock); +int fluid_server_socket_join(fluid_server_socket_t* sock); + + +/** Create a new client socket. */ +fluid_socket_t new_fluid_client_socket(char* host, int port); + +/** Delete the client socket. This function should only be called on + sockets create with 'new_fluid_client_socket'. */ +void delete_fluid_client_socket(fluid_socket_t sock); + + +void fluid_socket_close(fluid_socket_t sock); +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock); +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock); + + + +/** + + Profiling + */ + + +/** + Profile numbers. List all the pieces of code you want to profile + here. Be sure to add an entry in the fluid_profile_data table in + fluid_sys.c +*/ +enum { + FLUID_PROF_WRITE_S16, + FLUID_PROF_ONE_BLOCK, + FLUID_PROF_ONE_BLOCK_CLEAR, + FLUID_PROF_ONE_BLOCK_VOICE, + FLUID_PROF_ONE_BLOCK_VOICES, + FLUID_PROF_ONE_BLOCK_REVERB, + FLUID_PROF_ONE_BLOCK_CHORUS, + FLUID_PROF_VOICE_NOTE, + FLUID_PROF_VOICE_RELEASE, + FLUID_PROF_LAST +}; + + +#if WITH_PROFILING + +void fluid_profiling_print(void); + + +/** Profiling data. Keep track of min/avg/max values to execute a + piece of code. */ +typedef struct _fluid_profile_data_t { + int num; + char* description; + double min, max, total; + unsigned int count; +} fluid_profile_data_t; + +extern fluid_profile_data_t fluid_profile_data[]; + +/** Macro to obtain a time refence used for the profiling */ +#define fluid_profile_ref() fluid_utime() + +/** Macro to calculate the min/avg/max. Needs a time refence and a + profile number. */ +#define fluid_profile(_num,_ref) { \ + double _now = fluid_utime(); \ + double _delta = _now - _ref; \ + fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ? _delta : fluid_profile_data[_num].min; \ + fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ? _delta : fluid_profile_data[_num].max; \ + fluid_profile_data[_num].total += _delta; \ + fluid_profile_data[_num].count++; \ + _ref = _now; \ +} + + +#else + +/* No profiling */ +#define fluid_profiling_print() +#define fluid_profile_ref() 0 +#define fluid_profile(_num,_ref) + +#endif + + + +/** + + Memory locking + + Memory locking is used to avoid swapping of the large block of + sample data. + */ + +#if HAVE_SYS_MMAN_H +#define fluid_mlock(_p,_n) mlock(_p, _n) +#define fluid_munlock(_p,_n) munlock(_p,_n) +#else +#define fluid_mlock(_p,_n) 0 +#define fluid_munlock(_p,_n) +#endif + + +/** + + Floating point exceptions + + fluid_check_fpe() checks for "unnormalized numbers" and other + exceptions of the floating point processsor. +*/ +#if 0 +/* Enable FPE exception check */ +#define fluid_check_fpe(expl) fluid_check_fpe_i386(expl) +#else +/* Disable FPE exception check */ +#define fluid_check_fpe(expl) +#endif + +unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe); + +#endif /* _FLUID_SYS_H */ + diff --git a/src/libs/fluidsynth/src/fluid_tuning.c b/src/libs/fluidsynth/src/fluid_tuning.c new file mode 100644 index 0000000000..9404e64947 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_tuning.c @@ -0,0 +1,108 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#include "fluid_tuning.h" +#include "fluidsynth_priv.h" + + +fluid_tuning_t* new_fluid_tuning(char* name, int bank, int prog) +{ + fluid_tuning_t* tuning; + int i; + + tuning = FLUID_NEW(fluid_tuning_t); + if (tuning == NULL) { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + tuning->name = NULL; + + if (name != NULL) { + tuning->name = FLUID_STRDUP(name); + } + + tuning->bank = bank; + tuning->prog = prog; + + for (i = 0; i < 128; i++) { + tuning->pitch[i] = i * 100.0; + } + + return tuning; +} + +void delete_fluid_tuning(fluid_tuning_t* tuning) +{ + if (tuning == NULL) { + return; + } + if (tuning->name != NULL) { + FLUID_FREE(tuning->name); + } + FLUID_FREE(tuning); +} + +void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name) +{ + if (tuning->name != NULL) { + FLUID_FREE(tuning->name); + tuning->name = NULL; + } + if (name != NULL) { + tuning->name = FLUID_STRDUP(name); + } +} + +char* fluid_tuning_get_name(fluid_tuning_t* tuning) +{ + return tuning->name; +} + +void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch) +{ + tuning->pitch[key] = pitch; +} + +void fluid_tuning_set_octave(fluid_tuning_t* tuning, double* pitch_deriv) +{ + int i; + + for (i = 0; i < 128; i++) { + tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; + } +} + +void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch) +{ + int i; + + for (i = 0; i < 128; i++) { + tuning->pitch[i] = pitch[i]; + } +} + +void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch) +{ + if ((key >= 0) && (key < 128)) { + tuning->pitch[key] = pitch; + } +} diff --git a/src/libs/fluidsynth/src/fluid_tuning.h b/src/libs/fluidsynth/src/fluid_tuning.h new file mode 100644 index 0000000000..661a537921 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_tuning.h @@ -0,0 +1,64 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +/* + + More information about micro tuning can be found at: + + http://www.midi.org/about-midi/tuning.htm + http://www.midi.org/about-midi/tuning-scale.htm + http://www.midi.org/about-midi/tuning_extens.htm + +*/ + +#ifndef _FLUID_TUNING_H +#define _FLUID_TUNING_H + +#include "fluidsynth_priv.h" + +struct _fluid_tuning_t { + char* name; + int bank; + int prog; + double pitch[128]; /* the pitch of every key, in cents */ +}; + +fluid_tuning_t* new_fluid_tuning(char* name, int bank, int prog); +void delete_fluid_tuning(fluid_tuning_t* tuning); + +void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name); +char* fluid_tuning_get_name(fluid_tuning_t* tuning); + +#define fluid_tuning_get_bank(_t) ((_t)->bank) +#define fluid_tuning_get_prog(_t) ((_t)->prog) + +void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch); +#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key]) + +void fluid_tuning_set_octave(fluid_tuning_t* tuning, double* pitch_deriv); + +void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch); +#define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) + + + + +#endif /* _FLUID_TUNING_H */ diff --git a/src/libs/fluidsynth/src/fluid_voice.c b/src/libs/fluidsynth/src/fluid_voice.c new file mode 100644 index 0000000000..a2dda2ef56 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_voice.c @@ -0,0 +1,2091 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "fluidsynth_priv.h" +#include "fluid_voice.h" +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_conv.h" +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_sfont.h" + +#ifndef WITH_FLOAT +#ifdef ENABLE_SSE +#error "Can't use SSE extensions with other than float type!" +#endif +#endif + +#ifdef ENABLE_SSE +#include "fluid_sse.h" +float interp_coeff_sse_mem[FLUID_INTERP_MAX*4+4]; +sse_t* interp_coeff_sse; +#endif + +/* used for filter turn off optimization - if filter cutoff is above the + specified value and filter q is below the other value, turn filter off */ +#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f +#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f + +/* Smallest amplitude that can be perceived (full scale is +/- 0.5) + * 16 bits => 96+4=100 dB dynamic range => 0.00001 + * 0.00001 * 2 is approximately 0.00003 :) + */ +#define FLUID_NOISE_FLOOR 0.00003 + +/* these should be the absolute minimum that FluidSynth can deal with */ +#define FLUID_MIN_LOOP_SIZE 2 +#define FLUID_MIN_LOOP_PAD 1 + +/* min vol envelope release (to stop clicks) in SoundFont timecents */ +#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ + +fluid_interp_coeff_t interp_coeff[FLUID_INTERP_MAX]; +fluid_interp_coeff_t interp_coeff_linear[FLUID_INTERP_MAX]; +fluid_real_t sinc_table7[7][FLUID_INTERP_MAX]; + +/* + * fluid_voice_config + */ +void fluid_voice_config() +{ + int i; + double x; + +#ifdef ENABLE_SSE + sse_t* sse_a; + interp_coeff_sse = (sse_t*)FLUID_ALIGN16BYTE(&interp_coeff_sse_mem); +#endif + + /* Initialize the coefficients for the interpolation. The math comes + * from a mail, posted by Olli Niemitalo to the music-dsp mailing + * list (I found it in the music-dsp archives + * http://www.smartelectronix.com/musicdsp/). */ + + for (i = 0; i < FLUID_INTERP_MAX; i++) { + + x = (double) i / (double) FLUID_INTERP_MAX; + + interp_coeff[i].a0 = (fluid_real_t) (x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i].a1 = (fluid_real_t) (1.0 + x * x * (1.5 * x - 2.5)); + interp_coeff[i].a2 = (fluid_real_t) (x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i].a3 = (fluid_real_t) (0.5 * x * x * (x - 1.0)); + + interp_coeff_linear[i].a0 = (fluid_real_t) (1. -x); + interp_coeff_linear[i].a1 = (fluid_real_t) x; + +#if 0 + interp_coeff[i].c0 = (signed int) (65536.0 * x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i].c1 = (signed int) (65536.0 * (1.0 + x * x * (1.5 * x - 2.5))); + interp_coeff[i].c2 = (signed int) (65536.0 * x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i].c3 = (signed int) (65536.0 * 0.5 * x * x * (x - 1.0)); +#endif +#ifdef ENABLE_SSE + sse_a=&interp_coeff_sse[i]; + sse_a->sf[0]=interp_coeff[i].a0; + sse_a->sf[1]=interp_coeff[i].a1; + sse_a->sf[2]=interp_coeff[i].a2; + sse_a->sf[3]=interp_coeff[i].a3; +#endif +} + +#define sinc_interp_order 7 + /* i: Offset in terms of whole samples */ + for (i = 0; i < sinc_interp_order; i++){ + int ii; + /* ii: Offset in terms of fractional samples ('subsamples') */ + for (ii = 0; ii < FLUID_INTERP_MAX; ii++){ + /* Move the origin into the center of the table */ + double i_shifted=(double)i-((double)sinc_interp_order)/2. + + (double)ii/(double)FLUID_INTERP_MAX; + double v=1.; + + /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ + if (fabs(i_shifted) > 0.000001) { + v = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted); + /* Hamming window */ + v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)sinc_interp_order)); + } + sinc_table7[i][FLUID_INTERP_MAX-ii-1]=v; + } + } + fluid_check_fpe("interpolation table calculation"); +} + +/* + * new_fluid_voice + */ +fluid_voice_t* +new_fluid_voice(fluid_real_t output_rate) +{ + fluid_voice_t* voice; + voice = FLUID_NEW(fluid_voice_t); + if (voice == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + voice->status = FLUID_VOICE_CLEAN; + voice->chan = NO_CHANNEL; + voice->key = 0; + voice->vel = 0; + voice->channel = NULL; + voice->sample = NULL; + voice->output_rate = output_rate; + + /* The 'sustain' and 'finished' segments of the volume / modulation + * envelope are constant. They are never affected by any modulator + * or generator. Therefore it is enough to initialize them once + * during the lifetime of the synth. + */ + voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; + voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; + voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; + voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; + + voice->volenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; + voice->volenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; + voice->volenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; + + voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; + voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; + voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; + voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; + + voice->modenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; + voice->modenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; + voice->modenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; + + return voice; +} + +/* + * delete_fluid_voice + */ +int +delete_fluid_voice(fluid_voice_t* voice) +{ + if (voice == NULL) { + return FLUID_OK; + } + FLUID_FREE(voice); + return FLUID_OK; +} + +/* fluid_voice_init + * + * Initialize the synthesis process + */ +int +fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, + fluid_channel_t* channel, int key, int vel, unsigned int id, + unsigned int start_time, fluid_real_t gain) +{ + /* Note: The voice parameters will be initialized later, when the + * generators have been retrieved from the sound font. Here, only + * the 'working memory' of the voice (position in envelopes, history + * of IIR filters, position in sample etc) is initialized. */ + + + voice->id = id; + voice->chan = fluid_channel_get_num(channel); + voice->key = (unsigned char) key; + voice->vel = (unsigned char) vel; + voice->channel = channel; + voice->mod_count = 0; + voice->sample = sample; + voice->start_time = start_time; + voice->ticks = 0; + voice->debug = 0; + voice->has_looped = 0; /* Will be set during voice_write when the 2nd loop point is reached */ + voice->last_fres = -1; /* The filter coefficients have to be calculated later in the DSP loop. */ + voice->filter_startup = 1; /* Set the filter immediately, don't fade between old and new settings */ + voice->interp_method = fluid_channel_get_interp_method(voice->channel); + + /* vol env initialization */ + voice->volenv_count = 0; + voice->volenv_section = 0; + voice->volenv_val = 0.0f; + voice->amp = 0.0f; /* The last value of the volume envelope, used to + calculate the volume increment during + processing */ + + /* mod env initialization*/ + voice->modenv_count = 0; + voice->modenv_section = 0; + voice->modenv_val = 0.0f; + + /* mod lfo */ + voice->modlfo_val = 0.0;/* Fixme: Retrieve from any other existing + voice on this channel to keep LFOs in + unison? */ + + /* vib lfo */ + voice->viblfo_val = 0.0f; /* Fixme: See mod lfo */ + + /* Clear sample history in filter */ + voice->hist1 = 0; + voice->hist2 = 0; + + /* Set all the generators to their default value, according to SF + * 2.01 section 8.1.3 (page 48). The value of NRPN messages are + * copied from the channel to the voice's generators. The sound font + * loader overwrites them. The generator values are later converted + * into voice parameters in + * fluid_voice_calculate_runtime_synthesis_parameters. */ + fluid_gen_init(&voice->gen[0], channel); + + voice->synth_gain = gain; + /* avoid division by zero later*/ + if (voice->synth_gain < 0.0000001){ + voice->synth_gain = 0.0000001; + } + + /* For a looped sample, this value will be overwritten as soon as the + * loop parameters are initialized (they may depend on modulators). + * This value can be kept, it is a worst-case estimate. + */ + + voice->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain; + voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain; + + /* Increment the reference count of the sample to prevent the + unloading of the soundfont while this voice is playing. */ + fluid_sample_incr_ref(voice->sample); + + return FLUID_OK; +} + +void fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) +{ + voice->gen[i].val = val; + voice->gen[i].flags = GEN_SET; +} + +void fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val) +{ + voice->gen[i].val += val; + voice->gen[i].flags = GEN_SET; +} + +float fluid_voice_gen_get(fluid_voice_t* voice, int gen) +{ + return voice->gen[gen].val; +} + +fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) +{ + /* This is an extension to the SoundFont standard. More + * documentation is available at the fluid_synth_set_gen2() + * function. */ + if (voice->gen[num].flags == GEN_ABS_NRPN) { + return (fluid_real_t) voice->gen[num].nrpn; + } else { + return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); + } +} + + +/* + * fluid_voice_write + * + * This is where it all happens. This function is called by the + * synthesizer to generate the sound samples. The synthesizer passes + * four audio buffers: left, right, reverb out, and chorus out. + * + * The biggest part of this function sets the correct values for all + * the dsp parameters (all the control data boil down to only a few + * dsp parameters). The dsp routine is #included in several places (fluid_dsp_core.c). + */ +int +fluid_voice_write(fluid_voice_t* voice, + fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf, + fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf) +{ + unsigned int i, start, end_in_buffer; + fluid_real_t incr; + fluid_real_t fres; + + /* All variables starting with dsp_ are used by the DSP + loop. Documented in fluid_dsp_core.c */ + + int dsp_phase_index; + unsigned int dsp_i; + fluid_phase_t dsp_phase, dsp_phase_incr; + fluid_real_t dsp_incr; + fluid_real_t dsp_amp, dsp_amp_incr, dsp_centernode, dsp_hist1, dsp_hist2; + fluid_real_t dsp_b02, dsp_b1, dsp_a1, dsp_a2; + fluid_real_t dsp_a1_incr; + fluid_real_t dsp_a2_incr; + fluid_real_t dsp_b02_incr; + fluid_real_t dsp_b1_incr; + fluid_interp_coeff_t* dsp_coeff; + unsigned int dsp_start, dsp_end; + int dsp_filter_coeff_incr_count; + int dsp_use_filter_flag = 1; + short* dsp_data; + int dsp_interp_method = voice->interp_method; + + +#ifdef ENABLE_SSE + float mem_for_sse_interface[5*4+4]; /* Reserve memory */ + /* +4: add four floats for 16 added bytes */ + + /* Align the first element */ + sse_t* sse_n = (sse_t*) FLUID_ALIGN16BYTE(&mem_for_sse_interface); + sse_t* sse_a = sse_n++; /* The ++ operator increases + * by the size of the structure! */ + sse_t* sse_b = sse_n++; + sse_t* sse_c = sse_n++; + sse_t* sse_d = sse_n++; + sse_t* sse_e = sse_n++; + sse_t* sse_coeff; + + sse_t* sse_src; + sse_t* sse_dest_left; + sse_t* sse_dest_right; + sse_t* sse_dest; +#endif + + /* +4: add four floats for 16 added bytes */ + fluid_real_t dsp_buf_unaligned[FLUID_BUFSIZE+4]; + fluid_real_t* dsp_buf = (fluid_real_t*) FLUID_ALIGN16BYTE(&dsp_buf_unaligned); + fluid_env_data_t* env_data; + fluid_real_t x; + + + /* make sure we're playing and that we have sample data */ + if (!_PLAYING(voice)) { + return FLUID_OK; + } + + /******************* sample **********************/ + + if (voice->sample == NULL) { + fluid_voice_off(voice); + return FLUID_OK; + } + + fluid_check_fpe("voice_write startup"); + + /* Range checking for sample- and loop-related parameters + * Initial phase is calculated here*/ + fluid_voice_check_sample_sanity(voice); + + /******************* vol env **********************/ + + env_data = &voice->volenv_data[voice->volenv_section]; + + /* skip to the next section of the envelope if necessary */ + while (voice->volenv_count >= env_data->count) { + env_data = &voice->volenv_data[++voice->volenv_section]; + voice->volenv_count = 0; + } + + /* calculate the envelope value and check for valid range */ + x = env_data->coeff * voice->volenv_val + env_data->incr; + if (x < env_data->min) { + x = env_data->min; + voice->volenv_section++; + voice->volenv_count = 0; + } else if (x > env_data->max) { + x = env_data->max; + voice->volenv_section++; + voice->volenv_count = 0; + } + + voice->volenv_val = x; + voice->volenv_count++; + + if (voice->volenv_section == FLUID_VOICE_ENVFINISHED) { + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); + fluid_voice_off(voice); + return FLUID_OK; + } + fluid_check_fpe("voice_write vol env"); + + /******************* mod env **********************/ + + env_data = &voice->modenv_data[voice->modenv_section]; + + /* skip to the next section of the envelope if necessary */ + while (voice->modenv_count >= env_data->count) { + env_data = &voice->modenv_data[++voice->modenv_section]; + voice->modenv_count = 0; + } + + /* calculate the envelope value and check for valid range */ + x = env_data->coeff * voice->modenv_val + env_data->incr; + + if (x < env_data->min) { + x = env_data->min; + voice->modenv_section++; + voice->modenv_count = 0; + } else if (x > env_data->max) { + x = env_data->max; + voice->modenv_section++; + voice->modenv_count = 0; + } + + voice->modenv_val = x; + voice->modenv_count++; + fluid_check_fpe("voice_write mod env"); + + /******************* mod lfo **********************/ + + if (voice->ticks >= voice->modlfo_delay) { + voice->modlfo_val += voice->modlfo_incr; + if (voice->modlfo_val > 1.0) { + voice->modlfo_incr = -voice->modlfo_incr; + voice->modlfo_val = (fluid_real_t) 2.0 - voice->modlfo_val; + } else if (voice->modlfo_val < -1.0) { + voice->modlfo_incr = -voice->modlfo_incr; + voice->modlfo_val = (fluid_real_t) -2.0 - voice->modlfo_val; + } + } + fluid_check_fpe("voice_write mod LFO"); + + /******************* vib lfo **********************/ + + if (voice->ticks >= voice->viblfo_delay) { + voice->viblfo_val += voice->viblfo_incr; + if (voice->viblfo_val > (fluid_real_t) 1.0) { + voice->viblfo_incr = -voice->viblfo_incr; + voice->viblfo_val = (fluid_real_t) 2.0 - voice->viblfo_val; + } else if (voice->viblfo_val < -1.0) { + voice->viblfo_incr = -voice->viblfo_incr; + voice->viblfo_val = (fluid_real_t) -2.0 - voice->viblfo_val; + } + } + fluid_check_fpe("voice_write Vib LFO"); + + /******************* amplitude **********************/ + + /* calculate final amplitude + * - initial gain + * - amplitude envelope + */ + + if (voice->volenv_section == FLUID_VOICE_ENVDELAY) { + /* The volume amplitude is in hold phase. No sound is produced. */ + goto post_process; + } else if (voice->volenv_section == FLUID_VOICE_ENVATTACK) { + /* the envelope is in the attack section: ramp linearly to max value. + * A positive modlfo_to_vol should increase volume (negative attenuation). + */ + dsp_amp = fluid_atten2amp(voice->attenuation) + * fluid_cb2amp (voice->modlfo_val * -voice->modlfo_to_vol) + * voice->volenv_val; + } else { + fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amp_max; + + dsp_amp = fluid_atten2amp(voice->attenuation) + * fluid_cb2amp (960.0f * (1.0f - voice->volenv_val) + + voice->modlfo_val * -voice->modlfo_to_vol); + + /* Here we are trying to turn off a voice, if the volume has dropped + * low enough. + * Motivation: + * If voices are not turned off as soon as possible, the + * DSP loop burns a lot of CPU time producing sounds that no one + * can hear + * Problem: + * It would be tempting to just look at the attenuation, but then + * a voice terminates as soon as it is brought to 0 with a volume + * pedal (MIDI CC 7 or 11). + */ + + /* A voice can be turned off, when an estimate for the volume + * (upper bound) falls below that volume, that will drop the + * sample below the noise floor. + */ + + /* If the loop amplitude is known, we can use it if the voice loop is within + * the sample loop + */ +#if 0 + printf("%i %i %i %i %i %i\n", voice->has_looped, voice->sample->amplitude_that_reaches_noise_floor_is_valid, voice->loopstart, voice->loopend, voice->sample->loopstart, voice->sample->loopend ); +#endif + + /* Is the playing pointer already in the loop? */ + if (voice->has_looped){ + amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop; + } else { + amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_nonloop; + }; +#if 0 + printf("Retrieving %f\n", amplitude_that_reaches_noise_floor); +#endif + + /* voice->attenuation_min is a lower boundary for the attenuation + * now and in the future (possibly 0 in the worst case). Now the + * amplitude of sample and volenv cannot exceed amp_max (since + * volenv_val can only drop): + */ + + amp_max = fluid_atten2amp(voice->min_attenuation_cB) * voice->volenv_val; + + /* printf("Att min: %f Amp max: %f Limit: %f\n",voice->min_attenuation_cB,amp_max, amplitude_that_reaches_noise_floor); */ + +// printf("Amp max: %f\n",amp_max); + + /* And if amp_max is already smaller than the known amplitude, + * which will attenuate the sample below the noise floor, then we + * can safely turn off the voice. Duh. */ + if (amp_max < amplitude_that_reaches_noise_floor){ + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); +#if 0 + printf("Voice turned off! Amp is %f\n", amp_max); +#endif + fluid_voice_off(voice); + goto post_process; + } + } + + /* At this point, dsp_amp is the desired amplitude for the voice. + * This value will be reached at the end of the next buffer. So we + * raise the amplitude smoothly from the current voice amplitude + * (voice->amp) to the wanted amplitude dsp_amp. + * + * By how much do we have to increase voice->amp, so that it reaches + * dsp_amp, when the increment is added FLUID_BUFSIZE times? */ + dsp_amp_incr = (dsp_amp - voice->amp) / FLUID_BUFSIZE; + + /* dsp_amp will now be fed into the DSP loop. Use the amplitude, + * that came out of the last DSP loop run. */ + dsp_amp = voice->amp; + + fluid_check_fpe("voice_write amplitude calculation"); + if ((dsp_amp == 0.0f) && (dsp_amp_incr == 0.0f)) { + goto post_process; + } + + /* Calculate the number of samples, that the DSP loop advances + * through the original waveform with each step in the output + * buffer. It is the ratio between the frequencies of original + * waveform and output waveform.*/ + incr = fluid_ct2hz_real(voice->pitch + + voice->modlfo_val * voice->modlfo_to_pitch + + voice->viblfo_val * voice->viblfo_to_pitch + + voice->modenv_val * voice->modenv_to_pitch) / voice->root_pitch; + + /* Transfer the phase from the voice into the dsp loop parameter + dsp_phase */ + fluid_phase_set(dsp_phase, voice->phase); + + /* Convert the 'speed' through the original waveform to a + * representation 'integer part' and 'fractional part' */ + fluid_phase_set_float(dsp_phase_incr, incr); + /* incr *= 1.00000000000001; FIXME: gcc optimization problem. Quick fix. [Commented out. Don't understand the problem, PH] */ + fluid_check_fpe("voice_write phase calculation"); + + /* Check, if we are really making progress through the original + * sample. If the step size is rounded to 0, the DSP loop would get + * stuck. */ + /* [PH] I commented this out. The first time a voice is called, dsp_phase + * will be zero. Settings the fractionnal part of the phase to + * non-zero, causes the sample to be interpolated even if it is + * played a the root key (cfr. fluid_dsp_float.c:96: + * if ((fluid_phase_fract(dsp_phase) == 0))) + * + * I replaced it with a check on the phase increment. + */ +/* if ((fluid_phase_index(dsp_phase) == 0) && (fluid_phase_fract(dsp_phase) == 0)) { */ +/* fluid_phase_fract(dsp_phase) = 1; */ +/* } */ + if ((fluid_phase_index(dsp_phase_incr) == 0) && (fluid_phase_fract(dsp_phase_incr) == 0)) { + fluid_phase_fract(dsp_phase_incr) = 1; + } + + /*************** resonant filter ******************/ + + /* calculate the frequency of the resonant filter in Hz */ + fres = fluid_ct2hz(voice->fres + + voice->modlfo_val * voice->modlfo_to_fc + + voice->modenv_val * voice->modenv_to_fc); + + /* Testcase for filter resonance: Use the following line, and press + * A3. There should be a _very_ pronounced resonant peak, which + * drops down, when moving the pitch bend wheel only slightly. */ + //fres=440;voice->q_lin=100; + + + /* FIXME - Still potential for a click during turn on, can we interpolate + between 20khz cutoff and 0 Q? */ + + /* if filter has not yet started and filter cutoff and Q don't + exceed "audible" thresholds, then don't turn on the filter. + Once the filter is turned on, it remains on. */ +/* if (voice->filter_startup */ +/* && (fres > FLUID_MAX_AUDIBLE_FILTER_FC) */ +/* && (voice->q_lin < FLUID_MIN_AUDIBLE_FILTER_Q)) { */ +/* dsp_use_filter_flag = 0; */ +/* } else if (fres < 5) { */ +/* fres = 5; */ +/* } */ + + /* I removed the optimization of turning the filter off when the + * resonance frequence is above the maximum frequency. Instead, the + * filter frequence is set to a maximum of 0.45 times the sampling + * rate. For a 44100 kHz sampling rate, this amounts to 19845 + * Hz. The reasing is that were problems with anti-aliasing when the + * synthesizer was run at lower sampling rates. Thanks to Stephan + * Tassart for pointing me to this bug. By turning the filter on and + * clipping the maximum filter frequency at 0.45*srate, the filter + * is used as an anti-aliasing filter. */ + + if (fres > 0.45f * voice->output_rate) { + fres = 0.45f * voice->output_rate; + } else if (fres < 5) { + fres = 5; + } + + + /* if filter enabled and there is a significant frequency change.. */ + if (/*dsp_use_filter_flag &&*/ (abs(fres - voice->last_fres) > 0.01)) { + + /* The filter coefficients have to be recalculated (filter + * parameters have changed). Recalculation for various reasons is + * forced by setting last_fres to -1. The flag filter_startup + * indicates, that the DSP loop runs for the first time, in this + * case, the filter is set directly, instead of smoothly fading + * between old and new settings. + * + * Those equations from Robert Bristow-Johnson's `Cookbook + * formulae for audio EQ biquad filter coefficients', obtained + * from Harmony-central.com / Computer / Programming. They are + * the result of the bilinear transform on an analogue filter + * prototype. To quote, `BLT frequency warping has been taken + * into account for both significant frequency relocation and for + * bandwidth readjustment'. */ + + fluid_real_t omega = (fluid_real_t) (2.0 * M_PI * (fres / 44100.0f)); + fluid_real_t sin_coeff = (fluid_real_t) sin(omega); + fluid_real_t cos_coeff = (fluid_real_t) cos(omega); + fluid_real_t alpha_coeff = sin_coeff / (2.0f * voice->q_lin); + fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); + + /* Calculate the filter coefficients. All coefficients are + * normalized by a0. Think of `a1' as `a1/a0'. + * + * Here a couple of multiplications are saved by reusing common expressions. + * The original equations should be: + * voice->b0=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; + * voice->b1=(1.-cos_coeff)*a0_inv*voice->filter_gain; + * voice->b2=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; */ + + fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; + fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; + fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * voice->filter_gain; + /* both b0 -and- b2 */ + fluid_real_t b02_temp = b1_temp * 0.5f; + + if (voice->filter_startup) { + /* The filter is calculated, because the voice was started up. + * In this case set the filter coefficients without delay. + */ + voice->a1 = a1_temp; + voice->a2 = a2_temp; + voice->b02 = b02_temp; + voice->b1 = b1_temp; + voice->filter_coeff_incr_count = 0; + voice->filter_startup = 0; +// printf("Setting initial filter coefficients.\n"); + } else { + + /* The filter frequency is changed. Calculate an increment + * factor, so that the new setting is reached after one buffer + * length. x_incr is added to the current value FLUID_BUFSIZE + * times. The length is arbitrarily chosen. Longer than one + * buffer will sacrifice some performance, though. Note: If + * the filter is still too 'grainy', then increase this number + * at will. + */ + +#define FILTER_TRANSITION_SAMPLES (FLUID_BUFSIZE) + + voice->a1_incr = (a1_temp - voice->a1) / FILTER_TRANSITION_SAMPLES; + voice->a2_incr = (a2_temp - voice->a2) / FILTER_TRANSITION_SAMPLES; + voice->b02_incr = (b02_temp - voice->b02) / FILTER_TRANSITION_SAMPLES; + voice->b1_incr = (b1_temp - voice->b1) / FILTER_TRANSITION_SAMPLES; + /* Have to add the increments filter_coeff_incr_count times. */ + voice->filter_coeff_incr_count = FILTER_TRANSITION_SAMPLES; + } + voice->last_fres = fres; + fluid_check_fpe("voice_write filter calculation"); + } + + /* Now we set up the variables, that go into the DSP routine. For documentation, + * see fluid_dsp_core.c + */ + /* Sample waveform data */ + dsp_data = voice->sample->data; + + /* IIR filter sample history */ + dsp_hist1 = voice->hist1; + dsp_hist2 = voice->hist2; + + /* IIR filter coefficients */ + dsp_a1 = voice->a1; + dsp_a2 = voice->a2; + dsp_b02 = voice->b02; + dsp_b1 = voice->b1; + dsp_a1_incr = voice->a1_incr; + dsp_a2_incr = voice->a2_incr; + dsp_b02_incr = voice->b02_incr; + dsp_b1_incr = voice->b1_incr; + dsp_filter_coeff_incr_count = voice->filter_coeff_incr_count; + + fluid_check_fpe("voice_write DSP coefficients"); + + /*********************** run the dsp chain ************************ + * The sample is mixed with the output buffer. + * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. + * Depending on the position in the loop and the loop size, this + * may require several runs. */ + fluid_check_fpe("voice_write DSP processing"); + + if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE)) + || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { + + /* At which index does the loop point occur in the output buffer? + * This calculates the first index in the buffer, which uses + * sample data taken after the looparound. */ + end_in_buffer = fluid_phase_steps(dsp_phase, voice->loopend, incr); + + if (end_in_buffer >= FLUID_BUFSIZE) { + /* The loop occurs after the end of the buffer. + * Process the whole buffer in one run. */ + dsp_start = 0; + dsp_end = FLUID_BUFSIZE; +#include "fluid_dsp_core.c" + + } else { + /* The loop occurs during the current buffer length. Note, that + * this method is unable to cope with a 'skipped' loop point. * + * If, by means of witchcraft, evil magic or modulators, we end + * up beyond * the loop point, a SEGV is unavoidable. * We + * can't even detect this here, because the calculations are + * done using * unsigned ints (only positive), and end_in_buffer + * would be negative * for a skipped loop point...*/ + start = 0; + + while (end_in_buffer < FLUID_BUFSIZE) { + + dsp_start = start; + dsp_end = end_in_buffer; +#include "fluid_dsp_core.c" + + /* loop */ + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + start = end_in_buffer; + end_in_buffer += fluid_phase_steps(dsp_phase, voice->loopend, incr); + } + voice->has_looped=1; + dsp_start = start; + dsp_end = FLUID_BUFSIZE; +#include "fluid_dsp_core.c" + } + + } else { + /* Not looping right now. */ + + dsp_start = 0; + end_in_buffer = fluid_phase_steps(dsp_phase, voice->end, incr); + + if (end_in_buffer >= FLUID_BUFSIZE) { + /* Run the whole buffer at once */ + dsp_end = FLUID_BUFSIZE; +#include "fluid_dsp_core.c" + } else { + /* The sample ends in the middle of the buffer length. + * Process that far, and turn the voice off. + */ + dsp_end = end_in_buffer; +#include "fluid_dsp_core.c" + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); + fluid_voice_off(voice); + goto post_process; + } + } + + /*************** finishing ******************/ + /* copy back the state for the next cycle */ + + voice->hist1 = dsp_hist1; + voice->hist2 = dsp_hist2; + voice->phase = dsp_phase; + voice->amp = dsp_amp; + voice->a1 = dsp_a1; + voice->a2 = dsp_a2; + voice->b02 = dsp_b02; + voice->b1 = dsp_b1; + voice->filter_coeff_incr_count = dsp_filter_coeff_incr_count; + +/* if (dsp_filter_coeff_incr_count) { */ +/* printf("ticks = %d, dsp_filter_coeff_incr_count = %d\n", */ +/* voice->ticks, dsp_filter_coeff_incr_count); */ +/* } */ + + post_process: + voice->ticks += FLUID_BUFSIZE; + fluid_check_fpe("voice_write postprocess"); + return FLUID_OK; +} + +/* + * fluid_voice_get_channel + */ +fluid_channel_t* +fluid_voice_get_channel(fluid_voice_t* voice) +{ + return voice->channel; +} + +/* + * fluid_voice_start + */ +void fluid_voice_start(fluid_voice_t* voice) +{ + /* The maximum volume of the loop is calculated and cached once for each + * sample with its nominal loop settings. This happens, when the sample is used + * for the first time.*/ + + fluid_voice_calculate_runtime_synthesis_parameters(voice); + + /* Force setting of the phase at the first DSP loop run + * This cannot be done earlier, because it depends on modulators.*/ + voice->check_sample_sanity_flag=FLUID_SAMPLESANITY_STARTUP; + + voice->ref = fluid_profile_ref(); + + voice->status = FLUID_VOICE_ON; +} + +/* + * fluid_voice_calculate_runtime_synthesis_parameters + * + * in this function we calculate the values of all the parameters. the + * parameters are converted to their most useful unit for the DSP + * algorithm, for example, number of samples instead of + * timecents. Some parameters keep their "perceptual" unit and + * conversion will be done in the DSP function. This is the case, for + * example, for the pitch since it is modulated by the controllers in + * cents. */ +int +fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) +{ + fluid_real_t x; + fluid_real_t q_db; + int i; + + int list_of_generators_to_initialize[35] = { + GEN_STARTADDROFS, /* SF2.01 page 48 #0 */ + GEN_ENDADDROFS, /* #1 */ + GEN_STARTLOOPADDROFS, /* #2 */ + GEN_ENDLOOPADDROFS, /* #3 */ + /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */ + GEN_MODLFOTOPITCH, /* #5 */ + GEN_VIBLFOTOPITCH, /* #6 */ + GEN_MODENVTOPITCH, /* #7 */ + GEN_FILTERFC, /* #8 */ + GEN_FILTERQ, /* #9 */ + GEN_MODLFOTOFILTERFC, /* #10 */ + GEN_MODENVTOFILTERFC, /* #11 */ + /* GEN_ENDADDRCOARSEOFS [1] #12 */ + GEN_MODLFOTOVOL, /* #13 */ + /* not defined #14 */ + GEN_CHORUSSEND, /* #15 */ + GEN_REVERBSEND, /* #16 */ + GEN_PAN, /* #17 */ + /* not defined #18 */ + /* not defined #19 */ + /* not defined #20 */ + GEN_MODLFODELAY, /* #21 */ + GEN_MODLFOFREQ, /* #22 */ + GEN_VIBLFODELAY, /* #23 */ + GEN_VIBLFOFREQ, /* #24 */ + GEN_MODENVDELAY, /* #25 */ + GEN_MODENVATTACK, /* #26 */ + GEN_MODENVHOLD, /* #27 */ + GEN_MODENVDECAY, /* #28 */ + /* GEN_MODENVSUSTAIN [1] #29 */ + GEN_MODENVRELEASE, /* #30 */ + /* GEN_KEYTOMODENVHOLD [1] #31 */ + /* GEN_KEYTOMODENVDECAY [1] #32 */ + GEN_VOLENVDELAY, /* #33 */ + GEN_VOLENVATTACK, /* #34 */ + GEN_VOLENVHOLD, /* #35 */ + GEN_VOLENVDECAY, /* #36 */ + /* GEN_VOLENVSUSTAIN [1] #37 */ + GEN_VOLENVRELEASE, /* #38 */ + /* GEN_KEYTOVOLENVHOLD [1] #39 */ + /* GEN_KEYTOVOLENVDECAY [1] #40 */ + /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */ + GEN_KEYNUM, /* #46 */ + GEN_VELOCITY, /* #47 */ + GEN_ATTENUATION, /* #48 */ + /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */ + /* GEN_COARSETUNE [1] #51 */ + /* GEN_FINETUNE [1] #52 */ + GEN_OVERRIDEROOTKEY, /* #58 */ + GEN_PITCH, /* --- */ + -1}; /* end-of-list marker */ + + /* When the voice is made ready for the synthesis process, a lot of + * voice-internal parameters have to be calculated. + * + * At this point, the sound font has already set the -nominal- value + * for all generators (excluding GEN_PITCH). Most generators can be + * modulated - they include a nominal value and an offset (which + * changes with velocity, note number, channel parameters like + * aftertouch, mod wheel...) Now this offset will be calculated as + * follows: + * + * - Process each modulator once. + * - Calculate its output value. + * - Find the target generator. + * - Add the output value to the modulation value of the generator. + * + * Note: The generators have been initialized with + * fluid_gen_set_default_values. + */ + + for (i = 0; i < voice->mod_count; i++) { + fluid_mod_t* mod = &voice->mod[i]; + fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice); + int dest_gen_index = mod->dest; + fluid_gen_t* dest_gen = &voice->gen[dest_gen_index]; + dest_gen->mod += modval; + /* fluid_dump_modulator(mod); */ + } + + /* The GEN_PITCH is a hack to fit the pitch bend controller into the + * modulator paradigm. Now the nominal pitch of the key is set. + * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a + * non-realtime parameter. So we don't allow modulation (as opposed + * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied, + * one key remains fixed. Here C3 (MIDI number 60) is used. + */ + if (fluid_channel_has_tuning(voice->channel)) { + /* pitch(60) + scale * (pitch(key) - pitch(60)) */ + #define __pitch(_k) fluid_tuning_get_pitch(tuning, _k) + fluid_tuning_t* tuning = fluid_channel_get_tuning(voice->channel); + voice->gen[GEN_PITCH].val = (__pitch(60) + (voice->gen[GEN_SCALETUNE].val / 100.0f * + (__pitch(voice->key) - __pitch(60)))); + } else { + voice->gen[GEN_PITCH].val = (voice->gen[GEN_SCALETUNE].val * (voice->key - 60.0f) + + 100.0f * 60.0f); + } + + /* Now the generators are initialized, nominal and modulation value. + * The voice parameters (which depend on generators) are calculated + * with fluid_voice_update_param. Processing the list of generator + * changes will calculate each voice parameter once. + * + * Note [1]: Some voice parameters depend on several generators. For + * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and + * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided + * by removing all but one generator from the list of voice + * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the + * initialisation list contains only GEN_XXX. + */ + + /* Calculate the voice parameter(s) dependent on each generator. */ + for (i = 0; list_of_generators_to_initialize[i] != -1; i++) { + fluid_voice_update_param(voice, list_of_generators_to_initialize[i]); + } + + /* Make an estimate on how loud this voice can get at any time (attenuation). */ + voice->min_attenuation_cB = fluid_voice_get_lower_boundary_for_attenuation(voice); + + return FLUID_OK; +} + +/* + * calculate_hold_decay_buffers + */ +int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, + int gen_key2base, int is_decay) +{ + /* Purpose: + * + * Returns the number of DSP loops, that correspond to the hold + * (is_decay=0) or decay (is_decay=1) time. + * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, + * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, + * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY + */ + + fluid_real_t timecents; + fluid_real_t seconds; + int buffers; + + /* SF2.01 section 8.4.3 # 31, 32, 39, 40 + * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. + * The unit of the generator is timecents per key number. + * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) + * will cause (60-72)*100=-1200 timecents of time variation. + * The time is cut in half. + */ + timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key)); + + /* Range checking */ + if (is_decay){ + /* SF 2.01 section 8.1.3 # 28, 36 */ + if (timecents > 8000.0) { + timecents = 8000.0; + } + } else { + /* SF 2.01 section 8.1.3 # 27, 35 */ + if (timecents > 5000) { + timecents = 5000.0; + } + /* SF 2.01 section 8.1.2 # 27, 35: + * The most negative number indicates no hold time + */ + if (timecents <= -32768.) { + return 0; + } + } + /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ + if (timecents < -12000.0) { + timecents = -12000.0; + } + + seconds = fluid_tc2sec(timecents); + /* Each DSP loop processes FLUID_BUFSIZE samples. */ + + /* round to next full number of buffers */ + buffers = (int)(((fluid_real_t)voice->output_rate * seconds) + / (fluid_real_t)FLUID_BUFSIZE + +0.5); + + return buffers; +} + +/* + * fluid_voice_update_param + * + * Purpose: + * + * The value of a generator (gen) has changed. (The different + * generators are listed in fluidsynth.h, or in SF2.01 page 48-49) + * Now the dependent 'voice' parameters are calculated. + * + * fluid_voice_update_param can be called during the setup of the + * voice (to calculate the initial value for a voice parameter), or + * during its operation (a generator has been changed due to + * real-time parameter modifications like pitch-bend). + * + * Note: The generator holds three values: The base value .val, an + * offset caused by modulators .mod, and an offset caused by the + * NRPN system. _GEN(voice, generator_enumerator) returns the sum + * of all three. + */ +void +fluid_voice_update_param(fluid_voice_t* voice, int gen) +{ + double q_dB; + fluid_real_t x; + fluid_real_t y; + unsigned int count; + + switch (gen) { + + case GEN_PAN: + /* range checking is done in the fluid_pan function */ + voice->pan = _GEN(voice, GEN_PAN); + voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f; + voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f; + break; + + case GEN_ATTENUATION: + voice->attenuation = _GEN(voice, GEN_ATTENUATION); + + /* Range: SF2.01 section 8.1.3 # 48 + * Motivation for range checking: + * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ + fluid_clip(voice->attenuation, 0.0, 1440.0); + break; + + /* The pitch is calculated from three different generators. + * Read comment in fluidsynth.h about GEN_PITCH. + */ + case GEN_PITCH: + case GEN_COARSETUNE: + case GEN_FINETUNE: + /* The testing for allowed range is done in 'fluid_ct2hz' */ + voice->pitch = (_GEN(voice, GEN_PITCH) + + 100.0f * _GEN(voice, GEN_COARSETUNE) + + _GEN(voice, GEN_FINETUNE)); + break; + + case GEN_REVERBSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f; + fluid_clip(voice->reverb_send, 0.0, 1.0); + voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f; + break; + + case GEN_CHORUSSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f; + fluid_clip(voice->chorus_send, 0.0, 1.0); + voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f; + break; + + case GEN_OVERRIDEROOTKEY: + /* This is a non-realtime parameter. Therefore the .mod part of the generator + * can be neglected. + * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount + * which offsets the original rate. This means that the fine tuning is + * inverted with respect to the root note (so subtract it, not add). + */ + if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) { //FIXME: use flag instead of -1 + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f + - voice->sample->pitchadj; + } else { + voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; + } + voice->root_pitch = fluid_ct2hz(voice->root_pitch); + if (voice->sample != NULL) { + voice->root_pitch *= (fluid_real_t) voice->output_rate / voice->sample->samplerate; + } + break; + + case GEN_FILTERFC: + /* The resonance frequency is converted from absolute cents to + * midicents .val and .mod are both used, this permits real-time + * modulation. The allowed range is tested in the 'fluid_ct2hz' + * function [PH,20021214] + */ + voice->fres = _GEN(voice, GEN_FILTERFC); + + /* The synthesis loop will have to recalculate the filter + * coefficients. */ + voice->last_fres = -1.0f; + break; + + case GEN_FILTERQ: + /* The generator contains 'centibels' (1/10 dB) => divide by 10 to + * obtain dB */ + q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f; + + /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ + fluid_clip(q_dB, 0.0f, 96.0f); + + /* Short version: Modify the Q definition in a way, that a Q of 0 + * dB leads to no resonance hump in the freq. response. + * + * Long version: From SF2.01, page 39, item 9 (initialFilterQ): + * "The gain at the cutoff frequency may be less than zero when + * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave + * q as it is, then this results in a 3 dB hump slightly below + * fc. At fc, the gain is exactly the DC gain (0 dB). What is + * (probably) meant here is that the filter does not show a + * resonance hump for q_dB=0. In this case, the corresponding + * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of + * attenuation at fc now. In this case Q_dB is the height of the + * resonance peak not over the DC gain, but over the frequency + * response of a non-resonant filter. This idea is implemented as + * follows: */ + q_dB -= 3.01f; + + /* The 'sound font' Q is defined in dB. The filter needs a linear + q. Convert. */ + voice->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f)); + + /* SF 2.01 page 59: + * + * The SoundFont specs ask for a gain reduction equal to half the + * height of the resonance peak (Q). For example, for a 10 dB + * resonance peak, the gain is reduced by 5 dB. This is done by + * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB + * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) + * The gain is later factored into the 'b' coefficients + * (numerator of the filter equation). This gain factor depends + * only on Q, so this is the right place to calculate it. + */ + voice->filter_gain = (fluid_real_t) (1.0 / sqrt(voice->q_lin)); + + /* The synthesis loop will have to recalculate the filter coefficients. */ + voice->last_fres = -1.; + break; + + case GEN_MODLFOTOPITCH: + voice->modlfo_to_pitch = _GEN(voice, GEN_MODLFOTOPITCH); + fluid_clip(voice->modlfo_to_pitch, -12000.0, 12000.0); + break; + + case GEN_MODLFOTOVOL: + voice->modlfo_to_vol = _GEN(voice, GEN_MODLFOTOVOL); + fluid_clip(voice->modlfo_to_vol, -960.0, 960.0); + break; + + case GEN_MODLFOTOFILTERFC: + voice->modlfo_to_fc = _GEN(voice, GEN_MODLFOTOFILTERFC); + fluid_clip(voice->modlfo_to_fc, -12000, 12000); + break; + + case GEN_MODLFODELAY: + x = _GEN(voice, GEN_MODLFODELAY); + fluid_clip(x, -12000.0f, 5000.0f); + voice->modlfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); + break; + + case GEN_MODLFOFREQ: + /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + x = _GEN(voice, GEN_MODLFOFREQ); + fluid_clip(x, -16000.0f, 4500.0f); + voice->modlfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); + break; + + case GEN_VIBLFOFREQ: + /* vib lfo + * + * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + x = _GEN(voice, GEN_VIBLFOFREQ); + fluid_clip(x, -16000.0f, 4500.0f); + voice->viblfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); + break; + + case GEN_VIBLFODELAY: + x = _GEN(voice,GEN_VIBLFODELAY); + fluid_clip(x, -12000.0f, 5000.0f); + voice->viblfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); + break; + + case GEN_VIBLFOTOPITCH: + voice->viblfo_to_pitch = _GEN(voice, GEN_VIBLFOTOPITCH); + fluid_clip(voice->viblfo_to_pitch, -12000.0, 12000.0); + break; + + case GEN_KEYNUM: + /* GEN_KEYNUM: SF2.01 page 46, item 46 + * + * If this generator is active, it forces the key number to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. + * */ + x = _GEN(voice, GEN_KEYNUM); + if (x >= 0){ + voice->key = x; + } + break; + + case GEN_VELOCITY: + /* GEN_VELOCITY: SF2.01 page 46, item 47 + * + * If this generator is active, it forces the velocity to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. */ + x = _GEN(voice, GEN_VELOCITY); + if (x > 0) { + voice->vel = x; + } + break; + + case GEN_MODENVTOPITCH: + voice->modenv_to_pitch = _GEN(voice, GEN_MODENVTOPITCH); + fluid_clip(voice->modenv_to_pitch, -12000.0, 12000.0); + break; + + case GEN_MODENVTOFILTERFC: + voice->modenv_to_fc = _GEN(voice,GEN_MODENVTOFILTERFC); + + /* Range: SF2.01 section 8.1.3 # 1 + * Motivation for range checking: + * Filter is reported to make funny noises now and then + */ + fluid_clip(voice->modenv_to_fc, -12000.0, 12000.0); + break; + + + /* sample start and ends points + * + * Range checking is initiated via the + * voice->check_sample_sanity flag, + * because it is impossible to check here: + * During the voice setup, all modulators are processed, while + * the voice is inactive. Therefore, illegal settings may + * occur during the setup (for example: First move the loop + * end point ahead of the loop start point => invalid, then + * move the loop start point forward => valid again. + */ + case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ + case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ + if (voice->sample != NULL) { + voice->start = (voice->sample->start + + (int) _GEN(voice, GEN_STARTADDROFS) + + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); + voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + } + break; + case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ + case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ + if (voice->sample != NULL) { + voice->end = (voice->sample->end + + (int) _GEN(voice, GEN_ENDADDROFS) + + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); + voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + } + break; + case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ + case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ + if (voice->sample != NULL) { + voice->loopstart = (voice->sample->loopstart + + (int) _GEN(voice, GEN_STARTLOOPADDROFS) + + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); + voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + } + break; + + case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ + case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ + if (voice->sample != NULL) { + voice->loopend = (voice->sample->loopend + + (int) _GEN(voice, GEN_ENDLOOPADDROFS) + + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); + voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; + } + break; + + /* Conversion functions differ in range limit */ +#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE) + + /* volume envelope + * + * - delay and hold times are converted to absolute number of samples + * - sustain is converted to its absolute value + * - attack, decay and release are converted to their increment per sample + */ + case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ + x = _GEN(voice, GEN_VOLENVDELAY); + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + voice->volenv_data[FLUID_VOICE_ENVDELAY].count = count; + voice->volenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; + voice->volenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; + break; + + case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ + x = _GEN(voice, GEN_VOLENVATTACK); + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + voice->volenv_data[FLUID_VOICE_ENVATTACK].count = count; + voice->volenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; + voice->volenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; + voice->volenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; + voice->volenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; + break; + + case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ + case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ + count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ + voice->volenv_data[FLUID_VOICE_ENVHOLD].count = count; + voice->volenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; + voice->volenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; + voice->volenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; + break; + + case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ + case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ + case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ + y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN); + fluid_clip(y, 0.0f, 1.0f); + count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ + voice->volenv_data[FLUID_VOICE_ENVDECAY].count = count; + voice->volenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; + voice->volenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; + voice->volenv_data[FLUID_VOICE_ENVDECAY].min = y; + voice->volenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; + break; + + case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ + x = _GEN(voice, GEN_VOLENVRELEASE); + fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + voice->volenv_data[FLUID_VOICE_ENVRELEASE].count = count; + voice->volenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; + voice->volenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0f; + voice->volenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; + voice->volenv_data[FLUID_VOICE_ENVRELEASE].max = 1.0f; + break; + + /* Modulation envelope */ + case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ + x = _GEN(voice, GEN_MODENVDELAY); + fluid_clip(x, -12000.0f, 5000.0f); + voice->modenv_data[FLUID_VOICE_ENVDELAY].count = NUM_BUFFERS_DELAY(x); + voice->modenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; + voice->modenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; + break; + + case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ + x = _GEN(voice, GEN_MODENVATTACK); + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + voice->modenv_data[FLUID_VOICE_ENVATTACK].count = count; + voice->modenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; + voice->modenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; + voice->modenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; + voice->modenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; + break; + + case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ + case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ + voice->modenv_data[FLUID_VOICE_ENVHOLD].count = count; + voice->modenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; + voice->modenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; + voice->modenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; + break; + + case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ + case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ + case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ + y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN); + fluid_clip(y, 0.0f, 1.0f); + voice->modenv_data[FLUID_VOICE_ENVDECAY].count = count; + voice->modenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; + voice->modenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; + voice->modenv_data[FLUID_VOICE_ENVDECAY].min = y; + voice->modenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; + break; + + case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ + x = _GEN(voice, GEN_MODENVRELEASE); + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + voice->modenv_data[FLUID_VOICE_ENVRELEASE].count = count; + voice->modenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; + voice->modenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0; + voice->modenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; + voice->modenv_data[FLUID_VOICE_ENVRELEASE].max = 2.0f; + break; + + } /* switch gen */ +} + +/** + * fluid_voice_modulate + * + * In this implementation, I want to make sure that all controllers + * are event based: the parameter values of the DSP algorithm should + * only be updates when a controller event arrived and not at every + * iteration of the audio cycle (which would probably be feasible if + * the synth was made in silicon). + * + * The update is done in three steps: + * + * - first, we look for all the modulators that have the changed + * controller as a source. This will yield a list of generators that + * will be changed because of the controller event. + * + * - For every changed generator, calculate its new value. This is the + * sum of its original value plus the values of al the attached + * modulators. + * + * - For every changed generator, convert its value to the correct + * unit of the corresponding DSP parameter + * + * @fn int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl, int val) + * @param voice the synthesis voice + * @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...) + * @param ctrl the control number + * */ +int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl) +{ + int i, k; + fluid_mod_t* mod; + int gen; + fluid_real_t modval; + +/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ + + for (i = 0; i < voice->mod_count; i++) { + + mod = &voice->mod[i]; + + /* step 1: find all the modulators that have the changed controller + * as input source. */ + if (fluid_mod_has_source(mod, cc, ctrl)) { + + gen = fluid_mod_get_dest(mod); + modval = 0.0; + + /* step 2: for every changed modulator, calculate the modulation + * value of its associated generator */ + for (k = 0; k < voice->mod_count; k++) { + if (fluid_mod_has_dest(&voice->mod[k], gen)) { + modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice); + } + } + + fluid_gen_set_mod(&voice->gen[gen], modval); + + /* step 3: now that we have the new value of the generator, + * recalculate the parameter values that are derived from the + * generator */ + fluid_voice_update_param(voice, gen); + } + } + return FLUID_OK; +} + +/** + * fluid_voice_modulate_all + * + * Update all the modulators. This function is called after a + * ALL_CTRL_OFF MIDI message has been received (CC 121). + * + */ +int fluid_voice_modulate_all(fluid_voice_t* voice) +{ + fluid_mod_t* mod; + int i, k, gen; + fluid_real_t modval; + + /* Loop through all the modulators. + + FIXME: we should loop through the set of generators instead of + the set of modulators. We risk to call 'fluid_voice_update_param' + several times for the same generator if several modulators have + that generator as destination. It's not an error, just a wast of + energy (think polution, global warming, unhappy musicians, + ...) */ + + for (i = 0; i < voice->mod_count; i++) { + + mod = &voice->mod[i]; + gen = fluid_mod_get_dest(mod); + modval = 0.0; + + /* Accumulate the modulation values of all the modulators with + * destination generator 'gen' */ + for (k = 0; k < voice->mod_count; k++) { + if (fluid_mod_has_dest(&voice->mod[k], gen)) { + modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice); + } + } + + fluid_gen_set_mod(&voice->gen[gen], modval); + + /* Update the parameter values that are depend on the generator + * 'gen' */ + fluid_voice_update_param(voice, gen); + } + + return FLUID_OK; +} + +/* + * fluid_voice_noteoff + */ +int +fluid_voice_noteoff(fluid_voice_t* voice) +{ + fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref); + + if (voice->channel && fluid_channel_sustained(voice->channel)) { + voice->status = FLUID_VOICE_SUSTAINED; + } else { + if (voice->volenv_section == FLUID_VOICE_ENVATTACK) { + /* A voice is turned off during the attack section of the volume + * envelope. The attack section ramps up linearly with + * amplitude. The other sections use logarithmic scaling. Calculate new + * volenv_val to achieve equievalent amplitude during the release phase + * for seamless volume transition. + */ + if (voice->volenv_val > 0){ + fluid_real_t lfo = voice->modlfo_val * -voice->modlfo_to_vol; + fluid_real_t amp = voice->volenv_val * pow (10.0, lfo / -200); + fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); + fluid_clip (env_value, 0.0, 1.0); + voice->volenv_val = env_value; + } + } + voice->volenv_section = FLUID_VOICE_ENVRELEASE; + voice->volenv_count = 0; + voice->modenv_section = FLUID_VOICE_ENVRELEASE; + voice->modenv_count = 0; + } + + return FLUID_OK; +} + +/* + * fluid_voice_kill_excl + * + * Percussion sounds can be mutually exclusive: for example, a 'closed + * hihat' sound will terminate an 'open hihat' sound ringing at the + * same time. This behaviour is modeled using 'exclusive classes', + * turning on a voice with an exclusive class other than 0 will kill + * all other voices having that exclusive class within the same preset + * or channel. fluid_voice_kill_excl gets called, when 'voice' is to + * be killed for that reason. + */ +int +fluid_voice_kill_excl(fluid_voice_t* voice){ + + if (!_PLAYING(voice)) { + return FLUID_OK; + } + + /* Turn off the exclusive class information for this voice, + so that it doesn't get killed twice + */ + fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); + + /* If the voice is not yet in release state, put it into release state */ + if (voice->volenv_section != FLUID_VOICE_ENVRELEASE){ + voice->volenv_section = FLUID_VOICE_ENVRELEASE; + voice->volenv_count = 0; + voice->modenv_section = FLUID_VOICE_ENVRELEASE; + voice->modenv_count = 0; + } + + /* Speed up the volume envelope */ + /* The value was found through listening tests with hi-hat samples. */ + fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_VOLENVRELEASE); + + /* Speed up the modulation envelope */ + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_MODENVRELEASE); + + return FLUID_OK; +} + +/* + * fluid_voice_off + * + * Purpose: + * Turns off a voice, meaning that it is not processed + * anymore by the DSP loop. + */ +int +fluid_voice_off(fluid_voice_t* voice) +{ + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); + + voice->chan = NO_CHANNEL; + voice->volenv_section = FLUID_VOICE_ENVFINISHED; + voice->volenv_count = 0; + voice->modenv_section = FLUID_VOICE_ENVFINISHED; + voice->modenv_count = 0; + voice->status = FLUID_VOICE_OFF; + + /* Decrement the reference count of the sample. */ + fluid_sample_decr_ref(voice->sample); + + voice->sample = NULL; + + return FLUID_OK; +} + +/* + * fluid_voice_add_mod + * + * Adds a modulator to the voice. "mode" indicates, what to do, if + * an identical modulator exists already. + * + * mode == FLUID_VOICE_ADD: Identical modulators on preset level are added + * mode == FLUID_VOICE_OVERWRITE: Identical modulators on instrument level are overwritten + * mode == FLUID_VOICE_DEFAULT: This is a default modulator, there can be no identical modulator. + * Don't check. + */ +void +fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode) +{ + int i; + + /* + * Some soundfonts come with a huge number of non-standard + * controllers, because they have been designed for one particular + * sound card. Discard them, maybe print a warning. + */ + + if (((mod->flags1 & FLUID_MOD_CC) == 0) + && ((mod->src1 != 0) /* SF2.01 section 8.2.1: Constant value */ + && (mod->src1 != 2) /* Note-on velocity */ + && (mod->src1 != 3) /* Note-on key number */ + && (mod->src1 != 10) /* Poly pressure */ + && (mod->src1 != 13) /* Channel pressure */ + && (mod->src1 != 14) /* Pitch wheel */ + && (mod->src1 != 16))) { /* Pitch wheel sensitivity */ + FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); + return; + } + + if (mode == FLUID_VOICE_ADD) { + + /* if identical modulator exists, add them */ + for (i = 0; i < voice->mod_count; i++) { + if (fluid_mod_test_identity(&voice->mod[i], mod)) { + // printf("Adding modulator...\n"); + voice->mod[i].amount += mod->amount; + return; + } + } + + } else if (mode == FLUID_VOICE_OVERWRITE) { + + /* if identical modulator exists, replace it (only the amount has to be changed) */ + for (i = 0; i < voice->mod_count; i++) { + if (fluid_mod_test_identity(&voice->mod[i], mod)) { + // printf("Replacing modulator...amount is %f\n",mod->amount); + voice->mod[i].amount = mod->amount; + return; + } + } + } + + /* Add a new modulator (No existing modulator to add / overwrite). + Also, default modulators (FLUID_VOICE_DEFAULT) are added without + checking, if the same modulator already exists. */ + if (voice->mod_count < FLUID_NUM_MOD) { + fluid_mod_clone(&voice->mod[voice->mod_count++], mod); + } +} + +unsigned int fluid_voice_get_id(fluid_voice_t* voice) +{ + return voice->id; +} + +int fluid_voice_is_playing(fluid_voice_t* voice) +{ + return _PLAYING(voice); +} + +/* + * fluid_voice_get_lower_boundary_for_attenuation + * + * Purpose: + * + * A lower boundary for the attenuation (as in 'the minimum + * attenuation of this voice, with volume pedals, modulators + * etc. resulting in minimum attenuation, cannot fall below x cB) is + * calculated. This has to be called during fluid_voice_init, after + * all modulators have been run on the voice once. Also, + * voice->attenuation has to be initialized. + */ +fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice) +{ + int i; + fluid_mod_t* mod; + fluid_real_t possible_att_reduction_cB=0; + fluid_real_t lower_bound; + + for (i = 0; i < voice->mod_count; i++) { + mod = &voice->mod[i]; + + /* Modulator has attenuation as target and can change over time? */ + if ((mod->dest == GEN_ATTENUATION) + && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) { + + fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice); + fluid_real_t v = fabs(mod->amount); + + if ((mod->src1 == FLUID_MOD_PITCHWHEEL) + || (mod->flags1 & FLUID_MOD_BIPOLAR) + || (mod->flags2 & FLUID_MOD_BIPOLAR) + || (mod->amount < 0)) { + /* Can this modulator produce a negative contribution? */ + v *= -1.0; + } else { + /* No negative value possible. But still, the minimum contribution is 0. */ + v = 0; + } + + /* For example: + * - current_val=100 + * - min_val=-4000 + * - possible_att_reduction_cB += 4100 + */ + if (current_val > v){ + possible_att_reduction_cB += (current_val - v); + } + } + } + + lower_bound = voice->attenuation-possible_att_reduction_cB; + + /* SF2.01 specs do not allow negative attenuation */ + if (lower_bound < 0) { + lower_bound = 0; + } + return lower_bound; +} + + +/* Purpose: + * + * Make sure, that sample start / end point and loop points are in + * proper order. When starting up, calculate the initial phase. + */ +void fluid_voice_check_sample_sanity(fluid_voice_t* voice) +{ + int min_index_nonloop=(int) voice->sample->start; + int max_index_nonloop=(int) voice->sample->end; + + /* make sure we have enough samples surrounding the loop */ + int min_index_loop=(int) voice->sample->start + FLUID_MIN_LOOP_PAD; + int max_index_loop=(int) voice->sample->end - FLUID_MIN_LOOP_PAD; + fluid_check_fpe("voice_check_sample_sanity start"); + + if (!voice->check_sample_sanity_flag){ + return; + } + +#if 0 + printf("Sample from %i to %i\n",voice->sample->start, voice->sample->end); + printf("Sample loop from %i %i\n",voice->sample->loopstart, voice->sample->loopend); + printf("Playback from %i to %i\n", voice->start, voice->end); + printf("Playback loop from %i to %i\n",voice->loopstart, voice->loopend); +#endif + + /* Keep the start point within the sample data */ + if (voice->start < min_index_nonloop){ + voice->start = min_index_nonloop; + } else if (voice->start > max_index_nonloop){ + voice->start = max_index_nonloop; + } + + /* Keep the end point within the sample data */ + if (voice->end < min_index_nonloop){ + voice->end = min_index_nonloop; + } else if (voice->end > max_index_nonloop){ + voice->end = max_index_nonloop; + } + + /* Keep start and end point in the right order */ + if (voice->start > voice->end){ + int temp = voice->start; + voice->start = voice->end; + voice->end = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ + } + + /* Zero length? */ + if (voice->start == voice->end){ + fluid_voice_off(voice); + return; + } + + if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) + || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { + /* Keep the loop start point within the sample data */ + if (voice->loopstart < min_index_loop){ + voice->loopstart = min_index_loop; + } else if (voice->loopstart > max_index_loop){ + voice->loopstart = max_index_loop; + } + + /* Keep the loop end point within the sample data */ + if (voice->loopend < min_index_loop){ + voice->loopend = min_index_loop; + } else if (voice->loopend > max_index_loop){ + voice->loopend = max_index_loop; + } + + /* Keep loop start and end point in the right order */ + if (voice->loopstart > voice->loopend){ + int temp = voice->loopstart; + voice->loopstart = voice->loopend; + voice->loopend = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ + } + + /* Loop too short? Then don't loop. */ + if (voice->loopend < voice->loopstart + FLUID_MIN_LOOP_SIZE){ + voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; + } + + /* The loop points may have changed. Obtain a new estimate for the loop volume. */ + /* Is the voice loop within the sample loop? */ + if ((int)voice->loopstart >= (int)voice->sample->loopstart + && (int)voice->loopend <= (int)voice->sample->loopend){ + /* Is there a valid peak amplitude available for the loop? */ + if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){ + voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain; + } else { + /* Worst case */ + voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop; + }; + }; + + } /* if sample mode is looped */ + + /* Run startup specific code (only once, when the voice is started) */ + if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ + if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ + if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) + || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){ + voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; + } + } + + /* Set the initial phase of the voice (using the result from the + start offset modulators). */ + fluid_phase_set_int(voice->phase, voice->start); + } /* if startup */ + + /* Is this voice run in loop mode, or does it run straight to the + end of the waveform data? */ + if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE)) + || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { + /* Yes, it will loop as soon as it reaches the loop point. In + * this case we must prevent, that the playback pointer (phase) + * happens to end up beyond the 2nd loop point, because the + * point has moved. The DSP algorithm is unable to cope with + * that situation. So if the phase is beyond the 2nd loop + * point, set it to the start of the loop. No way to avoid some + * noise here. Note: If the sample pointer ends up -before the + * first loop point- instead, then the DSP loop will just play + * the sample, enter the loop and proceed as expected => no + * actions required. + */ + int index_in_sample = fluid_phase_index(voice->phase); + if (index_in_sample >= voice->loopend){ + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ + fluid_phase_set_int(voice->phase, voice->loopstart); + } + } +/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */ + + /* Sample sanity has been assured. Don't check again, until some + sample parameter is changed by modulation. */ + voice->check_sample_sanity_flag=0; +#if 0 + printf("Sane? playback loop from %i to %i\n", voice->loopstart, voice->loopend); +#endif + fluid_check_fpe("voice_check_sample_sanity"); +} + + +int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) +{ + voice->gen[gen].nrpn = nrpn_value; + voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET; + fluid_voice_update_param(voice, gen); + return FLUID_OK; +} + +int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) +{ + /* avoid division by zero*/ + if (gain < 0.0000001){ + gain = 0.0000001; + } + + voice->synth_gain = gain; + voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f; + voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f; + voice->amp_reverb = voice->reverb_send * gain / 32768.0f; + voice->amp_chorus = voice->chorus_send * gain / 32768.0f; + + return FLUID_OK; +} + +/* - Scan the loop + * - determine the peak level + * - Calculate, what factor will make the loop inaudible + * - Store in sample + */ +int fluid_voice_optimize_sample(fluid_sample_t* s) +{ + signed short peak_max = 0; + signed short peak_min = 0; + signed short peak; + fluid_real_t normalized_amplitude_during_loop; + double result; + int i; + + /* ignore ROM and other(?) invalid samples */ + if (!s->valid) return (FLUID_OK); + + if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */ + /* Scan the loop */ + for (i = (int)s->loopstart; i < (int) s->loopend; i ++){ + signed short val = s->data[i]; + if (val > peak_max) { + peak_max = val; + } else if (val < peak_min) { + peak_min = val; + } + } + + /* Determine the peak level */ + if (peak_max >- peak_min){ + peak = peak_max; + } else { + peak =- peak_min; + }; + if (peak == 0){ + /* Avoid division by zero */ + peak = 1; + }; + + /* Calculate what factor will make the loop inaudible + * For example: Take a peak of 3277 (10 % of 32768). The + * normalized amplitude is 0.1 (10 % of 32768). An amplitude + * factor of 0.0001 (as opposed to the default 0.00001) will + * drop this sample to the noise floor. + */ + + /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ + normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.; + result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; + + /* Store in sample */ + s->amplitude_that_reaches_noise_floor = (double)result; + s->amplitude_that_reaches_noise_floor_is_valid = 1; +#if 0 + printf("Sample peak detection: factor %f\n", (double)result); +#endif + }; + return FLUID_OK; +} diff --git a/src/libs/fluidsynth/src/fluid_voice.h b/src/libs/fluidsynth/src/fluid_voice.h new file mode 100644 index 0000000000..6a4a60b5a2 --- /dev/null +++ b/src/libs/fluidsynth/src/fluid_voice.h @@ -0,0 +1,288 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUID_VOICE_H +#define _FLUID_VOICE_H + +#include "fluid_phase.h" +#include "fluid_gen.h" +#include "fluid_mod.h" + +#define NO_CHANNEL 0xff + +enum fluid_voice_status +{ + FLUID_VOICE_CLEAN, + FLUID_VOICE_ON, + FLUID_VOICE_SUSTAINED, + FLUID_VOICE_OFF +}; + + +/* + * envelope data + */ +struct _fluid_env_data_t { + unsigned int count; + fluid_real_t coeff; + fluid_real_t incr; + fluid_real_t min; + fluid_real_t max; +}; + +/* Indices for envelope tables */ +enum fluid_voice_envelope_index_t{ + FLUID_VOICE_ENVDELAY, + FLUID_VOICE_ENVATTACK, + FLUID_VOICE_ENVHOLD, + FLUID_VOICE_ENVDECAY, + FLUID_VOICE_ENVSUSTAIN, + FLUID_VOICE_ENVRELEASE, + FLUID_VOICE_ENVFINISHED, + FLUID_VOICE_ENVLAST +}; + +/* + * interpolation data + */ +typedef struct { + fluid_real_t a0, a1, a2, a3; +/* signed int c0, c1, c2, c3; */ +} fluid_interp_coeff_t; + +/* + * fluid_voice_t + */ +struct _fluid_voice_t +{ + unsigned int id; /* the id is incremented for every new noteon. + it's used for noteoff's */ + unsigned char status; + unsigned char chan; /* the channel number, quick access for channel messages */ + unsigned char key; /* the key, quick acces for noteoff */ + unsigned char vel; /* the velocity */ + fluid_channel_t* channel; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t mod[FLUID_NUM_MOD]; + int mod_count; + int has_looped; /* Flag that is set as soon as the first loop is completed. */ + fluid_sample_t* sample; + int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters + have to be checked. */ +#if 0 + /* Instead of keeping a pointer to a fluid_sample_t structure, + * I think it would be better to copy the sample data in the + * voice structure. SoundFont loader then do not have to + * allocate and maintain the fluid_sample_t structure. [PH] + * + * The notify callback may be used also for streaming samples. + */ + short* sample_data; /* pointer to the sample data */ + int sample_data_offset; /* the offset of data[0] in the whole sample */ + int sample_data_length; /* the length of the data array */ + unsigned int sample_start; + unsigned int sample_end; + unsigned int sample_loopstart; + unsigned int sample_loopend; + unsigned int sample_rate; + int sample_origpitch; + int sample_pitchadj; + int sample_type; + int (*sample_notify)(fluid_voice_t* voice, int reason); + void* sample_userdata; +#endif + + /* basic parameters */ + fluid_real_t output_rate; /* the sample rate of the synthesizer */ + + unsigned int start_time; + unsigned int ticks; + + fluid_real_t amp; /* the linear amplitude */ + fluid_phase_t phase; /* the phase of the sample wave */ + +#if 0 + fluid_real_t incr; /* the phase increment for the next 64 samples [NEW, PH] */ +#endif + + /* basic parameters */ + fluid_real_t pitch; /* the pitch in midicents */ + fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation + * during the lifetime of the voice */ + fluid_real_t root_pitch; + + /* sample and loop start and end points (offset in sample memory). */ + int start; + int end; + int loopstart; + int loopend; + + /* master gain */ + fluid_real_t synth_gain; + + /* vol env */ + fluid_env_data_t volenv_data[FLUID_VOICE_ENVLAST]; + unsigned int volenv_count; + int volenv_section; + fluid_real_t volenv_val; + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; + + /* mod env */ + fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST]; + unsigned int modenv_count; + int modenv_section; + fluid_real_t modenv_val; /* the value of the modulation envelope */ + fluid_real_t modenv_to_fc; + fluid_real_t modenv_to_pitch; + + /* mod lfo */ + fluid_real_t modlfo_val; /* the value of the modulation LFO */ + unsigned int modlfo_delay; /* the delay of the lfo in samples */ + fluid_real_t modlfo_incr; /* the lfo frequency is converted to a per-buffer increment */ + fluid_real_t modlfo_to_fc; + fluid_real_t modlfo_to_pitch; + fluid_real_t modlfo_to_vol; + + /* vib lfo */ + fluid_real_t viblfo_val; /* the value of the vibrato LFO */ + unsigned int viblfo_delay; /* the delay of the lfo in samples */ + fluid_real_t viblfo_incr; /* the lfo frequency is converted to a per-buffer increment */ + fluid_real_t viblfo_to_pitch; + + /* resonant filter */ + fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */ + fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */ + /* Serves as a flag: A deviation between fres and last_fres */ + /* indicates, that the filter has to be recalculated. */ + fluid_real_t q_lin; /* the q-factor on a linear scale */ + fluid_real_t filter_gain; /* Gain correction factor, depends on q */ + fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ + int filter_startup; /* Flag: If set, the filter will be set directly. + Else it changes smoothly. */ + + /* filter coefficients */ + /* The coefficients are normalized to a0. */ + /* b0 and b2 are identical => b02 */ + fluid_real_t b02; /* b0 / a0 */ + fluid_real_t b1; /* b1 / a0 */ + fluid_real_t a1; /* a0 / a0 */ + fluid_real_t a2; /* a1 / a0 */ + + fluid_real_t b02_incr; + fluid_real_t b1_incr; + fluid_real_t a1_incr; + fluid_real_t a2_incr; + int filter_coeff_incr_count; + + /* pan */ + fluid_real_t pan; + fluid_real_t amp_left; + fluid_real_t amp_right; + + /* reverb */ + fluid_real_t reverb_send; + fluid_real_t amp_reverb; + + /* chorus */ + fluid_real_t chorus_send; + fluid_real_t amp_chorus; + + /* interpolation method, as in fluid_interp in fluidsynth.h */ + int interp_method; + + /* for debugging */ + int debug; + double ref; +}; + + +fluid_voice_t* new_fluid_voice(fluid_real_t output_rate); +int delete_fluid_voice(fluid_voice_t* voice); + +void fluid_voice_start(fluid_voice_t* voice); + +int fluid_voice_write(fluid_voice_t* voice, + fluid_real_t* left, fluid_real_t* right, + fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); + +int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, + fluid_channel_t* channel, int key, int vel, + unsigned int id, unsigned int time, fluid_real_t gain); + +int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl); +int fluid_voice_modulate_all(fluid_voice_t* voice); + +/** Set the NRPN value of a generator. */ +int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs); + + +/** Set the gain. */ +int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain); + + +/** Update all the synthesis parameters, which depend on generator + 'gen'. This is only necessary after changing a generator of an + already operating voice. Most applications will not need this + function.*/ +void fluid_voice_update_param(fluid_voice_t* voice, int gen); + +int fluid_voice_noteoff(fluid_voice_t* voice); +int fluid_voice_off(fluid_voice_t* voice); +int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice); +fluid_channel_t* fluid_voice_get_channel(fluid_voice_t* voice); +int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, + int gen_key2base, int is_decay); +int fluid_voice_kill_excl(fluid_voice_t* voice); +fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); +fluid_real_t fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice); +void fluid_voice_check_sample_sanity(fluid_voice_t* voice); + +#define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); } +#define fluid_voice_get_chan(_voice) (_voice)->chan + + +#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED)) + +/* A voice is 'ON', if it has not yet received a noteoff + * event. Sending a noteoff event will advance the envelopes to + * section 5 (release). */ +#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && (voice)->volenv_section < FLUID_VOICE_ENVRELEASE) +#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED) +#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)) +#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) +#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) + + +fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); + +#define _GEN(_voice, _n) \ + ((fluid_real_t)(_voice)->gen[_n].val \ + + (fluid_real_t)(_voice)->gen[_n].mod \ + + (fluid_real_t)(_voice)->gen[_n].nrpn) + +#define FLUID_SAMPLESANITY_CHECK (1 << 0) +#define FLUID_SAMPLESANITY_STARTUP (1 << 1) +void fluid_voice_config(void); + + +#endif /* _FLUID_VOICE_H */ diff --git a/src/libs/fluidsynth/src/fluidsynth.c b/src/libs/fluidsynth/src/fluidsynth.c new file mode 100644 index 0000000000..1f2297c008 --- /dev/null +++ b/src/libs/fluidsynth/src/fluidsynth.c @@ -0,0 +1,705 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include "config.h" + +#include +#include +#include + +#include "fluidsynth_priv.h" + +#if !defined(WIN32) && !defined(MACINTOSH) +#define _GNU_SOURCE +#include +#endif + +#if defined(WIN32) +#include +#endif + +#include "fluidsynth.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(WIN32) && !defined(MINGW32) +#include "config_win32.h" +#endif + +#ifdef HAVE_SIGNAL_H +#include "signal.h" +#endif + +#include "fluid_lash.h" + +#ifndef WITH_MIDI +#define WITH_MIDI 1 +#endif + +/* default audio fragment count (if none specified) */ +#ifdef WIN32 +#define DEFAULT_FRAG_COUNT 32 +#else +#define DEFAULT_FRAG_COUNT 16 +#endif + +void print_usage(void); +void print_help(void); +void print_welcome(void); + +static fluid_cmd_handler_t* newclient(void* data, char* addr); + +/* + * the globals + */ +fluid_cmd_handler_t* cmd_handler = NULL; +int option_help = 0; /* set to 1 if "-o help" is specified */ + +/* + * support for the getopt function + */ +#if !defined(WIN32) && !defined(MACINTOSH) +#define GETOPT_SUPPORT 1 +int getopt(int argc, char * const argv[], const char *optstring); +extern char *optarg; +extern int optind, opterr, optopt; +#endif + + +/* process_o_cmd_line_option + * + * Purpose: + * Process a command line option -o setting=value, + * for example: -o synth.polyhony=16 + */ +void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg){ + char* val; + for (val = optarg; *val != '\0'; val++) { + if (*val == '=') { + *val++ = 0; + break; + } + } + + /* did user request list of settings */ + if (strcmp (optarg, "help") == 0) + { + option_help = 1; + return; + } + + /* At this point: + * optarg => "synth.polyphony" + * val => "16" + */ + switch(fluid_settings_get_type(settings, optarg)){ + case FLUID_NUM_TYPE: + if (fluid_settings_setnum(settings, optarg, atof(val))){ + break; + }; + case FLUID_INT_TYPE: + if (fluid_settings_setint(settings, optarg, atoi(val))){ + break; + }; + case FLUID_STR_TYPE: + if (fluid_settings_setstr(settings, optarg, val)){ + break; + }; + default: + fprintf (stderr, "Settings argument on command line: Failed to set \"%s\" to \"%s\".\n" + "Most likely the parameter \"%s\" does not exist.\n", optarg, val, optarg); + } +} + +static void +print_pretty_int (int i) +{ + if (i == INT_MAX) printf ("MAXINT"); + else if (i == INT_MIN) printf ("MININT"); + else printf ("%d", i); +} + +/* fluid_settings_foreach function for displaying option help "-o help" */ +static void +settings_foreach_func (void *data, char *name, int type) +{ + fluid_settings_t *settings = (fluid_settings_t *)data; + double dmin, dmax, ddef; + int imin, imax, idef; + char *defstr; + + switch (type) + { + case FLUID_NUM_TYPE: + fluid_settings_getnum_range (settings, name, &dmin, &dmax); + ddef = fluid_settings_getnum_default (settings, name); + printf ("%-24s FLOAT [min=%0.3f, max=%0.3f, def=%0.3f]\n", + name, dmin, dmax, ddef); + break; + case FLUID_INT_TYPE: + fluid_settings_getint_range (settings, name, &imin, &imax); + idef = fluid_settings_getint_default (settings, name); + printf ("%-24s INT [min=", name); + print_pretty_int (imin); + printf (", max="); + print_pretty_int (imax); + printf (", def="); + print_pretty_int (idef); + printf ("]\n"); + break; + case FLUID_STR_TYPE: + defstr = fluid_settings_getstr_default (settings, name); + printf ("%-24s STR", name); + if (defstr) printf (" [def='%s']\n", defstr); + else printf ("\n"); + break; + case FLUID_SET_TYPE: + printf ("%-24s SET\n", name); + break; + } +} + + +#ifdef HAVE_SIGNAL_H +/* + * handle_signal + */ +void handle_signal(int sig_num) +{ +} +#endif + + +/* + * main + */ +int main(int argc, char** argv) +{ + fluid_settings_t* settings; + int arg1 = 1; + char buf[512]; + int c, i, fragcount = DEFAULT_FRAG_COUNT; + int interactive = 1; + int midi_in = 1; + fluid_player_t* player = NULL; + fluid_midi_router_t* router = NULL; + fluid_midi_driver_t* mdriver = NULL; + fluid_audio_driver_t* adriver = NULL; + fluid_synth_t* synth = NULL; + fluid_server_t* server = NULL; + char* midi_id = NULL; + char* midi_driver = NULL; + char* midi_device = NULL; + char* config_file = NULL; + int audio_groups = 0; + int audio_channels = 0; + int with_server = 0; + int dump = 0; + int connect_lash = 1; + char *optchars = "a:C:c:df:G:g:hijK:L:lm:no:R:r:sVvz:"; +#ifdef LASH_ENABLED + int enabled_lash = 0; /* set to TRUE if lash gets enabled */ + fluid_lash_args_t *lash_args; + + lash_args = fluid_lash_extract_args (&argc, &argv); +#endif + + settings = new_fluid_settings(); + +#ifdef GETOPT_SUPPORT /* pre section of GETOPT supported argument handling */ + opterr = 0; + + while (1) { + int option_index = 0; + + static struct option long_options[] = { + {"audio-bufcount", 1, 0, 'c'}, + {"audio-bufsize", 1, 0, 'z'}, + {"audio-channels", 1, 0, 'L'}, + {"audio-driver", 1, 0, 'a'}, + {"audio-groups", 1, 0, 'G'}, + {"chorus", 1, 0, 'C'}, + {"connect-jack-outputs", 0, 0, 'j'}, + {"disable-lash", 0, 0, 'l'}, + {"dump", 0, 0, 'd'}, + {"gain", 1, 0, 'g'}, + {"help", 0, 0, 'h'}, + {"load-config", 1, 0, 'f'}, + {"midi-channels", 1, 0, 'K'}, + {"midi-driver", 1, 0, 'm'}, + {"no-midi-in", 0, 0, 'n'}, + {"no-shell", 0, 0, 'i'}, + {"option", 1, 0, 'o'}, + {"reverb", 1, 0, 'R'}, + {"sample-rate", 1, 0, 'r'}, + {"server", 0, 0, 's'}, + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, optchars, long_options, &option_index); + if (c == -1) { + break; + } +#else /* "pre" section to non getopt argument handling */ + for (i = 1; i < argc; i++) { + char *optarg; + + /* Skip non switch arguments (assume they are file names) */ + if ((argv[i][0] != '-') || (argv[i][1] == '\0')) continue; + + c = argv[i][1]; + + optarg = strchr (optchars, c); /* find the option character in optchars */ + if (optarg && optarg[1] == ':') /* colon follows if switch argument expected */ + { + if (++i >= argc) + { + printf ("Option -%c requires an argument\n", c); + print_usage(); + exit(0); + } + else + { + optarg = argv[i]; + if (optarg[0] == '-') + { + printf ("Expected argument to option -%c found switch instead\n", c); + print_usage(); + exit(0); + } + } + } + else optarg = ""; +#endif + + switch (c) { +#ifdef GETOPT_SUPPORT + case 0: /* shouldn't normally happen, a long option's flag is set to NULL */ + printf ("option %s", long_options[option_index].name); + if (optarg) { + printf (" with arg %s", optarg); + } + printf ("\n"); + break; +#endif + case 'a': + fluid_settings_setstr(settings, "audio.driver", optarg); + break; + case 'C': + if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + fluid_settings_setstr(settings, "synth.chorus.active", "no"); + } else { + fluid_settings_setstr(settings, "synth.chorus.active", "yes"); + } + break; + case 'c': + fluid_settings_setint(settings, "audio.periods", atoi(optarg)); + break; + case 'd': + fluid_settings_setstr(settings, "synth.dump", "yes"); + dump = 1; + break; + case 'f': + config_file = optarg; + break; + case 'G': + audio_groups = atoi(optarg); + break; + case 'g': + fluid_settings_setnum(settings, "synth.gain", atof(optarg)); + break; + case 'h': + print_help(); + break; + case 'i': + interactive = 0; + break; + case 'j': + fluid_settings_setint(settings, "audio.jack.autoconnect", 1); + break; + case 'K': + fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg)); + break; + case 'L': + audio_channels = atoi(optarg); + fluid_settings_setint(settings, "synth.audio-channels", audio_channels); + break; + case 'l': /* disable LASH */ + connect_lash = 0; + break; + case 'm': + fluid_settings_setstr(settings, "midi.driver", optarg); + break; + case 'n': + midi_in = 0; + break; + case 'o': + process_o_cmd_line_option(settings, optarg); + break; + case 'R': + if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { + fluid_settings_setstr(settings, "synth.reverb.active", "no"); + } else { + fluid_settings_setstr(settings, "synth.reverb.active", "yes"); + } + break; + case 'r': + fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg)); + break; + case 's': + with_server = 1; + break; + case 'V': + printf("FluidSynth %s\n", VERSION); + exit (0); + break; + case 'v': + fluid_settings_setstr(settings, "synth.verbose", "yes"); + break; + case 'z': + fluid_settings_setint(settings, "audio.period-size", atoi(optarg)); + break; +#ifdef GETOPT_SUPPORT + case '?': + printf ("Unknown option %c\n", optopt); + print_usage(); + exit(0); + break; + default: + printf ("?? getopt returned character code 0%o ??\n", c); + break; +#else /* Non getopt default case */ + default: + printf ("Unknown switch '%c'\n", c); + print_usage(); + exit(0); + break; +#endif + } /* end of switch statement */ + } /* end of loop */ + +#ifdef GETOPT_SUPPORT + arg1 = optind; +#else + arg1 = i; +#endif + + /* option help requested? "-o help" */ + if (option_help) + { + print_welcome (); + printf ("FluidSynth settings:\n"); + fluid_settings_foreach (settings, settings, settings_foreach_func); + exit (0); + } + +#ifdef WIN32 + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); +#endif + +#ifdef LASH_ENABLED + /* connect to the lash server */ + if (connect_lash) + { + enabled_lash = fluid_lash_connect (lash_args); + fluid_settings_setint (settings, "lash.enable", enabled_lash ? 1 : 0); + } +#endif + + /* The 'groups' setting is only relevant for LADSPA operation + * If not given, set number groups to number of audio channels, because + * they are the same (there is nothing between synth output and 'sound card') + */ + if ((audio_groups == 0) && (audio_channels != 0)) { + audio_groups = audio_channels; + } + fluid_settings_setint(settings, "synth.audio-groups", audio_groups); + + /* create the synthesizer */ + synth = new_fluid_synth(settings); + if (synth == NULL) { + fprintf(stderr, "Failed to create the synthesizer\n"); + exit(-1); + } + + cmd_handler = new_fluid_cmd_handler(synth); + if (cmd_handler == NULL) { + fprintf(stderr, "Failed to create the command handler\n"); + goto cleanup; + } + + /* try to load the user or system configuration */ + if (config_file != NULL) { + fluid_source(cmd_handler, config_file); + } else if (fluid_get_userconf(buf, 512) != NULL) { + fluid_source(cmd_handler, buf); + } else if (fluid_get_sysconf(buf, 512) != NULL) { + fluid_source(cmd_handler, buf); + } + + /* load the soundfonts */ + for (i = arg1; i < argc; i++) { + if ((argv[i][0] != '-') && fluid_is_soundfont(argv[i])) { + if (fluid_synth_sfload(synth, argv[i], 1) == -1) { + fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]); + } + } + } + +#ifdef HAVE_SIGNAL_H +/* signal(SIGINT, handle_signal); */ +#endif + + /* start the synthesis thread */ + adriver = new_fluid_audio_driver(settings, synth); + if (adriver == NULL) { + fprintf(stderr, "Failed to create the audio driver\n"); + goto cleanup; + } + + + /* start the midi router and link it to the synth */ +#if WITH_MIDI + if (midi_in) { + /* In dump mode, text output is generated for events going into and out of the router. + * The example dump functions are put into the chain before and after the router.. + */ + + router = new_fluid_midi_router( + settings, + dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event, + (void*)synth); + + if (router == NULL) { + fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n" + "will be available. You can access the synthesizer \n" + "through the console.\n"); + } else { + fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */ + mdriver = new_fluid_midi_driver( + settings, + dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event, + (void*) router); + if (mdriver == NULL) { + fprintf(stderr, "Failed to create the MIDI thread; no MIDI input\n" + "will be available. You can access the synthesizer \n" + "through the console.\n"); + } + } + } +#endif + + /* play the midi files, if any */ + for (i = arg1; i < argc; i++) { + if ((argv[i][0] != '-') && fluid_is_midifile(argv[i])) { + + if (player == NULL) { + player = new_fluid_player(synth); + if (player == NULL) { + fprintf(stderr, "Failed to create the midifile player.\n" + "Continuing without a player.\n"); + break; + } + } + + fluid_player_add(player, argv[i]); + } + } + + if (player != NULL) { + fluid_player_play(player); + } + + /* run the server, if requested */ +#if !defined(MACINTOSH) && !defined(WIN32) && !defined(__BEOS__) + if (with_server) { + server = new_fluid_server(settings, newclient, synth); + if (server == NULL) { + fprintf(stderr, "Failed to create the server.\n" + "Continuing without it.\n"); + } + } +#endif + + +#ifdef LASH_ENABLED + if (enabled_lash) + fluid_lash_create_thread (synth); +#endif + + /* run the shell */ + if (interactive) { + print_welcome(); + + printf ("Type 'help' for information on commands and 'help help' for help topics.\n\n"); + + /* In dump mode we set the prompt to "". The UI cannot easily + * handle lines, which don't end with CR. Changing the prompt + * cannot be done through a command, because the current shell + * does not handle empty arguments. The ordinary case is dump == + * 0. + */ + fluid_settings_setstr(settings, "shell.prompt", dump ? "" : "> "); + fluid_usershell(settings, cmd_handler); + } + + cleanup: + +#if !defined(MACINTOSH) && !defined(WIN32) && !defined(__BEOS__) + if (server != NULL) { + /* if the user typed 'quit' in the shell, kill the server */ + if (!interactive) { + fluid_server_join(server); + } + delete_fluid_server(server); + } +#endif + + if (cmd_handler != NULL) { + delete_fluid_cmd_handler(cmd_handler); + } + + if (player != NULL) { + /* if the user typed 'quit' in the shell, stop the player */ + if (interactive) { + fluid_player_stop(player); + } + fluid_player_join(player); + delete_fluid_player(player); + } + + if (router) { +#if WITH_MIDI + if (mdriver) { + delete_fluid_midi_driver(mdriver); + } + delete_fluid_midi_router(router); +#endif + } + + if (adriver) { + delete_fluid_audio_driver(adriver); + } + + if (synth) { + delete_fluid_synth(synth); + } + + if (settings) { + delete_fluid_settings(settings); + } + + return 0; +} + +static fluid_cmd_handler_t* newclient(void* data, char* addr) +{ + fluid_synth_t* synth = (fluid_synth_t*) data; + return new_fluid_cmd_handler(synth); +} + + +/* + * print_usage + */ +void +print_usage() +{ + print_welcome (); + fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n"); + fprintf(stderr, "Try -h for help.\n"); + exit(0); +} + +/* + * print_welcome + */ +void +print_welcome() +{ + printf("FluidSynth version %s\n" + "Copyright (C) 2000-2006 Peter Hanappe and others.\n" + "Distributed under the LGPL license.\n" + "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n", + FLUIDSYNTH_VERSION); +} + +/* + * print_help + */ +void +print_help() +{ + print_welcome (); + printf("Usage: \n"); + printf(" fluidsynth [options] [soundfonts] [midifiles]\n"); + printf("Possible options:\n"); + printf(" -a, --audio-driver=[label]\n" + " The audio driver [alsa,jack,oss,dsound,...]\n"); + printf(" -C, --chorus\n" + " Turn the chorus on or off [0|1|yes|no, default = on]\n"); + printf(" -c, --audio-bufcount=[count]\n" + " Number of audio buffers\n"); + printf(" -d, --dump\n" + " Dump incoming and outgoing MIDI events to stdout\n"); + printf(" -f, --load-config\n" + " Load command configuration file (shell commands)\n"); + printf(" -G, --audio-groups\n" + " Defines the number of LADSPA audio nodes\n"); + printf(" -g, --gain\n" + " Set the master gain [0 < gain < 10, default = 0.2]\n"); + printf(" -h, --help\n" + " Print out this help summary\n"); + printf(" -i, --no-shell\n" + " Don't read commands from the shell [default = yes]\n"); + printf(" -j, --connect-jack-outputs\n" + " Attempt to connect the jack outputs to the physical ports\n"); + printf(" -K, --midi-channels=[num]\n" + " The number of midi channels [default = 16]\n"); + printf(" -L, --audio-channels=[num]\n" + " The number of stereo audio channels [default = 1]\n"); +#ifdef LASH_ENABLED + printf(" -l, --disable-lash\n" + " Don't connect to LASH server\n"); +#endif + printf(" -m, --midi-driver=[label]\n" + " The name of the midi driver to use [oss,alsa,alsa_seq,...]\n"); + printf(" -n, --no-midi-in\n" + " Don't create a midi driver to read MIDI input events [default = yes]\n"); + printf(" -o\n" + " Define a setting, -o name=value (\"-o help\" to dump current values)\n"); + printf(" -R, --reverb\n" + " Turn the reverb on or off [0|1|yes|no, default = on]\n"); + printf(" -r, --sample-rate\n" + " Set the sample rate\n"); + printf(" -s, --server\n" + " Start FluidSynth as a server process\n"); + printf(" -V, --version\n" + " Show version of program\n"); + printf(" -v, --verbose\n" + " Print out verbose messages about midi events\n"); + printf(" -z, --audio-bufsize=[size]\n" + " Size of each audio buffer\n"); + exit(0); +} diff --git a/src/libs/fluidsynth/src/fluidsynth_priv.h b/src/libs/fluidsynth/src/fluidsynth_priv.h new file mode 100644 index 0000000000..05f91f6bf3 --- /dev/null +++ b/src/libs/fluidsynth/src/fluidsynth_priv.h @@ -0,0 +1,326 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + + +#ifndef _FLUIDSYNTH_PRIV_H +#define _FLUIDSYNTH_PRIV_H + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(__POWERPC__) && !(defined(__APPLE__) && defined(__MACH__)) +#include "config_maxmsp43.h" +#endif + +#if defined(WIN32) && !defined(MINGW32) +#include "config_win32.h" +#endif + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_STDIO_H +#include +#endif + +#if HAVE_MATH_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_SYS_MMAN_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_PTHREAD_H +#include +#endif + +#if HAVE_IO_H +#include +#endif + +#if HAVE_WINDOWS_H +#include +#endif + +/* MinGW32 special defines */ +#ifdef MINGW32 + +#include +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +#define DSOUND_SUPPORT 1 +#define WINMIDI_SUPPORT 1 +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define WITHOUT_SERVER 1 + +#endif + +/* Darwin special defines (taken from config_macosx.h) */ +#ifdef DARWIN +#define MACINTOSH +#define __Types__ +#define WITHOUT_SERVER 1 +#endif + + +#include "fluidsynth.h" + + +/*************************************************************** + * + * BASIC TYPES + */ + +#if defined(WITH_FLOAT) +typedef float fluid_real_t; +#else +typedef double fluid_real_t; +#endif + + +typedef enum { + FLUID_OK = 0, + FLUID_FAILED = -1 +} fluid_status; + + +#if defined(WIN32) +typedef SOCKET fluid_socket_t; +#else +typedef int fluid_socket_t; +#define INVALID_SOCKET -1 +#endif + + +/** Integer types */ + +#if defined(MINGW32) + +/* Windows using MinGW32 */ +typedef int8_t sint8; +typedef uint8_t uint8; +typedef int16_t sint16; +typedef uint16_t uint16; +typedef int32_t sint32; +typedef uint32_t uint32; +typedef int64_t sint64; +typedef uint64_t uint64; + +#elif defined(_WIN32) + +/* Windows */ +typedef signed __int8 sint8; +typedef unsigned __int8 uint8; +typedef signed __int16 sint16; +typedef unsigned __int16 uint16; +typedef signed __int32 sint32; +typedef unsigned __int32 uint32; +typedef signed __int64 sint64; +typedef unsigned __int64 uint64; + +#elif defined(MACOS9) + +/* Macintosh */ +typedef signed char sint8; +typedef unsigned char uint8; +typedef signed short sint16; +typedef unsigned short uint16; +typedef signed int sint32; +typedef unsigned int uint32; +/* FIXME: needs to be verified */ +typedef long long sint64; +typedef unsigned long long uint64; + +#elif defined(__BEOS__) +typedef signed char sint8; +typedef signed short sint16; +typedef signed int sint32; +typedef long long sint64; + +#else + +/* Linux & Darwin */ +typedef int8_t sint8; +typedef u_int8_t uint8; +typedef int16_t sint16; +typedef u_int16_t uint16; +typedef int32_t sint32; +typedef u_int32_t uint32; +typedef int64_t sint64; +typedef u_int64_t uint64; + +#endif + + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_env_data_t fluid_env_data_t; +typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t; +typedef struct _fluid_channel_t fluid_channel_t; +typedef struct _fluid_tuning_t fluid_tuning_t; +typedef struct _fluid_hashtable_t fluid_hashtable_t; +typedef struct _fluid_client_t fluid_client_t; +typedef struct _fluid_server_socket_t fluid_server_socket_t; + +/*************************************************************** + * + * CONSTANTS + */ + +#define FLUID_BUFSIZE 64 + +#ifndef PI +#define PI 3.141592654 +#endif + +/*************************************************************** + * + * SYSTEM INTERFACE + */ +typedef FILE* fluid_file; + +#define FLUID_MALLOC(_n) malloc(_n) +#define FLUID_REALLOC(_p,_n) realloc(_p,_n) +#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t)) +#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t)) +#define FLUID_FREE(_p) free(_p) +#define FLUID_FOPEN(_f,_m) fopen(_f,_m) +#define FLUID_FCLOSE(_f) fclose(_f) +#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) +#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) +#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) +#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) +#define FLUID_STRLEN(_s) strlen(_s) +#define FLUID_STRCMP(_s,_t) strcmp(_s,_t) +#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) +#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) +#define FLUID_STRCHR(_s,_c) strchr(_s,_c) +#ifdef strdup +#define FLUID_STRDUP(s) strdup(s) +#else +#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) +#endif +#define FLUID_SPRINTF sprintf +#define FLUID_FPRINTF fprintf + +#define fluid_clip(_val, _min, _max) \ +{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } + +/* Purpose: + * Some commands (SSE extensions on Pentium) need aligned data( + * The address must be ...xxx0. + * Take a pointer, and round it up to the next suitable address. + * Obviously, one has to allocate 15 bytes of additional memory. + * As soon as proper alignment is supported by the compiler, this + * can be removed. + */ +#ifdef ENABLE_SSE +/* FIXME - This is broken on AMD 64 - only used if SSE enabled */ +#define FLUID_ALIGN16BYTE(ptr)(((int)(ptr)+15) & (~0xFL)) +#else +#define FLUID_ALIGN16BYTE(ptr) ptr +#endif + +#if WITH_FTS +#define FLUID_PRINTF post +#define FLUID_FLUSH() +#else +#define FLUID_PRINTF printf +#define FLUID_FLUSH() fflush(stdout) +#endif + +#define FLUID_LOG fluid_log + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + + +#define FLUID_ASSERT(a,b) +#define FLUID_ASSERT_P(a,b) + +char* fluid_error(void); + + +/* Internationalization */ +#define _(s) s + + +#endif /* _FLUIDSYNTH_PRIV_H */