From abf7905d5f82ad796544aa664cf3abaf20385cf7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 18 Oct 2018 00:41:02 +0200 Subject: Update Fluidsynth to 2.0.1 --- libs/fluidsynth/README | 5 +- libs/fluidsynth/fluidsynth/event.h | 151 +- libs/fluidsynth/fluidsynth/fluidsynth.h | 2 +- libs/fluidsynth/fluidsynth/gen.h | 172 +- libs/fluidsynth/fluidsynth/log.h | 35 +- libs/fluidsynth/fluidsynth/midi.h | 168 +- libs/fluidsynth/fluidsynth/misc.h | 24 +- libs/fluidsynth/fluidsynth/mod.h | 96 +- libs/fluidsynth/fluidsynth/settings.h | 115 +- libs/fluidsynth/fluidsynth/sfont.h | 439 +- libs/fluidsynth/fluidsynth/synth.h | 468 +- libs/fluidsynth/fluidsynth/types.h | 19 +- libs/fluidsynth/fluidsynth/voice.h | 46 +- libs/fluidsynth/src/fluid_adsr_env.c | 35 +- libs/fluidsynth/src/fluid_adsr_env.h | 196 +- libs/fluidsynth/src/fluid_chan.c | 805 ++- libs/fluidsynth/src/fluid_chan.h | 279 +- libs/fluidsynth/src/fluid_chorus.c | 673 ++- libs/fluidsynth/src/fluid_chorus.h | 52 +- libs/fluidsynth/src/fluid_conv.c | 463 +- libs/fluidsynth/src/fluid_conv.h | 58 +- libs/fluidsynth/src/fluid_defsfont.c | 4675 ++++++--------- libs/fluidsynth/src/fluid_defsfont.h | 547 +- libs/fluidsynth/src/fluid_event.c | 694 ++- libs/fluidsynth/src/fluid_event.h | 87 + libs/fluidsynth/src/fluid_gen.c | 195 +- libs/fluidsynth/src/fluid_gen.h | 47 +- libs/fluidsynth/src/fluid_hash.c | 969 ++-- libs/fluidsynth/src/fluid_hash.h | 124 +- libs/fluidsynth/src/fluid_iir_filter.c | 568 +- libs/fluidsynth/src/fluid_iir_filter.h | 74 +- libs/fluidsynth/src/fluid_lfo.c | 16 +- libs/fluidsynth/src/fluid_lfo.h | 71 +- libs/fluidsynth/src/fluid_list.c | 373 +- libs/fluidsynth/src/fluid_list.h | 32 +- libs/fluidsynth/src/fluid_midi.c | 1459 +++-- libs/fluidsynth/src/fluid_midi.h | 490 +- libs/fluidsynth/src/fluid_mod.c | 845 +-- libs/fluidsynth/src/fluid_mod.h | 39 +- libs/fluidsynth/src/fluid_phase.h | 24 +- libs/fluidsynth/src/fluid_rev.c | 688 +-- libs/fluidsynth/src/fluid_rev.h | 58 +- libs/fluidsynth/src/fluid_ringbuffer.c | 63 +- libs/fluidsynth/src/fluid_ringbuffer.h | 73 +- libs/fluidsynth/src/fluid_rvoice.c | 1102 ++-- libs/fluidsynth/src/fluid_rvoice.h | 267 +- libs/fluidsynth/src/fluid_rvoice_dsp.c | 1112 ++-- libs/fluidsynth/src/fluid_rvoice_event.c | 364 +- libs/fluidsynth/src/fluid_rvoice_event.h | 119 +- libs/fluidsynth/src/fluid_rvoice_mixer.c | 1885 +++--- libs/fluidsynth/src/fluid_rvoice_mixer.h | 67 +- libs/fluidsynth/src/fluid_samplecache.c | 295 + libs/fluidsynth/src/fluid_samplecache.h | 34 + libs/fluidsynth/src/fluid_settings.c | 2355 ++++---- libs/fluidsynth/src/fluid_settings.h | 49 +- libs/fluidsynth/src/fluid_sffile.c | 2566 +++++++++ libs/fluidsynth/src/fluid_sffile.h | 230 + libs/fluidsynth/src/fluid_sfont.c | 784 +++ libs/fluidsynth/src/fluid_sfont.h | 159 +- libs/fluidsynth/src/fluid_synth.c | 8573 +++++++++++++++++----------- libs/fluidsynth/src/fluid_synth.h | 268 +- libs/fluidsynth/src/fluid_synth_monopoly.c | 727 +++ libs/fluidsynth/src/fluid_sys.c | 1865 +++--- libs/fluidsynth/src/fluid_sys.h | 453 +- libs/fluidsynth/src/fluid_tuning.c | 207 +- libs/fluidsynth/src/fluid_tuning.h | 41 +- libs/fluidsynth/src/fluid_voice.c | 2692 +++++---- libs/fluidsynth/src/fluid_voice.h | 247 +- libs/fluidsynth/src/fluidsynth_priv.h | 177 +- libs/fluidsynth/wscript | 10 +- 70 files changed, 26159 insertions(+), 17001 deletions(-) create mode 100644 libs/fluidsynth/src/fluid_event.h create mode 100644 libs/fluidsynth/src/fluid_samplecache.c create mode 100644 libs/fluidsynth/src/fluid_samplecache.h create mode 100644 libs/fluidsynth/src/fluid_sffile.c create mode 100644 libs/fluidsynth/src/fluid_sffile.h create mode 100644 libs/fluidsynth/src/fluid_sfont.c create mode 100644 libs/fluidsynth/src/fluid_synth_monopoly.c (limited to 'libs/fluidsynth') diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README index 6032b9c299..de74bfb548 100644 --- a/libs/fluidsynth/README +++ b/libs/fluidsynth/README @@ -1,8 +1,7 @@ This is a stripped down version of fluidsynth (library only) -from git://git.code.sf.net/p/fluidsynth/code-git -revisition f52597be038a5a045fc74b6f96d5f9b0bbbbc044 from May/2015 -imported into Ardour Aug/2016 +from git://github.com/FluidSynth/fluidsynth.git +rev. v2.0.1-5-gebc177f Oct/2018 fluidsynth is licensed in terms of the LGPL-2+, see individual source files for (C) holders. diff --git a/libs/fluidsynth/fluidsynth/event.h b/libs/fluidsynth/fluidsynth/event.h index b154304515..cbd1fa6a09 100644 --- a/libs/fluidsynth/fluidsynth/event.h +++ b/libs/fluidsynth/fluidsynth/event.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -35,99 +35,102 @@ extern "C" { /** * Sequencer event type enumeration. */ -enum fluid_seq_event_type { - FLUID_SEQ_NOTE = 0, /**< Note event with duration */ - FLUID_SEQ_NOTEON, /**< Note on event */ - FLUID_SEQ_NOTEOFF, /**< Note off event */ - FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */ - FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */ - FLUID_SEQ_BANKSELECT, /**< Bank select message */ - FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ - FLUID_SEQ_PROGRAMSELECT, /**< Program select message (DOCME) */ - FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ - FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was mispelled previously */ - FLUID_SEQ_MODULATION, /**< Modulation controller event */ - FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ - FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ - FLUID_SEQ_PAN, /**< Stereo pan set event */ - FLUID_SEQ_VOLUME, /**< Volume set event */ - FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ - FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ - FLUID_SEQ_TIMER, /**< Timer event (DOCME) */ - FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */ - FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ - FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ - FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ - FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */ +enum fluid_seq_event_type +{ + FLUID_SEQ_NOTE = 0, /**< Note event with duration */ + FLUID_SEQ_NOTEON, /**< Note on event */ + FLUID_SEQ_NOTEOFF, /**< Note off event */ + FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */ + FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */ + FLUID_SEQ_BANKSELECT, /**< Bank select message */ + FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ + FLUID_SEQ_PROGRAMSELECT, /**< Program select message */ + FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ + FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was mispelled previously */ + FLUID_SEQ_MODULATION, /**< Modulation controller event */ + FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ + FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ + FLUID_SEQ_PAN, /**< Stereo pan set event */ + FLUID_SEQ_VOLUME, /**< Volume set event */ + FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ + FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ + FLUID_SEQ_TIMER, /**< Timer event (useful for giving a callback at a certain time) */ + FLUID_SEQ_ANYCONTROLCHANGE, /**< Any control change message (only internally used for remove_events) */ + FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ + FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 2.0.0 */ + FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ + FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ +#ifndef __DOXYGEN__ + FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +#endif }; -#define FLUID_SEQ_PITCHWHHELSENS FLUID_SEQ_PITCHWHEELSENS /**< Old deprecated misspelling of #FLUID_SEQ_PITCHWHEELSENS */ - /* Event alloc/free */ -FLUIDSYNTH_API fluid_event_t* new_fluid_event(void); -FLUIDSYNTH_API void delete_fluid_event(fluid_event_t* evt); +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); +FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src); +FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest); /* Timer events */ -FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data); +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_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); +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); +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); +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); +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); -FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t* evt); +FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val); +FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt); /* Only for removing events */ -FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel); +FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t *evt, int channel); /* Only when unregistering clients */ -FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t* evt); +FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt); /* 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); +FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t 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 } diff --git a/libs/fluidsynth/fluidsynth/fluidsynth.h b/libs/fluidsynth/fluidsynth/fluidsynth.h index 8c599b5be7..8852ea2d84 100644 --- a/libs/fluidsynth/fluidsynth/fluidsynth.h +++ b/libs/fluidsynth/fluidsynth/fluidsynth.h @@ -15,7 +15,7 @@ extern "C" { FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); -FLUIDSYNTH_API char* fluid_version_str(void); +FLUIDSYNTH_API const char* fluid_version_str(void); #include "types.h" diff --git a/libs/fluidsynth/fluidsynth/gen.h b/libs/fluidsynth/fluidsynth/gen.h index e4bbc8ef69..4b625831b0 100644 --- a/libs/fluidsynth/fluidsynth/gen.h +++ b/libs/fluidsynth/fluidsynth/gen.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -33,99 +33,83 @@ extern "C" { /** * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3) */ -enum fluid_gen_type { - GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ - GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ - GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ - GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ - GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ - GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ - GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ - GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ - GEN_FILTERFC, /**< Filter cutoff */ - GEN_FILTERQ, /**< Filter Q */ - GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ - GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ - GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ - GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ - GEN_UNUSED1, /**< Unused */ - GEN_CHORUSSEND, /**< Chorus send amount */ - GEN_REVERBSEND, /**< Reverb send amount */ - GEN_PAN, /**< Stereo panning */ - GEN_UNUSED2, /**< Unused */ - GEN_UNUSED3, /**< Unused */ - GEN_UNUSED4, /**< Unused */ - GEN_MODLFODELAY, /**< Modulation LFO delay */ - GEN_MODLFOFREQ, /**< Modulation LFO frequency */ - GEN_VIBLFODELAY, /**< Vibrato LFO delay */ - GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ - GEN_MODENVDELAY, /**< Modulation envelope delay */ - GEN_MODENVATTACK, /**< Modulation envelope attack */ - GEN_MODENVHOLD, /**< Modulation envelope hold */ - GEN_MODENVDECAY, /**< Modulation envelope decay */ - GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ - GEN_MODENVRELEASE, /**< Modulation envelope release */ - GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ - GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ - GEN_VOLENVDELAY, /**< Volume envelope delay */ - GEN_VOLENVATTACK, /**< Volume envelope attack */ - GEN_VOLENVHOLD, /**< Volume envelope hold */ - GEN_VOLENVDECAY, /**< Volume envelope decay */ - GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ - GEN_VOLENVRELEASE, /**< Volume envelope release */ - GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ - GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ - GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ - GEN_RESERVED1, /**< Reserved */ - GEN_KEYRANGE, /**< MIDI note range */ - GEN_VELRANGE, /**< MIDI velocity range */ - GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ - GEN_KEYNUM, /**< Fixed MIDI note number */ - GEN_VELOCITY, /**< Fixed MIDI velocity value */ - GEN_ATTENUATION, /**< Initial volume attenuation */ - GEN_RESERVED2, /**< Reserved */ - GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ - GEN_COARSETUNE, /**< Coarse tuning */ - GEN_FINETUNE, /**< Fine tuning */ - GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ - GEN_SAMPLEMODE, /**< Sample mode flags */ - GEN_RESERVED3, /**< Reserved */ - GEN_SCALETUNE, /**< Scale tuning */ - GEN_EXCLUSIVECLASS, /**< Exclusive class number */ - GEN_OVERRIDEROOTKEY, /**< Sample root note override */ - - /* 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, /**< Pitch (NOTE: Not a real SoundFont generator) */ - GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) */ -}; - - -/** - * SoundFont generator structure. - */ -typedef struct _fluid_gen_t +enum fluid_gen_type { - unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ - double val; /**< The nominal value */ - double mod; /**< Change by modulators */ - double nrpn; /**< Change by NRPN messages */ -} fluid_gen_t; + GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ + GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ + GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ + GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ + GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ + GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ + GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ + GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ + GEN_FILTERFC, /**< Filter cutoff */ + GEN_FILTERQ, /**< Filter Q */ + GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ + GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ + GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ + GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ + GEN_UNUSED1, /**< Unused */ + GEN_CHORUSSEND, /**< Chorus send amount */ + GEN_REVERBSEND, /**< Reverb send amount */ + GEN_PAN, /**< Stereo panning */ + GEN_UNUSED2, /**< Unused */ + GEN_UNUSED3, /**< Unused */ + GEN_UNUSED4, /**< Unused */ + GEN_MODLFODELAY, /**< Modulation LFO delay */ + GEN_MODLFOFREQ, /**< Modulation LFO frequency */ + GEN_VIBLFODELAY, /**< Vibrato LFO delay */ + GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ + GEN_MODENVDELAY, /**< Modulation envelope delay */ + GEN_MODENVATTACK, /**< Modulation envelope attack */ + GEN_MODENVHOLD, /**< Modulation envelope hold */ + GEN_MODENVDECAY, /**< Modulation envelope decay */ + GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ + GEN_MODENVRELEASE, /**< Modulation envelope release */ + GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ + GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ + GEN_VOLENVDELAY, /**< Volume envelope delay */ + GEN_VOLENVATTACK, /**< Volume envelope attack */ + GEN_VOLENVHOLD, /**< Volume envelope hold */ + GEN_VOLENVDECAY, /**< Volume envelope decay */ + GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ + GEN_VOLENVRELEASE, /**< Volume envelope release */ + GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ + GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ + GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ + GEN_RESERVED1, /**< Reserved */ + GEN_KEYRANGE, /**< MIDI note range */ + GEN_VELRANGE, /**< MIDI velocity range */ + GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ + GEN_KEYNUM, /**< Fixed MIDI note number */ + GEN_VELOCITY, /**< Fixed MIDI velocity value */ + GEN_ATTENUATION, /**< Initial volume attenuation */ + GEN_RESERVED2, /**< Reserved */ + GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ + GEN_COARSETUNE, /**< Coarse tuning */ + GEN_FINETUNE, /**< Fine tuning */ + GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ + GEN_SAMPLEMODE, /**< Sample mode flags */ + GEN_RESERVED3, /**< Reserved */ + GEN_SCALETUNE, /**< Scale tuning */ + GEN_EXCLUSIVECLASS, /**< Exclusive class number */ + GEN_OVERRIDEROOTKEY, /**< Sample root note override */ -/** - * Enum value for 'flags' field of #fluid_gen_t (not really flags). - */ -enum fluid_gen_flags -{ - GEN_UNUSED, /**< Generator value is not set */ - GEN_SET, /**< Generator value is set */ - GEN_ABS_NRPN /**< Generator is an absolute value */ -}; + /* 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, /**< Pitch @note Not a real SoundFont generator */ -FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen); + GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */ + /* non-standard generator for an additional custom high- or low-pass filter */ + GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */ + GEN_CUSTOM_FILTERQ, /**< Custom filter Q */ +#ifndef __DOXYGEN__ + GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +#endif +}; #ifdef __cplusplus diff --git a/libs/fluidsynth/fluidsynth/log.h b/libs/fluidsynth/fluidsynth/log.h index 85db03e1cb..00d802f50e 100644 --- a/libs/fluidsynth/fluidsynth/log.h +++ b/libs/fluidsynth/fluidsynth/log.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -52,13 +52,16 @@ extern "C" { /** * FluidSynth log levels. */ -enum fluid_log_level { - FLUID_PANIC, /**< The synth can't function correctly any more */ - FLUID_ERR, /**< Serious error occurred */ - FLUID_WARN, /**< Warning */ - FLUID_INFO, /**< Verbose informational messages */ - FLUID_DBG, /**< Debugging messages */ - LAST_LOG_LEVEL +enum fluid_log_level +{ + FLUID_PANIC, /**< The synth can't function correctly any more */ + FLUID_ERR, /**< Serious error occurred */ + FLUID_WARN, /**< Warning */ + FLUID_INFO, /**< Verbose informational messages */ + FLUID_DBG, /**< Debugging messages */ +#ifndef __DOXYGEN__ + LAST_LOG_LEVEL /**< @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +#endif }; /** @@ -67,12 +70,12 @@ enum fluid_log_level { * @param message Log message text * @param data User data pointer supplied to fluid_set_log_function(). */ -typedef void (*fluid_log_function_t)(int level, char* message, void* data); +typedef void (*fluid_log_function_t)(int level, const char *message, void *data); -FLUIDSYNTH_API -fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data); +FLUIDSYNTH_API +fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void *data); -FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data); +FLUIDSYNTH_API void fluid_default_log_function(int level, const char *message, void *data); FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...); diff --git a/libs/fluidsynth/fluidsynth/midi.h b/libs/fluidsynth/fluidsynth/midi.h index ab1e6a198b..cd30e2088b 100644 --- a/libs/fluidsynth/fluidsynth/midi.h +++ b/libs/fluidsynth/fluidsynth/midi.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -30,27 +30,31 @@ extern "C" { * @brief Functions for MIDI events, drivers and MIDI file playback. */ -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); -FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t* evt, void *data, - int size, int dynamic); +FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void); +FLUIDSYNTH_API void 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); +FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, + int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, + void *data, int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, + void *data, int size, int dynamic); /** * MIDI router rule type. @@ -58,13 +62,15 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t* evt, void *dat */ typedef enum { - FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */ - FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */ - FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */ - FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ - FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ - FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ - FLUID_MIDI_ROUTER_RULE_COUNT /**< Total count of rule types */ + FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */ + FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */ + FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */ + FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ + FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ + FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ +#ifndef __DOXYGEN__ + FLUID_MIDI_ROUTER_RULE_COUNT /**< @internal Total count of rule types @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time!*/ +#endif } fluid_midi_router_rule_type; /** @@ -79,35 +85,35 @@ typedef enum * to communicate events. * In the not-so-far future... */ -typedef int (*handle_midi_event_func_t)(void* data, fluid_midi_event_t* event); - -FLUIDSYNTH_API fluid_midi_router_t* new_fluid_midi_router(fluid_settings_t* settings, - handle_midi_event_func_t handler, - void* event_handler_data); -FLUIDSYNTH_API int delete_fluid_midi_router(fluid_midi_router_t* handler); -FLUIDSYNTH_API int fluid_midi_router_set_default_rules (fluid_midi_router_t *router); -FLUIDSYNTH_API int fluid_midi_router_clear_rules (fluid_midi_router_t *router); -FLUIDSYNTH_API int fluid_midi_router_add_rule (fluid_midi_router_t *router, - fluid_midi_router_rule_t *rule, int type); -FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule (void); -FLUIDSYNTH_API void delete_fluid_midi_router_rule (fluid_midi_router_rule_t *rule); -FLUIDSYNTH_API void fluid_midi_router_rule_set_chan (fluid_midi_router_rule_t *rule, - int min, int max, float mul, int add); -FLUIDSYNTH_API void fluid_midi_router_rule_set_param1 (fluid_midi_router_rule_t *rule, - int min, int max, float mul, int add); -FLUIDSYNTH_API void fluid_midi_router_rule_set_param2 (fluid_midi_router_rule_t *rule, - int min, int max, float mul, int add); -FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event); -FLUIDSYNTH_API int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event); -FLUIDSYNTH_API int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event); - - -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); +typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); + +FLUIDSYNTH_API fluid_midi_router_t *new_fluid_midi_router(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +FLUIDSYNTH_API void delete_fluid_midi_router(fluid_midi_router_t *handler); +FLUIDSYNTH_API int fluid_midi_router_set_default_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_clear_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_add_rule(fluid_midi_router_t *router, + fluid_midi_router_rule_t *rule, int type); +FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule(void); +FLUIDSYNTH_API void delete_fluid_midi_router_rule(fluid_midi_router_rule_t *rule); +FLUIDSYNTH_API void fluid_midi_router_rule_set_chan(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param1(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param2(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_prerouter(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_postrouter(void *data, fluid_midi_event_t *event); + + +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); /** @@ -116,23 +122,31 @@ FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver); */ enum fluid_player_status { - FLUID_PLAYER_READY, /**< Player is ready */ - FLUID_PLAYER_PLAYING, /**< Player is currently playing */ - FLUID_PLAYER_DONE /**< Player is finished playing */ + FLUID_PLAYER_READY, /**< Player is ready */ + FLUID_PLAYER_PLAYING, /**< Player is currently playing */ + FLUID_PLAYER_DONE /**< Player is finished playing */ }; -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, const char *midifile); -FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len); -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); -FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t* player); -FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t* player, handle_midi_event_func_t handler, void* handler_data); +FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth); +FLUIDSYNTH_API void delete_fluid_player(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_add(fluid_player_t *player, const char *midifile); +FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len); +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); +FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data); + +FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks); + +/// #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/misc.h b/libs/fluidsynth/fluidsynth/misc.h index 4f97d8437d..7a2b457b5a 100644 --- a/libs/fluidsynth/fluidsynth/misc.h +++ b/libs/fluidsynth/fluidsynth/misc.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -36,7 +36,7 @@ extern "C" { * Value that indicates success, used by most libfluidsynth functions. * @since 1.1.0 * - * NOTE: This was not publicly defined prior to libfluidsynth 1.1.0. When + * @note This was not publicly defined prior to libfluidsynth 1.1.0. When * writing code which should also be compatible with older versions, something * like the following can be used: * @@ -55,19 +55,13 @@ extern "C" { * Value that indicates failure, used by most libfluidsynth functions. * @since 1.1.0 * - * NOTE: See #FLUID_OK for more details. + * @note See #FLUID_OK for more details. */ #define FLUID_FAILED (-1) -FLUIDSYNTH_API int fluid_is_soundfont (const char *filename); -FLUIDSYNTH_API int fluid_is_midifile (const char *filename); - - -#ifdef WIN32 -FLUIDSYNTH_API void* fluid_get_hinstance(void); -FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance); -#endif +FLUIDSYNTH_API int fluid_is_soundfont(const char *filename); +FLUIDSYNTH_API int fluid_is_midifile(const char *filename); #ifdef __cplusplus diff --git a/libs/fluidsynth/fluidsynth/mod.h b/libs/fluidsynth/fluidsynth/mod.h index e34309546b..5ea5f89d4a 100644 --- a/libs/fluidsynth/fluidsynth/mod.h +++ b/libs/fluidsynth/fluidsynth/mod.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -30,26 +30,6 @@ extern "C" { * @brief SoundFont modulator functions and constants. */ -#define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ - -/** - * Modulator structure. See SoundFont 2.04 PDF section 8.2. - */ -struct _fluid_mod_t -{ - unsigned char dest; /**< Destination generator to control */ - unsigned char src1; /**< Source controller 1 */ - unsigned char flags1; /**< Source controller 1 flags */ - unsigned char src2; /**< Source controller 2 */ - unsigned char flags2; /**< Source controller 2 flags */ - double amount; /**< Multiplier 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 defining the polarity, mapping function and type of a modulator source. @@ -60,16 +40,18 @@ struct _fluid_mod_t */ enum fluid_mod_flags { - FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */ - FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */ - FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */ - FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */ - FLUID_MOD_LINEAR = 0, /**< Linear mapping function */ - FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */ - FLUID_MOD_CONVEX = 8, /**< Convex mapping function */ - FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */ - FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */ - FLUID_MOD_CC = 16 /**< MIDI CC controller (source will be a MIDI CC number) */ + FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */ + FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */ + FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */ + FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */ + FLUID_MOD_LINEAR = 0, /**< Linear mapping function */ + FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */ + FLUID_MOD_CONVEX = 8, /**< Convex mapping function */ + FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */ + FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */ + FLUID_MOD_CC = 16, /**< MIDI CC controller (source will be a MIDI CC number) */ + + FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */ }; /** @@ -78,32 +60,36 @@ enum fluid_mod_flags */ enum fluid_mod_src { - FLUID_MOD_NONE = 0, /**< No source controller */ - FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */ - FLUID_MOD_KEY = 3, /**< MIDI note-on note number */ - FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */ - FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */ - FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */ - FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ + FLUID_MOD_NONE = 0, /**< No source controller */ + FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */ + FLUID_MOD_KEY = 3, /**< MIDI note-on note number */ + FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */ + FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */ + FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */ + FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ }; -FLUIDSYNTH_API fluid_mod_t* fluid_mod_new(void); -FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod); +FLUIDSYNTH_API fluid_mod_t *new_fluid_mod(void); +FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod); +FLUIDSYNTH_API size_t fluid_mod_sizeof(void); -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 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); +FLUIDSYNTH_API int fluid_mod_get_source1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod); +FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod); -FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2); +FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2); +FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl); +FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen); +FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src); #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/settings.h b/libs/fluidsynth/fluidsynth/settings.h index 3a0502a920..aa7f083129 100644 --- a/libs/fluidsynth/fluidsynth/settings.h +++ b/libs/fluidsynth/fluidsynth/settings.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -78,35 +78,6 @@ extern "C" { */ #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. - * @deprecated - * - * As there is an integer setting type, this hint is not used. - */ -#define FLUID_HINT_INTEGER 0x20 - - -#define FLUID_HINT_FILENAME 0x01 /**< String setting is a file name */ #define FLUID_HINT_OPTIONLIST 0x02 /**< Setting is a list of string options */ @@ -117,70 +88,68 @@ extern "C" { * set of values. The type of each setting can be retrieved using the * function fluid_settings_get_type() */ -enum fluid_types_enum { - FLUID_NO_TYPE = -1, /**< Undefined type */ - FLUID_NUM_TYPE, /**< Numeric (double) */ - FLUID_INT_TYPE, /**< Integer */ - FLUID_STR_TYPE, /**< String */ - FLUID_SET_TYPE /**< Set of values */ +enum fluid_types_enum +{ + FLUID_NO_TYPE = -1, /**< Undefined type */ + FLUID_NUM_TYPE, /**< Numeric (double) */ + FLUID_INT_TYPE, /**< Integer */ + FLUID_STR_TYPE, /**< String */ + FLUID_SET_TYPE /**< Set of values */ }; -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, const char *name); +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_hints(fluid_settings_t* settings, const char *name); +int fluid_settings_get_type(fluid_settings_t *settings, const char *name); FLUIDSYNTH_API -int fluid_settings_is_realtime(fluid_settings_t* settings, const char *name); +int fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *val); FLUIDSYNTH_API -int fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str); +int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name); FLUIDSYNTH_API -int fluid_settings_copystr(fluid_settings_t* settings, const char *name, char *str, int len); +int fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str); FLUIDSYNTH_API -int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str); +int fluid_settings_copystr(fluid_settings_t *settings, const char *name, char *str, int len); FLUIDSYNTH_API -int fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str); +int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str); FLUIDSYNTH_API -char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name); +int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); FLUIDSYNTH_API -int fluid_settings_str_equal(fluid_settings_t* settings, const char *name, const char *value); +int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value); FLUIDSYNTH_API -int fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val); +int fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val); FLUIDSYNTH_API -int fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val); +int fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val); FLUIDSYNTH_API -double fluid_settings_getnum_default(fluid_settings_t* settings, const char *name); +int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val); FLUIDSYNTH_API -void fluid_settings_getnum_range(fluid_settings_t* settings, const char *name, - double* min, double* max); +int fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max); FLUIDSYNTH_API -int fluid_settings_setint(fluid_settings_t* settings, const char *name, int val); +int fluid_settings_setint(fluid_settings_t *settings, const char *name, int val); FLUIDSYNTH_API -int fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val); +int fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val); FLUIDSYNTH_API -int fluid_settings_getint_default(fluid_settings_t* settings, const char *name); +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val); FLUIDSYNTH_API -void fluid_settings_getint_range(fluid_settings_t* settings, const char *name, - int* min, int* max); +int fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max); /** * Callback function type used with fluid_settings_foreach_option() @@ -188,17 +157,17 @@ void fluid_settings_getint_range(fluid_settings_t* settings, const char *name, * @param name Setting name * @param option A string option for this setting (iterates through the list) */ -typedef void (*fluid_settings_foreach_option_t)(void *data, char *name, char *option); +typedef void (*fluid_settings_foreach_option_t)(void *data, const char *name, const char *option); FLUIDSYNTH_API -void fluid_settings_foreach_option(fluid_settings_t* settings, - const char* name, void* data, - fluid_settings_foreach_option_t func); +void fluid_settings_foreach_option(fluid_settings_t *settings, + const char *name, void *data, + fluid_settings_foreach_option_t func); FLUIDSYNTH_API -int fluid_settings_option_count (fluid_settings_t* settings, const char* name); -FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings, - const char* name, - const char* separator); +int fluid_settings_option_count(fluid_settings_t *settings, const char *name); +FLUIDSYNTH_API char *fluid_settings_option_concat(fluid_settings_t *settings, + const char *name, + const char *separator); /** * Callback function type used with fluid_settings_foreach() @@ -206,11 +175,11 @@ FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings, * @param name Setting name * @param type Setting type (#fluid_types_enum) */ -typedef void (*fluid_settings_foreach_t)(void *data, char *name, int type); +typedef void (*fluid_settings_foreach_t)(void *data, const char *name, int type); FLUIDSYNTH_API -void fluid_settings_foreach(fluid_settings_t* settings, void* data, - fluid_settings_foreach_t func); +void fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func); #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/sfont.h b/libs/fluidsynth/fluidsynth/sfont.h index 30aebfd126..55413a9e24 100644 --- a/libs/fluidsynth/fluidsynth/sfont.h +++ b/libs/fluidsynth/fluidsynth/sfont.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -31,30 +31,30 @@ extern "C" { * @brief 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. This API allows for virtual SoundFont files to be loaded + * synthesizer. This API allows for virtual SoundFont files to be loaded * and synthesized, which may not actually be SoundFont files, as long as they * can be represented by the SoundFont synthesis model. * * 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. + * #fluid_sfloader_t instance created by new_fluid_sfloader(). + * On creation, you must specify a callback function \p load + * that will be called for every file attempting to load it and + * if successful returns a #fluid_sfont_t instance, 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 a #fluid_preset_t structure. + * function should return a #fluid_preset_t instance. * - * The #fluid_preset_t structure contains some functions to obtain + * The #fluid_preset_t instance contains some functions to obtain * information from the preset (name, bank, number). The most * important callback is the noteon function. The noteon function + * is called by fluidsynth internally and * 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_sample_t instance and returns a pointer to the opaque * #fluid_voice_t structure. To set or increment the values of a * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are * finished initializing the voice call fluid_voice_start() to @@ -64,215 +64,250 @@ extern "C" { /** * Some notification enums for presets and samples. */ -enum { - FLUID_PRESET_SELECTED, /**< Preset selected notify */ - FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ - FLUID_SAMPLE_DONE /**< Sample no longer needed notify */ +enum +{ + FLUID_PRESET_SELECTED, /**< Preset selected notify */ + FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ + FLUID_SAMPLE_DONE /**< Sample no longer needed notify */ }; - /** - * SoundFont loader structure. + * Indicates the type of a sample used by the _fluid_sample_t::sampletype field. */ -struct _fluid_sfloader_t { - void* data; /**< User defined data pointer */ - - /** - * The free method should free the memory allocated for the loader in - * addition to any private data. - * @param loader SoundFont loader - * @return Should return 0 if no error occured, non-zero otherwise - */ - int (*free)(fluid_sfloader_t* loader); - - /** - * Method to load an instrument file (does not actually need to be a real file name, - * could be another type of string identifier that the \a loader understands). - * @param loader SoundFont loader - * @param filename File name or other string identifier - * @return The loaded instrument file (SoundFont) or NULL if an error occured. - */ - fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename); +enum fluid_sample_type +{ + FLUID_SAMPLETYPE_MONO = 0x1, /**< Used for mono samples */ + FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Used for right samples of a stereo pair */ + FLUID_SAMPLETYPE_LEFT = 0x4, /**< Used for left samples of a stereo pair */ + FLUID_SAMPLETYPE_LINKED = 0x8, /**< Currently not used */ + FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Used for Ogg Vorbis compressed samples @since 1.1.7 */ + FLUID_SAMPLETYPE_ROM = 0x8000 /**< Indicates ROM samples, causes sample to be ignored */ }; + /** - * Virtual SoundFont instance structure. + * Method to load an instrument file (does not actually need to be a real file name, + * could be another type of string identifier that the \a loader understands). + * @param loader SoundFont loader + * @param filename File name or other string identifier + * @return The loaded instrument file (SoundFont) or NULL if an error occured. */ -struct _fluid_sfont_t { - void* data; /**< User defined data */ - unsigned int id; /**< SoundFont ID */ - - /** - * Method to free a virtual SoundFont bank. - * @param sfont Virtual SoundFont to free. - * @return Should return 0 when it was able to free all resources or non-zero - * if some of the samples could not be freed because they are still in use, - * in which case the free will be tried again later, until success. - */ - int (*free)(fluid_sfont_t* sfont); - - /** - * Method to return the name of a virtual SoundFont. - * @param sfont Virtual SoundFont - * @return The name of the virtual SoundFont. - */ - char* (*get_name)(fluid_sfont_t* sfont); - - /** - * Get a virtual SoundFont preset by bank and program numbers. - * @param sfont Virtual SoundFont - * @param bank MIDI bank number (0-16384) - * @param prenum MIDI preset number (0-127) - * @return Should return an allocated virtual preset or NULL if it could not - * be found. - */ - fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); - - /** - * Start virtual SoundFont preset iteration method. - * @param sfont Virtual SoundFont - * - * Starts/re-starts virtual preset iteration in a SoundFont. - */ - void (*iteration_start)(fluid_sfont_t* sfont); - - /** - * Virtual SoundFont preset iteration function. - * @param sfont Virtual SoundFont - * @param preset Caller supplied preset to fill in with current preset information - * @return 0 when no more presets are available, 1 otherwise - * - * Should store preset information to the caller supplied \a preset structure - * and advance the internal iteration state to the next preset for subsequent - * calls. - */ - int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset); -}; +typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename); + +/** + * The free method should free the memory allocated for a fluid_sfloader_t instance in + * addition to any private data. Any custom user provided cleanup function must ultimately call + * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfloader() is sufficient. + * @param loader SoundFont loader + */ +typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t *loader); + + +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free); +FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t *loader); -#define fluid_sfont_get_id(_sf) ((_sf)->id) +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings); /** - * Virtual SoundFont preset. + * Opens the file or memory indicated by \c filename in binary read mode. + * \c filename matches the string provided during the fluid_synth_sfload() call. + * + * @return returns a file handle on success, NULL otherwise */ -struct _fluid_preset_t { - void* data; /**< User supplied data */ - fluid_sfont_t* sfont; /**< Parent virtual SoundFont */ - - /** - * Method to free a virtual SoundFont preset. - * @param preset Virtual SoundFont preset - * @return Should return 0 - */ - int (*free)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset name. - * @param preset Virtual SoundFont preset - * @return Should return the name of the preset. The returned string must be - * valid for the duration of the virtual preset (or the duration of the - * SoundFont, in the case of preset iteration). - */ - char* (*get_name)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset MIDI bank number. - * @param preset Virtual SoundFont preset - * @param return The bank number of the preset - */ - int (*get_banknum)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset MIDI program number. - * @param preset Virtual SoundFont preset - * @param return The program number of the preset - */ - int (*get_num)(fluid_preset_t* preset); - - /** - * Method to handle a noteon event (synthesize the instrument). - * @param preset Virtual SoundFont preset - * @param synth Synthesizer instance - * @param chan MIDI channel number of the note on event - * @param key MIDI note number (0-127) - * @param vel MIDI velocity (0-127) - * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise - * - * This method may be called from within synthesis context and therefore - * should be as efficient as possible and not perform any operations considered - * bad for realtime audio output (memory allocations and other OS calls). - * - * 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 increment the values of a - * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are - * finished initializing the voice call fluid_voice_start() to - * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices - * created will be started at the same time. - */ - int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); - - /** - * Virtual SoundFont preset notify method. - * @param preset Virtual SoundFont preset - * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED - * @param chan MIDI channel number - * @return Should return #FLUID_OK - * - * Implement this optional method if the preset needs to be notified about - * preset select and unselect events. - * - * This method may be called from within synthesis context and therefore - * should be as efficient as possible and not perform any operations considered - * bad for realtime audio output (memory allocations and other OS calls). - */ - int (*notify)(fluid_preset_t* preset, int reason, int chan); -}; +typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); /** - * Virtual SoundFont sample. + * Reads \c count bytes to the specified buffer \c buf. + * + * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified. */ -struct _fluid_sample_t -{ - char name[21]; /**< Sample name */ - unsigned int start; /**< Start index */ - unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ - unsigned int loopstart; /**< Loop start index */ - unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ - unsigned int samplerate; /**< Sample rate */ - int origpitch; /**< Original pitch (MIDI note number, 0-127) */ - int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ - int sampletype; /**< Values: #FLUID_SAMPLETYPE_MONO, FLUID_SAMPLETYPE_RIGHT, FLUID_SAMPLETYPE_LEFT, FLUID_SAMPLETYPE_ROM */ - int valid; /**< Should be TRUE if sample data is valid, FALSE otherwise (in which case it will not be synthesized) */ - short* data; /**< Pointer to the sample's data */ - - int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ - double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ - - unsigned int refcount; /**< Count of voices using this sample (use #fluid_sample_refcount to access this field) */ - - /** - * Implement this function to receive notification when sample is no longer used. - * @param sample Virtual SoundFont sample - * @param reason #FLUID_SAMPLE_DONE only currently - * @return Should return #FLUID_OK - */ - int (*notify)(fluid_sample_t* sample, int reason); - - void* userdata; /**< User defined data */ -}; +typedef int (* fluid_sfloader_callback_read_t)(void *buf, int count, void *handle); + +/** + * Same purpose and behaviour as fseek. + * + * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * + * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise + */ +typedef int (* fluid_sfloader_callback_seek_t)(void *handle, long offset, int origin); + +/** + * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used ressources. + * + * @return returns #FLUID_OK on success, #FLUID_FAILED on error + */ +typedef int (* fluid_sfloader_callback_close_t)(void *handle); + +/** @return returns current file offset or #FLUID_FAILED on error */ +typedef long (* fluid_sfloader_callback_tell_t)(void *handle); + + +FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close); + +FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data); +FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader); + + + +/** + * Method to return the name of a virtual SoundFont. + * @param sfont Virtual SoundFont + * @return The name of the virtual SoundFont. + */ +typedef const char *(*fluid_sfont_get_name_t)(fluid_sfont_t *sfont); + +/** + * Get a virtual SoundFont preset by bank and program numbers. + * @param sfont Virtual SoundFont + * @param bank MIDI bank number (0-16383) + * @param prenum MIDI preset number (0-127) + * @return Should return an allocated virtual preset or NULL if it could not + * be found. + */ +typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int bank, int prenum); + +/** + * Start virtual SoundFont preset iteration method. + * @param sfont Virtual SoundFont + * + * Starts/re-starts virtual preset iteration in a SoundFont. + */ +typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); + +/** + * Virtual SoundFont preset iteration function. + * @param sfont Virtual SoundFont + * @param preset Caller supplied uninitialized buffer to fill in with current preset information + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + * + * Should store preset information to the caller supplied \a preset structure + * and advance the internal iteration state to the next preset for subsequent + * calls. + */ +typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); + +/** + * Method to free a virtual SoundFont bank. Any custom user provided cleanup function must ultimately call + * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfont() is sufficient. + * @param sfont Virtual SoundFont to free. + * @return Should return 0 when it was able to free all resources or non-zero + * if some of the samples could not be freed because they are still in use, + * in which case the free will be tried again later, until success. + */ +typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont); -#define fluid_sample_refcount(_sample) ((_sample)->refcount) /**< Get the reference count of a sample. Should only be called from within synthesis context (noteon method for example) */ +FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free); +FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont); -#define FLUID_SAMPLETYPE_MONO 1 /**< Flag for #fluid_sample_t \a sampletype field for mono samples */ -#define FLUID_SAMPLETYPE_RIGHT 2 /**< Flag for #fluid_sample_t \a sampletype field for right samples of a stereo pair */ -#define FLUID_SAMPLETYPE_LEFT 4 /**< Flag for #fluid_sample_t \a sampletype field for left samples of a stereo pair */ -#define FLUID_SAMPLETYPE_LINKED 8 /**< Flag for #fluid_sample_t \a sampletype field, not used currently */ -#define FLUID_SAMPLETYPE_ROM 0x8000 /**< Flag for #fluid_sample_t \a sampletype field, ROM sample, causes sample to be ignored */ +FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data); +FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_sfont_get_id(fluid_sfont_t *sfont); +FLUIDSYNTH_API const char *fluid_sfont_get_name(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +FLUIDSYNTH_API void fluid_sfont_iteration_start(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont); +/** + * Method to get a virtual SoundFont preset name. + * @param preset Virtual SoundFont preset + * @return Should return the name of the preset. The returned string must be + * valid for the duration of the virtual preset (or the duration of the + * SoundFont, in the case of preset iteration). + */ +typedef const char *(*fluid_preset_get_name_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI bank number. + * @param preset Virtual SoundFont preset + * @param return The bank number of the preset + */ +typedef int (*fluid_preset_get_banknum_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI program number. + * @param preset Virtual SoundFont preset + * @param return The program number of the preset + */ +typedef int (*fluid_preset_get_num_t)(fluid_preset_t *preset); + +/** + * Method to handle a noteon event (synthesize the instrument). + * @param preset Virtual SoundFont preset + * @param synth Synthesizer instance + * @param chan MIDI channel number of the note on event + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127) + * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + * + * 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 increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices + * created will be started at the same time. + */ +typedef int (*fluid_preset_noteon_t)(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); + +/** + * Method to free a virtual SoundFont preset. Any custom user provided cleanup function must ultimately call + * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data + * needs to be freed, setting this to delete_fluid_preset() is sufficient. + * @param preset Virtual SoundFont preset + * @return Should return 0 + */ +typedef void (*fluid_preset_free_t)(fluid_preset_t *preset); + +FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free); +FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset); + +FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data); +FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset); + +FLUIDSYNTH_API const char *fluid_preset_get_name(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset); +FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset); + +FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void); +FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample); +FLUIDSYNTH_API size_t fluid_sample_sizeof(void); + +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t *sample, const char *name); +FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data); + +FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end); +FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune); #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/synth.h b/libs/fluidsynth/fluidsynth/synth.h index f62e60cd1b..a4afb90947 100644 --- a/libs/fluidsynth/fluidsynth/synth.h +++ b/libs/fluidsynth/fluidsynth/synth.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -30,126 +30,112 @@ extern "C" { /** * @file synth.h * @brief Embeddable SoundFont 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. + * 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(), ... */ -#define FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE 32 /**< Length of channel info name field (including zero terminator) */ - -/** - * Channel information structure for fluid_synth_get_channel_info(). - * @since 1.1.1 - */ -struct _fluid_synth_channel_info_t -{ - int assigned : 1; /**< TRUE if a preset is assigned, FALSE otherwise */ - /* Reserved flag bits (at the least 31) */ - int sfont_id; /**< ID of parent SoundFont */ - int bank; /**< MIDI bank number (0-16383) */ - int program; /**< MIDI program number (0-127) */ - char name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE]; /**< Channel preset name */ - char reserved[32]; /**< Reserved data for future expansion */ -}; - -FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); -FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth); -FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); +FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings); +FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth); +FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); /* MIDI channel messages */ -FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); -FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); -FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); -FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); +FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel); +FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key); +FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t *synth, int chan, int ctrl, int val); +FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t *synth, int chan, int ctrl, int *pval); FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int *handled, int dryrun); -FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); -FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); -FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); -FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); -FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); -FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val); -FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); -FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); +FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend); +FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval); +FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t *synth, int chan, int program); +FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val); +FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank); +FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id); 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); +int fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num); FLUIDSYNTH_API int -fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, - const char *sfont_name, unsigned int bank_num, - unsigned int preset_num); -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); -FLUIDSYNTH_API int fluid_synth_unset_program (fluid_synth_t *synth, int chan); -FLUIDSYNTH_API int fluid_synth_get_channel_info (fluid_synth_t *synth, int chan, - fluid_synth_channel_info_t *info); -FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth); - -FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); -FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num); +FLUIDSYNTH_API +int fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num); +FLUIDSYNTH_API int fluid_synth_unset_program(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan); + +/** + * The midi channel type used by fluid_synth_set_channel_type() + */ enum fluid_midi_channel_type { - CHANNEL_TYPE_MELODIC = 0, - CHANNEL_TYPE_DRUM = 1 + CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ + CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ }; -int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type); +FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type); /* Low level access */ -FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); -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); -FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id); +FLUIDSYNTH_API fluid_preset_t *fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan); +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); +FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t *synth, unsigned int id); /* SoundFont management */ -FLUIDSYNTH_API -int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets); -FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id); -FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets); -FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); -FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); -FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); -FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); -FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); -FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name (fluid_synth_t* synth, - const char *name); -FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); -FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); +FLUIDSYNTH_API +int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets); +FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t *synth, int id); +FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets); +FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t *synth); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name(fluid_synth_t *synth, + const char *name); +FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset); +FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id); /* Reverb */ -FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, - double damping, double width, double level); -FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on); -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); -#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */ -#define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */ -#define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */ -#define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */ +FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, + double damping, double width, double level); +FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); +FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); +FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); +FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); + +FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); +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); /* Chorus */ @@ -157,107 +143,112 @@ FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth); /** * Chorus modulation waveform type. */ -enum fluid_chorus_mod { - FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ - FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ +enum fluid_chorus_mod +{ + FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ + FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ }; -FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, - double speed, double depth_ms, int type); -FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on); -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 */ +FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type); +FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); +FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); +FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); +FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); +FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); -#define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */ -#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */ -#define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */ -#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */ -#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */ +FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); +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(fluid_synth_t *synth); +FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ /* Audio and MIDI channels */ -FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth); +FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); /* Synthesis parameters */ -FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate); -FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain); -FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony); -FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t* synth); -FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth); +FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); +FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain); +FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony); +FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t *synth); -FLUIDSYNTH_API -int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method); +FLUIDSYNTH_API +int fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method); /** * Synthesis interpolation method. */ -enum fluid_interp { - FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ - FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ - FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ - FLUID_INTERP_7THORDER = 7 /**< Seventh-order interpolation */ -}; - -#define FLUID_INTERP_DEFAULT FLUID_INTERP_4THORDER /**< Default interpolation method from #fluid_interp. */ -#define FLUID_INTERP_HIGHEST FLUID_INTERP_7THORDER /**< Highest interpolation method from #fluid_interp. */ +enum fluid_interp +{ + FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ + FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ + FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ + FLUID_INTERP_7THORDER = 7, /**< Seventh-order interpolation */ + FLUID_INTERP_DEFAULT = FLUID_INTERP_4THORDER, /**< Default interpolation method */ + FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */ +}; /* Generator interface */ -FLUIDSYNTH_API -int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); -FLUIDSYNTH_API int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan, - int param, float value, - int absolute, int normalized); -FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); +FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, + int param, float value); +FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); /* Tuning */ -FLUIDSYNTH_API -int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch); FLUIDSYNTH_API -int fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply); -FLUIDSYNTH_API -int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch); +int fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); FLUIDSYNTH_API -int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply); -FLUIDSYNTH_API -int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, - int len, const int *keys, const double* pitch, int apply); -FLUIDSYNTH_API -int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog); +int fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *keys, const double *pitch, int apply); FLUIDSYNTH_API -int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, +int fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, int apply); -FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); FLUIDSYNTH_API -int fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply); -FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); -FLUIDSYNTH_API -int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); -FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch); +int fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply); +FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t *synth); +FLUIDSYNTH_API +int fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog); +FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch); /* Misc */ -FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t* synth); -FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); +FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); +FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); + + +/* Default modulators */ + +/** + * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. + */ +enum fluid_synth_add_mod +{ + FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_SYNTH_ADD, /**< Add (sum) modulator amounts */ +}; + +FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); +FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); /* @@ -265,48 +256,141 @@ FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); * * 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. + * any of the functions below to get the audio. */ -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); -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); -FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len, - int nin, float** in, - int nout, float** out); +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); +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); +FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len, + int nfx, float *fx[], + int nout, float *out[]); + + +/* Synthesizer's interface to handle SoundFont loaders */ + +FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); +FLUIDSYNTH_API fluid_voice_t *fluid_synth_alloc_voice(fluid_synth_t *synth, + fluid_sample_t *sample, + int channum, int key, int vel); +FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice); +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t *synth, + fluid_voice_t *buf[], int bufsize, int ID); +FLUIDSYNTH_API int fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event); + +/** + * Specifies the type of filter to use for the custom IIR filter + */ +enum fluid_iir_filter_type +{ + FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */ + FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */ + FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */ + FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; /** - * Type definition of the synthesizer's audio callback function. - * @param synth FluidSynth instance - * @param len Count of audio frames to synthesize - * @param out1 Array to store left channel of audio to - * @param loff Offset index in 'out1' for first sample - * @param lincr Increment between samples stored to 'out1' - * @param out2 Array to store right channel of audio to - * @param roff Offset index in 'out2' for first sample - * @param rincr Increment between samples stored to 'out2' + * Specifies optional settings to use for the custom IIR filter */ -typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, - void* out1, int loff, int lincr, - void* out2, int roff, int rincr); +enum fluid_iir_filter_flags +{ + FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */ + FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */ + FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */ +}; -/* Synthesizer's interface to handle SoundFont loaders */ +FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags); + + +/* LADSPA */ + +#ifdef LADSPA +FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); +#endif + + +/* API: Poly mono mode */ + +/** Interface to poly/mono mode variables + * + * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) + */ +enum fluid_channel_mode_flags +{ + FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */ + FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ +}; + +/** Indicates the breath mode a channel is set to */ +enum fluid_channel_breath_flags +{ + FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ + FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ + FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controler(MSB)triggers noteon/noteoff on the running note */ +}; + +/** Indicates the mode a basic channel is set to */ +enum fluid_basic_channel_modes +{ + FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ + FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */ + FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */ + FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */ + FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */ + FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan); + +FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_chan_out, + int *basic_val_out); +FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val); + +/** Interface to mono legato mode + * + * Indicates the legato mode a channel is set to + * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ +enum fluid_channel_legato_mode +{ + FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */ + FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */ + FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode); +FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode); + +/** Interface to portamento mode + * + * Indicates the portamento mode a channel is set to + */ +enum fluid_channel_portamento_mode +{ + FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ + FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes (#fluid_channel_portamento_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth, + int chan, int portamentomode); +FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth, + int chan, int *portamentomode); + +/* Interface to breath mode */ +FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth, + int chan, int breathmode); +FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth, + int chan, int *breathmode); -FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); -FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, - int channum, int key, int vel); -FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); -FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, - fluid_voice_t* buf[], int bufsize, int ID); -FLUIDSYNTH_API int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event); -FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth, - fluid_midi_router_t* router); #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/types.h b/libs/fluidsynth/fluidsynth/types.h index e956d818db..5ad29281ad 100644 --- a/libs/fluidsynth/fluidsynth/types.h +++ b/libs/fluidsynth/fluidsynth/types.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -35,7 +35,6 @@ extern "C" { typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */ typedef struct _fluid_synth_t fluid_synth_t; /**< Synthesizer instance */ -typedef struct _fluid_synth_channel_info_t fluid_synth_channel_info_t; /**< SoundFont channel info */ typedef struct _fluid_voice_t fluid_voice_t; /**< Synthesis voice instance */ typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */ typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */ @@ -49,17 +48,23 @@ typedef struct _fluid_midi_event_t fluid_midi_event_t; /**< MIDI event typedef struct _fluid_midi_driver_t fluid_midi_driver_t; /**< MIDI driver instance */ typedef struct _fluid_midi_router_t fluid_midi_router_t; /**< MIDI router instance */ typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; /**< MIDI router rule */ -typedef struct _fluid_hashtable_t fluid_cmd_handler_t; /**< Command handler */ +typedef struct _fluid_hashtable_t fluid_cmd_hash_t; /**< Command handler hash table */ typedef struct _fluid_shell_t fluid_shell_t; /**< Command shell */ typedef struct _fluid_server_t fluid_server_t; /**< TCP/IP shell server instance */ typedef struct _fluid_event_t fluid_event_t; /**< Sequencer event */ typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer instance */ typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */ typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */ +typedef struct _fluid_cmd_handler_t fluid_cmd_handler_t; /**< Shell Command Handler */ +#ifdef LADSPA +typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t; /**< LADSPA effects instance */ +#endif +typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t; /**< Callback struct to perform custom file loading of soundfonts */ typedef int fluid_istream_t; /**< Input stream descriptor */ typedef int fluid_ostream_t; /**< Output stream descriptor */ +typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/voice.h b/libs/fluidsynth/fluidsynth/voice.h index fe7ad8c1ab..f0644718b2 100644 --- a/libs/fluidsynth/fluidsynth/voice.h +++ b/libs/fluidsynth/fluidsynth/voice.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -36,26 +36,34 @@ extern "C" { */ -FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen); - /** * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators. */ -enum fluid_voice_add_mod { - FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */ - FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */ - FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */ +enum fluid_voice_add_mod +{ + FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */ + FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */ }; -FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode); -FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val); -FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen); -FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); +FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode); +FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t *voice, int gen); +FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t *voice, int gen, float val); +FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t *voice, int gen, float val); + +FLUIDSYNTH_API unsigned int fluid_voice_get_id(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_channel(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_playing(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_on(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t *s); +FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen); -FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice); -FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice); -FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s); - #ifdef __cplusplus } diff --git a/libs/fluidsynth/src/fluid_adsr_env.c b/libs/fluidsynth/src/fluid_adsr_env.c index 1d31fdb5e6..00bdd40f22 100644 --- a/libs/fluidsynth/src/fluid_adsr_env.c +++ b/libs/fluidsynth/src/fluid_adsr_env.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -20,19 +20,20 @@ #include "fluid_adsr_env.h" -void -fluid_adsr_env_set_data(fluid_adsr_env_t* env, - fluid_adsr_env_section_t section, - unsigned int count, - fluid_real_t coeff, - fluid_real_t increment, - fluid_real_t min, - fluid_real_t max) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data) { - env->data[section].count = count; - env->data[section].coeff = coeff; - env->data[section].increment = increment; - env->data[section].min = min; - env->data[section].max = max; + fluid_adsr_env_t *env = obj; + fluid_adsr_env_section_t section = param[0].i; + unsigned int count = param[1].i; + fluid_real_t coeff = param[2].real; + fluid_real_t increment = param[3].real; + fluid_real_t min = param[4].real; + fluid_real_t max = param[5].real; + + env->data[section].count = count; + env->data[section].coeff = coeff; + env->data[section].increment = increment; + env->data[section].min = min; + env->data[section].max = max; } diff --git a/libs/fluidsynth/src/fluid_adsr_env.h b/libs/fluidsynth/src/fluid_adsr_env.h index 31303a9ce9..9ed652d0b1 100644 --- a/libs/fluidsynth/src/fluid_adsr_env.h +++ b/libs/fluidsynth/src/fluid_adsr_env.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -27,135 +27,141 @@ /* * envelope data */ -struct _fluid_env_data_t { - unsigned int count; - fluid_real_t coeff; - fluid_real_t increment; - fluid_real_t min; - fluid_real_t max; +struct _fluid_env_data_t +{ + unsigned int count; + fluid_real_t coeff; + fluid_real_t increment; + 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 +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 }; typedef enum fluid_voice_envelope_index_t fluid_adsr_env_section_t; typedef struct _fluid_adsr_env_t fluid_adsr_env_t; -struct _fluid_adsr_env_t { - fluid_env_data_t data[FLUID_VOICE_ENVLAST]; - unsigned int count; - int section; - fluid_real_t val; /* the current value of the envelope */ +struct _fluid_adsr_env_t +{ + fluid_env_data_t data[FLUID_VOICE_ENVLAST]; + unsigned int count; + int section; + fluid_real_t val; /* the current value of the envelope */ }; /* For performance, all functions are inlined */ -static FLUID_INLINE void -fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv) +static FLUID_INLINE void +fluid_adsr_env_calc(fluid_adsr_env_t *env, int is_volenv) { - fluid_env_data_t* env_data; - fluid_real_t x; - - env_data = &env->data[env->section]; - - /* skip to the next section of the envelope if necessary */ - while (env->count >= env_data->count) - { - // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage - // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH] - if (env->section == FLUID_VOICE_ENVDECAY && is_volenv) - env->val = env_data->min * env_data->coeff; - - env_data = &env->data[++env->section]; - env->count = 0; - } + fluid_env_data_t *env_data; + fluid_real_t x; + + env_data = &env->data[env->section]; + + /* skip to the next section of the envelope if necessary */ + while(env->count >= env_data->count) + { + // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage + // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH] + if(env->section == FLUID_VOICE_ENVDECAY && is_volenv) + { + env->val = env_data->min * env_data->coeff; + } + + env_data = &env->data[++env->section]; + env->count = 0; + } + + /* calculate the envelope value and check for valid range */ + x = env_data->coeff * env->val + env_data->increment; + + if(x < env_data->min) + { + x = env_data->min; + env->section++; + env->count = 0; + } + else if(x > env_data->max) + { + x = env_data->max; + env->section++; + env->count = 0; + } + else + { + env->count++; + } + + env->val = x; - /* calculate the envelope value and check for valid range */ - x = env_data->coeff * env->val + env_data->increment; - if (x < env_data->min) - { - x = env_data->min; - env->section++; - env->count = 0; - } - else if (x > env_data->max) - { - x = env_data->max; - env->section++; - env->count = 0; - } - - env->val = x; - env->count++; } -/* This one cannot be inlined since it is referenced in +/* This one cannot be inlined since it is referenced in the event queue */ -void -fluid_adsr_env_set_data(fluid_adsr_env_t* env, - fluid_adsr_env_section_t section, - unsigned int count, - fluid_real_t coeff, - fluid_real_t increment, - fluid_real_t min, - fluid_real_t max); - -static inline void -fluid_adsr_env_reset(fluid_adsr_env_t* env) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data); + +static FLUID_INLINE void +fluid_adsr_env_reset(fluid_adsr_env_t *env) { - env->count = 0; - env->section = 0; - env->val = 0.0f; + env->count = 0; + env->section = 0; + env->val = 0.0f; } -static inline fluid_real_t -fluid_adsr_env_get_val(fluid_adsr_env_t* env) +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_val(fluid_adsr_env_t *env) { - return env->val; + return env->val; } -static inline void -fluid_adsr_env_set_val(fluid_adsr_env_t* env, fluid_real_t val) +static FLUID_INLINE void +fluid_adsr_env_set_val(fluid_adsr_env_t *env, fluid_real_t val) { - env->val = val; + env->val = val; } -static inline fluid_adsr_env_section_t -fluid_adsr_env_get_section(fluid_adsr_env_t* env) +static FLUID_INLINE fluid_adsr_env_section_t +fluid_adsr_env_get_section(fluid_adsr_env_t *env) { - return env->section; + return env->section; } -static inline void -fluid_adsr_env_set_section(fluid_adsr_env_t* env, +static FLUID_INLINE void +fluid_adsr_env_set_section(fluid_adsr_env_t *env, fluid_adsr_env_section_t section) { - env->section = section; - env->count = 0; + env->section = section; + env->count = 0; } -/* Used for determining which voice to kill. +/* Used for determining which voice to kill. Returns max amplitude from now, and forward in time. */ -static inline fluid_real_t -fluid_adsr_env_get_max_val(fluid_adsr_env_t* env) +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_max_val(fluid_adsr_env_t *env) { - if (env->section > FLUID_VOICE_ENVATTACK){ - return env->val * 1000; - } else { - return env->data[FLUID_VOICE_ENVATTACK].max; - } + if(env->section > FLUID_VOICE_ENVATTACK) + { + return env->val * 1000; + } + else + { + return env->data[FLUID_VOICE_ENVATTACK].max; + } } #endif diff --git a/libs/fluidsynth/src/fluid_chan.c b/libs/fluidsynth/src/fluid_chan.c index c6eb723146..49ef99ecbe 100644 --- a/libs/fluidsynth/src/fluid_chan.c +++ b/libs/fluidsynth/src/fluid_chan.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -36,256 +36,677 @@ #define SFONT_MASKVAL 0xFFC00000 -static void fluid_channel_init(fluid_channel_t* chan); +static void fluid_channel_init(fluid_channel_t *chan); -fluid_channel_t* -new_fluid_channel(fluid_synth_t* synth, int num) +fluid_channel_t * +new_fluid_channel(fluid_synth_t *synth, int num) { - fluid_channel_t* chan; + fluid_channel_t *chan; - chan = FLUID_NEW(fluid_channel_t); - if (chan == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } + chan = FLUID_NEW(fluid_channel_t); - chan->synth = synth; - chan->channum = num; - chan->preset = NULL; - chan->tuning = NULL; + if(chan == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + chan->synth = synth; + chan->channum = num; + chan->preset = NULL; + chan->tuning = NULL; - fluid_channel_init(chan); - fluid_channel_init_ctrl(chan, 0); + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); - return chan; + return chan; } static void -fluid_channel_init(fluid_channel_t* chan) +fluid_channel_init(fluid_channel_t *chan) { - fluid_preset_t *newpreset; - int prognum, banknum; - - chan->sostenuto_orderid = 0; - - chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; - prognum = 0; - banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; - - chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL - | prognum << PROG_SHIFTVAL; - - newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); - fluid_channel_set_preset(chan, newpreset); - - chan->interp_method = FLUID_INTERP_DEFAULT; - chan->tuning_bank = 0; - chan->tuning_prog = 0; - chan->nrpn_select = 0; - chan->nrpn_active = 0; + fluid_preset_t *newpreset; + int i, prognum, banknum; + + chan->sostenuto_orderid = 0; + /*--- Init poly/mono modes variables --------------------------------------*/ + chan->mode = 0; + chan->mode_val = 0; + + /* monophonic list initialization */ + for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++) + { + chan->monolist[i].next = i + 1; + } - if (chan->tuning) - { - fluid_tuning_unref (chan->tuning, 1); - chan->tuning = NULL; - } + chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */ + chan->i_last = chan->n_notes = 0; /* clears the list */ + chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */ + fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */ + /*---*/ + chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */ + chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */ + chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */ + /*--- End of poly/mono initialization --------------------------------------*/ + + chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + prognum = 0; + banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; + + chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL + | prognum << PROG_SHIFTVAL; + + newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); + fluid_channel_set_preset(chan, newpreset); + + chan->interp_method = FLUID_INTERP_DEFAULT; + chan->tuning_bank = 0; + chan->tuning_prog = 0; + chan->nrpn_select = 0; + chan->nrpn_active = 0; + + if(chan->tuning) + { + fluid_tuning_unref(chan->tuning, 1); + chan->tuning = NULL; + } } /* - @param is_all_ctrl_off if nonzero, only resets some controllers, according to - http://www.midi.org/techspecs/rp15.php + @param is_all_ctrl_off if nonzero, only resets some controllers, according to + http://www.midi.org/techspecs/rp15.php */ void -fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) +fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) { - int i; - - chan->key_pressure = 0; - chan->channel_pressure = 0; - chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ - - for (i = 0; i < GEN_LAST; i++) { - chan->gen[i] = 0.0f; - chan->gen_abs[i] = 0; - } - - if (is_all_ctrl_off) { - for (i = 0; i < ALL_SOUND_OFF; i++) { - if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) { - continue; - } - if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) { - continue; - } - if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || - i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) { - continue; - } - - fluid_channel_set_cc (chan, i, 0); - } - } - else { - for (i = 0; i < 128; i++) { - fluid_channel_set_cc (chan, i, 0); - } - } - - /* Set RPN controllers to NULL state */ - fluid_channel_set_cc (chan, RPN_LSB, 127); - fluid_channel_set_cc (chan, RPN_MSB, 127); - - /* Set NRPN controllers to NULL state */ - fluid_channel_set_cc (chan, NRPN_LSB, 127); - fluid_channel_set_cc (chan, NRPN_MSB, 127); - - /* Expression (MSB & LSB) */ - fluid_channel_set_cc (chan, EXPRESSION_MSB, 127); - fluid_channel_set_cc (chan, EXPRESSION_LSB, 127); - - if (!is_all_ctrl_off) { - - chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ - - /* Just like panning, a value of 64 indicates no change for sound ctrls */ - for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) { - fluid_channel_set_cc (chan, i, 64); - } - - /* Volume / initial attenuation (MSB & LSB) */ - fluid_channel_set_cc (chan, VOLUME_MSB, 100); - fluid_channel_set_cc (chan, VOLUME_LSB, 0); - - /* Pan (MSB & LSB) */ - fluid_channel_set_cc (chan, PAN_MSB, 64); - fluid_channel_set_cc (chan, PAN_LSB, 0); - - /* Reverb */ - /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ - /* Note: although XG standard specifies the default amount of reverb to - be 40, most people preferred having it at zero. - See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ - } + int i; + + chan->channel_pressure = 0; + chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ + + for(i = 0; i < GEN_LAST; i++) + { + chan->gen[i] = 0.0f; + chan->gen_abs[i] = 0; + } + + if(is_all_ctrl_off) + { + for(i = 0; i < ALL_SOUND_OFF; i++) + { + if(i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) + { + continue; + } + + if(i >= SOUND_CTRL1 && i <= SOUND_CTRL10) + { + continue; + } + + if(i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || + i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB || + i == BALANCE_MSB || i == BALANCE_LSB + ) + { + continue; + } + + fluid_channel_set_cc(chan, i, 0); + } + } + else + { + for(i = 0; i < 128; i++) + { + fluid_channel_set_cc(chan, i, 0); + } + + fluid_channel_clear_portamento(chan); /* Clear PTC receive */ + chan->previous_cc_breath = 0;/* Reset previous breath */ + } + + /* Reset polyphonic key pressure on all voices */ + for(i = 0; i < 128; i++) + { + fluid_channel_set_key_pressure(chan, i, 0); + } + + /* Set RPN controllers to NULL state */ + fluid_channel_set_cc(chan, RPN_LSB, 127); + fluid_channel_set_cc(chan, RPN_MSB, 127); + + /* Set NRPN controllers to NULL state */ + fluid_channel_set_cc(chan, NRPN_LSB, 127); + fluid_channel_set_cc(chan, NRPN_MSB, 127); + + /* Expression (MSB & LSB) */ + fluid_channel_set_cc(chan, EXPRESSION_MSB, 127); + fluid_channel_set_cc(chan, EXPRESSION_LSB, 127); + + if(!is_all_ctrl_off) + { + + chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ + + /* Just like panning, a value of 64 indicates no change for sound ctrls */ + for(i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) + { + fluid_channel_set_cc(chan, i, 64); + } + + /* Volume / initial attenuation (MSB & LSB) */ + fluid_channel_set_cc(chan, VOLUME_MSB, 100); + fluid_channel_set_cc(chan, VOLUME_LSB, 0); + + /* Pan (MSB & LSB) */ + fluid_channel_set_cc(chan, PAN_MSB, 64); + fluid_channel_set_cc(chan, PAN_LSB, 0); + + /* Balance (MSB & LSB) */ + fluid_channel_set_cc(chan, BALANCE_MSB, 64); + fluid_channel_set_cc(chan, BALANCE_LSB, 0); + + /* Reverb */ + /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ + /* Note: although XG standard specifies the default amount of reverb to + be 40, most people preferred having it at zero. + See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ + } } /* Only called by delete_fluid_synth(), so no need to queue a preset free event */ -int -delete_fluid_channel(fluid_channel_t* chan) +void +delete_fluid_channel(fluid_channel_t *chan) { - if (chan->preset) delete_fluid_preset (chan->preset); - FLUID_FREE(chan); - return FLUID_OK; + fluid_return_if_fail(chan != NULL); + + FLUID_FREE(chan); } /* FIXME - Calls fluid_channel_init() potentially in synthesis context */ void -fluid_channel_reset(fluid_channel_t* chan) +fluid_channel_reset(fluid_channel_t *chan) { - fluid_channel_init(chan); - fluid_channel_init_ctrl(chan, 0); + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); } /* Should only be called from synthesis context */ int -fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) +fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset) { + fluid_sfont_t *sfont; - fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); + if(chan->preset == preset) + { + return FLUID_OK; + } - if (chan->preset) { - fluid_sfont_t *sfont; - sfont = chan->preset->sfont; - delete_fluid_preset (chan->preset); - fluid_synth_sfont_unref (chan->synth, sfont); /* -- unref preset's SoundFont */ - } - - chan->preset = preset; + if(chan->preset) + { + sfont = chan->preset->sfont; + sfont->refcount--; + } + + fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); - fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum); + chan->preset = preset; + + if(preset) + { + sfont = preset->sfont; + sfont->refcount++; + } - return FLUID_OK; + fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); + + return FLUID_OK; } /* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */ void -fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfontnum, +fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfontnum, int banknum, int prognum) { - int oldval, newval, oldmask; + int oldval, newval, oldmask; - newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) - | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) - | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); + newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) + | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) + | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); - oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) - | ((banknum != -1) ? 0 : BANK_MASKVAL) - | ((prognum != -1) ? 0 : PROG_MASKVAL); + oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) + | ((banknum != -1) ? 0 : BANK_MASKVAL) + | ((prognum != -1) ? 0 : PROG_MASKVAL); - oldval = chan->sfont_bank_prog; - newval = (newval & ~oldmask) | (oldval & oldmask); - chan->sfont_bank_prog = newval; + oldval = chan->sfont_bank_prog; + newval = (newval & ~oldmask) | (oldval & oldmask); + chan->sfont_bank_prog = newval; } /* Set bank LSB 7 bits */ void -fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb) +fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb) { - int oldval, newval, style; - - style = chan->synth->bank_select; - if (style == FLUID_BANK_STYLE_GM || - style == FLUID_BANK_STYLE_GS) - return; /* ignored */ - - oldval = chan->sfont_bank_prog; - if (style == FLUID_BANK_STYLE_XG) - newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL); - else /* style == FLUID_BANK_STYLE_MMA */ - newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); - chan->sfont_bank_prog = newval; + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_GM || + style == FLUID_BANK_STYLE_GS) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_XG) + { + newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + + chan->sfont_bank_prog = newval; } /* Set bank MSB 7 bits */ void -fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb) +fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb) { - int oldval, newval, style; - - style = chan->synth->bank_select; - - if (style == FLUID_BANK_STYLE_XG) - { - /* XG bank, do drum-channel auto-switch */ - /* The number "120" was based on several keyboards having drums at 120 - 127, - reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ - chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; - return; - } - - if (style == FLUID_BANK_STYLE_GM || - chan->channel_type == CHANNEL_TYPE_DRUM) - return; /* ignored */ - - oldval = chan->sfont_bank_prog; - if (style == FLUID_BANK_STYLE_GS) - newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL); - else /* style == FLUID_BANK_STYLE_MMA */ - newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); - chan->sfont_bank_prog = newval; + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_XG) + { + /* XG bank, do drum-channel auto-switch */ + /* The number "120" was based on several keyboards having drums at 120 - 127, + reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ + chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + return; + } + + if(style == FLUID_BANK_STYLE_GM || + chan->channel_type == CHANNEL_TYPE_DRUM) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_GS) + { + newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); + } + + chan->sfont_bank_prog = newval; } /* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */ void -fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, +fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, int *bank, int *prog) { - int sfont_bank_prog; + int sfont_bank_prog; - sfont_bank_prog = chan->sfont_bank_prog; + sfont_bank_prog = chan->sfont_bank_prog; + + if(sfont) + { + *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; + } + + if(bank) + { + *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; + } + + if(prog) + { + *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; + } +} + +/** + * Updates legato/ staccato playing state + * The function is called: + * - on noteon before adding a note into the monolist. + * - on noteoff after removing a note out of the monolist. + * @param chan fluid_channel_t. +*/ +static void +fluid_channel_update_legato_staccato_state(fluid_channel_t *chan) +{ + /* Updates legato/ staccato playing state */ + if(chan->n_notes) + { + chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */ + } + else + { + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ + } +} + +/** + * Adds a note into the monophonic list. The function is part of the legato + * detector. fluid_channel_add_monolist() is intended to be called by + * fluid_synth_noteon_mono_LOCAL(). + * + * When a note is added at noteOn each element is use in the forward direction + * and indexed by i_last variable. + * + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127, 0=noteoff). + * @param onenote. When 1 the function adds the note but the monophonic list + * keeps only one note (used on noteOn poly). + * Note: i_last index keeps a trace of the most recent note added. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. + * + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). +*/ +void +fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel, unsigned char onenote) +{ + unsigned char i_last = chan->i_last; + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); + + if(chan->n_notes) + { + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + } + + /* moves i_last forward before writing new note */ + i_last = chan->monolist[i_last].next; + chan->i_last = i_last; /* now ilast indexes the last note */ + chan->monolist[i_last].note = key; /* we save note and velocity */ + chan->monolist[i_last].vel = vel; + + if(onenote) + { + /* clears monolist before one note addition */ + chan->i_first = i_last; + chan->n_notes = 0; + } + + if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST) + { + chan->n_notes++; /* updates n_notes */ + } + else + { + /* The end of buffer is reach. So circular motion for i_first */ + /* i_first index is moved forward */ + chan->i_first = chan->monolist[i_last].next; + } +} + +/** + * Searching a note in the monophonic list. The function is part of the legato + * detector. fluid_channel_search_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * The search starts from the first note in the list indexed by i_first + + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127) to search. + * @param i_prev pointer on returned index of the note prior the note to search. + * @return index of the note if find, FLUID_FAILED otherwise. + * + */ +int +fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev) +{ + short n = chan->n_notes; /* number of notes in monophonic list */ + short j, i = chan->i_first; /* searching starts from i_first included */ + + for(j = 0 ; j < n ; j++) + { + if(chan->monolist[i].note == key) + { + if(i == chan->i_first) + { + /* tracking index of the previous note (i_prev) */ + for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++) + { + j = chan->monolist[j].next; + } + + * i_prev = j; /* returns index of the previous note */ + } + + return i; /* returns index of the note to search */ + } + + * i_prev = i; /* tracking index of the previous note (i_prev) */ + i = chan->monolist[i].next; /* next element */ + } + + return FLUID_FAILED; /* not found */ +} + +/** + * removes a note from the monophonic list. The function is part of + * the legato detector. + * fluid_channel_remove_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * When a note is removed at noteOff the element concerned is fast unlinked + * and relinked after the i_last element. + * + * @param chan fluid_channel_t. + * @param + * i, index of the note to remove. If i is invalid or the list is + * empty, the function do nothing and returns FLUID_FAILED. + * @param + * On input, i_prev is a pointer on index of the note previous i. + * On output i_prev is a pointer on index of the note previous i if i is the last note + * in the list,FLUID_FAILED otherwise. When the returned index is valid it means + * a legato detection on noteoff. + * + * Note: the following variables in Channel keeps trace of the situation. + * - i_last index keeps a trace of the most recent note played even if + * the list is empty. + * - prev_note keeps a trace of the note removed if it is i_last. + * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. + * + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + */ +void +fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev) +{ + unsigned char i_last = chan->i_last; + + /* checks if index is valid */ + if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes) + { + * i_prev = FLUID_FAILED; + } + + /* The element is about to be removed and inserted between i_last and next */ + /* Note: when i is egal to i_last or egal to i_first, removing/inserting + isn't necessary */ + if(i == i_last) + { + /* Removing/Inserting isn't necessary */ + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + /* moves i_last backward to the previous */ + chan->i_last = *i_prev; /* i_last index is moved backward */ + } + else + { + /* i is before i_last */ + if(i == chan->i_first) + { + /* Removing/inserting isn't necessary */ + /* i_first index is moved forward to the next element*/ + chan->i_first = chan->monolist[i].next; + } + else + { + /* i is between i_first and i_last */ + /* Unlinks element i and inserts after i_last */ + chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */ + /*inserts i after i_last */ + chan->monolist[i].next = chan->monolist[i_last].next; + chan->monolist[i_last].next = i; + } + + * i_prev = FLUID_FAILED; + } + + chan->n_notes--; /* updates the number of note in the list */ + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); +} + +/** + * On noteOff on a polyphonic channel,the monophonic list is fully flushed. + * + * @param chan fluid_channel_t. + * Note: i_last index keeps a trace of the most recent note played even if + * the list is empty. + * prev_note keeps a trace of the note i_last . + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing. + */ +void fluid_channel_clear_monolist(fluid_channel_t *chan) +{ + /* keeps trace off the most recent note played */ + chan->prev_note = chan->monolist[chan->i_last].note; + + /* flushes the monolist */ + chan->i_first = chan->monolist[chan->i_last].next; + chan->n_notes = 0; + /* Update legato/ sataccato playing state */ + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ +} + +/** + * On noteOn on a polyphonic channel,adds the note into the monophonic list + * keeping only this note. + * @param + * chan fluid_channel_t. + * key, vel, note and velocity added in the monolist + * Note: i_last index keeps a trace of the most recent note inserted. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel) +{ + fluid_channel_add_monolist(chan, key, vel, 1); +} + +/** + * The function changes the state (Valid/Invalid) of the previous note played in + * a staccato manner (fluid_channel_prev_note()). + * When potamento mode 'each note' or 'staccato only' is selected, on next + * noteOn a portamento will be started from the most recent note played + * staccato. + * It will be possible that it isn't appropriate. To give the musician the + * possibility to choose a portamento from this note , prev_note will be forced + * to invalid state on noteOff if portamento pedal is Off. + * + * The function is intended to be called when the following event occurs: + * - On noteOff (in poly or mono mode), to mark prev_note invalid. + * - On Portamento Off(in poly or mono mode), to mark prev_note invalid. + * @param chan fluid_channel_t. + */ +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan) +{ + /* checks if the playing is staccato */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + + /* checks if portamento pedal is off */ + if(! fluid_channel_portamento(chan)) + { + /* forces prev_note invalid */ + fluid_channel_clear_prev_note(chan); + } + } + + /* else prev_note still remains valid for next fromkey portamento */ +} + +/** + * The function handles poly/mono commutation on legato pedal On/Off. + * @param chan fluid_channel_t. + * @param value, value of the CC legato. + */ +void fluid_channel_cc_legato(fluid_channel_t *chan, int value) +{ + /* Special handling of the monophonic list */ + if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */ + { + if(value < 64) /* legato is released */ + { + /* returns from monophonic to polyphonic with notes in the monophonic list */ + + /* The monophonic list is flushed keeping last note only + Note: i_last index keeps a trace of the most recent note played. + prev_note keeps a trace of the note i_last. + FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ + chan->i_first = chan->i_last; + chan->n_notes = 1; + } + else /* legato is depressed */ + { + /* Inters in monophonic from polyphonic with note in monophonic list */ + /* Stops the running note to remain coherent with Breath Sync mode */ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan)) + { + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + } +} + +/** + * The function handles CC Breath On/Off detection. When a channel is in + * Breath Sync mode and in monophonic playing, the breath controller allows + * to trigger noteon/noteoff note when the musician starts to breath (noteon) and + * stops to breath (noteoff). + * @param chan fluid_channel_t. + * @param value, value of the CC Breath.. + */ +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value) +{ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) && + (chan->n_notes)) + { + /* The monophonic list isn't empty */ + if((value > 0) && (chan->previous_cc_breath == 0)) + { + /* CC Breath On detection */ + fluid_synth_noteon_mono_staccato(chan->synth, chan->channum, + fluid_channel_last_note(chan), + fluid_channel_last_vel(chan)); + } + else if((value == 0) && (chan->previous_cc_breath > 0)) + { + /* CC Breath Off detection */ + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } - if (sfont) *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; - if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; - if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; + chan->previous_cc_breath = value; } diff --git a/libs/fluidsynth/src/fluid_chan.h b/libs/fluidsynth/src/fluid_chan.h index 85aa1ef00c..42d73df7b5 100644 --- a/libs/fluidsynth/src/fluid_chan.h +++ b/libs/fluidsynth/src/fluid_chan.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -25,6 +25,50 @@ #include "fluid_midi.h" #include "fluid_tuning.h" +/* The mononophonic list is part of the legato detector for monophonic mode */ +/* see fluid_synth_monopoly.c about a description of the legato detector device */ +/* Size of the monophonic list + - 1 is the minimum. it allows playing legato passage of any number + of notes on noteon only. + - Size above 1 allows playing legato on noteon but also on noteOff. + This allows the musician to play fast trills. + This feature is particularly usful when the MIDI input device is a keyboard. + Choosing a size of 10 is sufficient (because most musicians have only 10 + fingers when playing a monophonic instrument). +*/ +#define FLUID_CHANNEL_SIZE_MONOLIST 10 + +/* + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements. + Each element is linked forward at initialisation time. + - when a note is added at noteOn (see fluid_channel_add_monolist()) each + element is use in the forward direction and indexed by i_last variable. + - when a note is removed at noteOff (see fluid_channel_remove_monolist()), + the element concerned is fast unlinked and relinked after the i_last element. + + The most recent note added is indexed by i_last. + The most ancient note added is the first note indexed by i_first. i_first is + moving in the forward direction in a circular manner. + +*/ +struct mononote +{ + unsigned char next; /* next note */ + unsigned char note; /* note */ + unsigned char vel; /* velocity */ +}; + /* * fluid_channel_t * @@ -33,83 +77,93 @@ */ struct _fluid_channel_t { - fluid_mutex_t mutex; /* Lock for thread sensitive parameters */ - - fluid_synth_t* synth; /**< Parent synthesizer instance */ - int channum; /**< MIDI channel number */ - - int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ - fluid_preset_t* preset; /**< Selected preset */ - - int key_pressure; /**< MIDI key pressure */ - int channel_pressure; /**< MIDI channel pressure */ - int pitch_bend; /**< Current pitch bend value */ - int pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ - - int cc[128]; /**< MIDI controller values */ - - /* Sostenuto order id gives the order of SostenutoOn event. - This value is useful to known when the sostenuto pedal is depressed - (before or after a key note). We need to compare SostenutoOrderId with voice id. - */ - unsigned int sostenuto_orderid; - int interp_method; /**< Interpolation method (enum fluid_interp) */ - fluid_tuning_t* tuning; /**< Micro tuning */ - int tuning_bank; /**< Current tuning bank number */ - int tuning_prog; /**< Current tuning program number */ - - /* NRPN system */ - int nrpn_select; /* Generator ID of SoundFont NRPN message */ - int nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ - - /* 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(), which 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 SoundFont - * 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]; - - /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ - int channel_type; + fluid_synth_t *synth; /**< Parent synthesizer instance */ + int channum; /**< MIDI channel number */ + + /* Poly Mono variables see macro access description */ + int mode; /**< Poly Mono mode */ + int mode_val; /**< number of channel in basic channel group */ + + /* monophonic list - legato detector */ + unsigned char i_first; /**< First note index */ + unsigned char i_last; /**< most recent note index since the most recent add */ + unsigned char prev_note; /**< previous note of the most recent add/remove */ + unsigned char n_notes; /**< actual number of notes in the list */ + struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST]; /**< monophonic list */ + + unsigned char key_mono_sustained; /**< previous sustained monophonic note */ + unsigned char previous_cc_breath; /**< Previous Breath */ + enum fluid_channel_legato_mode legatomode; /**< legato mode */ + enum fluid_channel_portamento_mode portamentomode; /**< portamento mode */ + /*- End of Poly/mono variables description */ + + unsigned char cc[128]; /**< MIDI controller values from [0;127] */ + unsigned char key_pressure[128]; /**< MIDI polyphonic key pressure from [0;127] */ + + /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ + enum fluid_midi_channel_type channel_type; + enum fluid_interp interp_method; /**< Interpolation method (enum fluid_interp) */ + + unsigned char channel_pressure; /**< MIDI channel pressure from [0;127] */ + unsigned char pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ + short pitch_bend; /**< Current pitch bend value */ + /* Sostenuto order id gives the order of SostenutoOn event. + * This value is useful to known when the sostenuto pedal is depressed + * (before or after a key note). We need to compare SostenutoOrderId with voice id. + */ + unsigned int sostenuto_orderid; + + int tuning_bank; /**< Current tuning bank number */ + int tuning_prog; /**< Current tuning program number */ + fluid_tuning_t *tuning; /**< Micro tuning */ + + fluid_preset_t *preset; /**< Selected preset */ + int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ + + /* NRPN system */ + enum fluid_gen_type nrpn_select; /* Generator ID of SoundFont NRPN message */ + char nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ + /* 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(), which 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 SoundFont + * 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); -void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off); -int delete_fluid_channel(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); -void fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfont, +fluid_channel_t *new_fluid_channel(fluid_synth_t *synth, int num); +void fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off); +void delete_fluid_channel(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); +void fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfont, int bank, int prog); -void fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb); -void fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb); -void fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, +void fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb); +void fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb); +void fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, int *bank, int *prog); -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_get_preset(chan) ((chan)->preset) #define fluid_channel_set_cc(chan, num, val) \ ((chan)->cc[num] = (val)) #define fluid_channel_get_cc(chan, num) \ ((chan)->cc[num]) -#define fluid_channel_get_key_pressure(chan) \ - ((chan)->key_pressure) -#define fluid_channel_set_key_pressure(chan, val) \ - ((chan)->key_pressure = (val)) +#define fluid_channel_get_key_pressure(chan, key) \ + ((chan)->key_pressure[key]) +#define fluid_channel_set_key_pressure(chan, key, val) \ + ((chan)->key_pressure[key] = (val)) #define fluid_channel_get_channel_pressure(chan) \ ((chan)->channel_pressure) #define fluid_channel_set_channel_pressure(chan, val) \ @@ -138,6 +192,12 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan); ((chan)->tuning_prog) #define fluid_channel_set_tuning_prog(chan, prog) \ ((chan)->tuning_prog = (prog)) +#define fluid_channel_portamentotime(_c) \ + ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) +#define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) +#define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) +#define fluid_channel_clear_portamento(_c) ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE) +#define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) #define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) #define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64) #define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } @@ -146,4 +206,83 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan); #define fluid_channel_get_min_note_length_ticks(chan) \ ((chan)->synth->min_note_length_ticks) +/* Macros interface to poly/mono mode variables */ +#define MASK_BASICCHANINFOS (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED) +/* Set the basic channel infos for a MIDI basic channel */ +#define fluid_channel_set_basic_channel_info(chan,Infos) \ + (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) +/* Reset the basic channel infos for a MIDI basic channel */ +#define fluid_channel_reset_basic_channel_info(chan) (chan->mode &= ~MASK_BASICCHANINFOS) + +/* Macros interface to breath variables */ +#define FLUID_CHANNEL_BREATH_MASK (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC) +/* Set the breath infos for a MIDI channel */ +#define fluid_channel_set_breath_info(chan,BreathInfos) \ +(chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK)) +/* Get the breath infos for a MIDI channel */ +#define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK) + +/* Returns true when channel is mono or legato is on */ +#define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\ + fluid_channel_legato(chan)) + +/* Macros interface to monophonic list variables */ +#define INVALID_NOTE (255) +/* Returns true when a note is a valid note */ +#define fluid_channel_is_valid_note(n) (n != INVALID_NOTE) +/* Marks prev_note as invalid. */ +#define fluid_channel_clear_prev_note(chan) (chan->prev_note = INVALID_NOTE) + +/* Returns the most recent note from i_last entry of the monophonic list */ +#define fluid_channel_last_note(chan) (chan->monolist[chan->i_last].note) + +/* Returns the most recent velocity from i_last entry of the monophonic list */ +#define fluid_channel_last_vel(chan) (chan->monolist[chan->i_last].vel) + +/* + prev_note is used to determine fromkey_portamento as well as + fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()). + + prev_note is updated on noteOn/noteOff mono by the legato detector as this: + - On noteOn mono, before adding a new note into the monolist,the most + recent note in the list (i.e at i_last position) is kept in prev_note. + - Similarly, on noteOff mono , before removing a note out of the monolist, + the most recent note (i.e those at i_last position) is kept in prev_note. +*/ +#define fluid_channel_prev_note(chan) (chan->prev_note) + +/* Interface to poly/mono mode variables */ +enum fluid_channel_mode_flags_internal +{ + FLUID_CHANNEL_BASIC = 0x04, /**< if flag set the corresponding midi channel is a basic channel */ + FLUID_CHANNEL_ENABLED = 0x08, /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */ + + /* + FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato + state playing. + FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector: + - On noteOn, before inserting a new note into the monolist. + - On noteOff, after removing a note out of the monolist. + + - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL() + to play the current note legato or staccato. + - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL() + to play the current noteOff legato with the most recent note. + */ + /* bit7, 1: means legato playing , 0: means staccato playing */ + FLUID_CHANNEL_LEGATO_PLAYING = 0x80 +}; + +/* End of interface to monophonic list variables */ + +void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote); +int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev); +void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev); +void fluid_channel_clear_monolist(fluid_channel_t *chan); +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel); +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan); +void fluid_channel_cc_legato(fluid_channel_t *chan, int value); +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value); + + #endif /* _FLUID_CHAN_H */ diff --git a/libs/fluidsynth/src/fluid_chorus.c b/libs/fluidsynth/src/fluid_chorus.c index 4bead5ce2d..83a88d3826 100644 --- a/libs/fluidsynth/src/fluid_chorus.c +++ b/libs/fluidsynth/src/fluid_chorus.c @@ -1,13 +1,25 @@ -/* - * 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. +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe, Markus Nentwig and others. + * + * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA */ /* + based on a chrous implementation made by Juergen Mueller And Sundry Contributors in 1998 CHANGES @@ -78,8 +90,8 @@ * Set through MAX_SAMPLES_LN2. * For example: * MAX_SAMPLES_LN2=12 - * => MAX_SAMPLES=pow(2,12)=4096 - * => MAX_SAMPLES_ANDMASK=4095 + * => MAX_SAMPLES=pow(2,12-1)=2048 + * => MAX_SAMPLES_ANDMASK=2047 */ #define MAX_SAMPLES_LN2 12 @@ -103,131 +115,136 @@ #define INTERPOLATION_SAMPLES 5 /* Private data for SKEL file */ -struct _fluid_chorus_t { - int type; - fluid_real_t depth_ms; - fluid_real_t level; - fluid_real_t speed_Hz; - int number_blocks; - - 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]; +struct _fluid_chorus_t +{ + int type; + fluid_real_t depth_ms; + fluid_real_t level; + fluid_real_t speed_Hz; + int number_blocks; + + 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]; }; static void fluid_chorus_triangle(int *buf, int len, int depth); static void fluid_chorus_sine(int *buf, int len, int depth); -fluid_chorus_t* +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; - } + 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)); + }; + }; + }; - FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); + /* allocate lookup tables */ + chorus->lookup_tab = FLUID_ARRAY(int, (int)(chorus->sample_rate / MIN_SPEED_HZ)); - chorus->sample_rate = sample_rate; + if(chorus->lookup_tab == NULL) + { + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); + goto error_recovery; + } - /* Lookup table for the SI function (impulse response of an ideal low pass) */ + /* allocate sample buffer */ - /* i: Offset in terms of whole samples */ - for (i = 0; i < INTERPOLATION_SAMPLES; i++){ + chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES); - /* 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.; + if(chorus->chorusbuf == NULL) + { + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); + goto error_recovery; + } - } 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)); - }; + if(fluid_chorus_init(chorus) != FLUID_OK) + { + goto error_recovery; }; - }; - - /* 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; - return chorus; +error_recovery: + delete_fluid_chorus(chorus); - error_recovery: - delete_fluid_chorus(chorus); - return NULL; + return NULL; } void -delete_fluid_chorus(fluid_chorus_t* chorus) +delete_fluid_chorus(fluid_chorus_t *chorus) { - if (chorus == NULL) { - return; - } + fluid_return_if_fail(chorus != NULL); - if (chorus->chorusbuf != NULL) { FLUID_FREE(chorus->chorusbuf); - } - - if (chorus->lookup_tab != NULL) { FLUID_FREE(chorus->lookup_tab); - } - - FLUID_FREE(chorus); + FLUID_FREE(chorus); } int -fluid_chorus_init(fluid_chorus_t* chorus) +fluid_chorus_init(fluid_chorus_t *chorus) { - int i; + int i; - for (i = 0; i < MAX_SAMPLES; i++) { - chorus->chorusbuf[i] = 0.0; - } + for(i = 0; i < MAX_SAMPLES; i++) + { + chorus->chorusbuf[i] = 0.0; + } - /* initialize the chorus with the default settings */ - fluid_chorus_set (chorus, FLUID_CHORUS_SET_ALL, FLUID_CHORUS_DEFAULT_N, - FLUID_CHORUS_DEFAULT_LEVEL, FLUID_CHORUS_DEFAULT_SPEED, - FLUID_CHORUS_DEFAULT_DEPTH, FLUID_CHORUS_MOD_SINE); - return FLUID_OK; + return FLUID_OK; } void -fluid_chorus_reset(fluid_chorus_t* chorus) +fluid_chorus_reset(fluid_chorus_t *chorus) { - fluid_chorus_init(chorus); + fluid_chorus_init(chorus); } /** @@ -243,223 +260,272 @@ fluid_chorus_reset(fluid_chorus_t* chorus) * @param type Chorus waveform type (#fluid_chorus_mod) */ void -fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, - float speed, float depth_ms, int type) +fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type) { - int modulation_depth_samples; - int i; - - if (set & FLUID_CHORUS_SET_NR) chorus->number_blocks = nr; - if (set & FLUID_CHORUS_SET_LEVEL) chorus->level = level; - if (set & FLUID_CHORUS_SET_SPEED) chorus->speed_Hz = speed; - if (set & FLUID_CHORUS_SET_DEPTH) chorus->depth_ms = depth_ms; - if (set & FLUID_CHORUS_SET_TYPE) chorus->type = type; - - if (chorus->number_blocks < 0) { - fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); - chorus->number_blocks = 0; - } else if (chorus->number_blocks > MAX_CHORUS) { - fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", - MAX_CHORUS); - chorus->number_blocks = MAX_CHORUS; - } - - if (chorus->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->speed_Hz = MIN_SPEED_HZ; - } else if (chorus->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->speed_Hz = MAX_SPEED_HZ; - } - - if (chorus->depth_ms < 0.0) { - fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); - chorus->depth_ms = 0.0; - } - /* Depth: Check for too high value through modulation_depth_samples. */ - - if (chorus->level < 0.0) { - fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); - chorus->level = 0.0; - } else if (chorus->level > 10) { - fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " - "Setting it to 0.1."); - chorus->level = 0.1; - } - - /* The modulating LFO goes through a full period every x samples: */ - chorus->modulation_period_samples = chorus->sample_rate / chorus->speed_Hz; - - /* The variation in delay time is x: */ - modulation_depth_samples = (int) - (chorus->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; + int modulation_depth_samples; + int i; + + if(set & FLUID_CHORUS_SET_NR) + { + chorus->number_blocks = nr; + } + + if(set & FLUID_CHORUS_SET_LEVEL) + { + chorus->level = level; + } + + if(set & FLUID_CHORUS_SET_SPEED) + { + chorus->speed_Hz = speed; + } + + if(set & FLUID_CHORUS_SET_DEPTH) + { + chorus->depth_ms = depth_ms; + } + + if(set & FLUID_CHORUS_SET_TYPE) + { + chorus->type = type; + } + + if(chorus->number_blocks < 0) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); + chorus->number_blocks = 0; + } + else if(chorus->number_blocks > MAX_CHORUS) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", + MAX_CHORUS); + chorus->number_blocks = MAX_CHORUS; + } + + if(chorus->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->speed_Hz = MIN_SPEED_HZ; + } + else if(chorus->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->speed_Hz = MAX_SPEED_HZ; + } + + if(chorus->depth_ms < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); + chorus->depth_ms = 0.0; + } + + /* Depth: Check for too high value through modulation_depth_samples. */ + + if(chorus->level < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); + chorus->level = 0.0; + } + else if(chorus->level > 10) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " + "Setting it to 0.1."); + chorus->level = 0.1; + } + + /* The modulating LFO goes through a full period every x samples: */ + chorus->modulation_period_samples = chorus->sample_rate / chorus->speed_Hz; + + /* The variation in delay time is x: */ + modulation_depth_samples = (int) + (chorus->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; + // set depth to maximum to avoid spamming console with above warning + chorus->depth_ms = (modulation_depth_samples * 1000) / chorus->sample_rate; + } + + /* initialize LFO table */ + switch(chorus->type) + { + default: + FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); + chorus->type = FLUID_CHORUS_MOD_SINE; + /* fall-through */ + + case FLUID_CHORUS_MOD_SINE: + fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, + modulation_depth_samples); + break; + + case FLUID_CHORUS_MOD_TRIANGLE: + fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples, + modulation_depth_samples); + break; + } + + 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; } -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_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; + int sample_index; + int i; + fluid_real_t d_in, d_out; - for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { - d_in = in[sample_index]; - d_out = 0.0f; + 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; + /* 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; + /* 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. */ + 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]]); + /* 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; + int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; - /* modulo divide by INTERPOLATION_SUBSAMPLES */ - pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; + /* 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!*/ + 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]; + /* 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 */ + pos_samples--; + }; - d_out *= chorus->level; + /* Cycle the phase of the modulating LFO */ + chorus->phase[i]++; - /* Add the chorus sum d_out to output */ - left_out[sample_index] += d_out; - right_out[sample_index] += d_out; + chorus->phase[i] %= (chorus->modulation_period_samples); + } /* foreach chorus block */ - /* Move forward in circular buffer */ - chorus->counter++; - chorus->counter %= MAX_SAMPLES; + d_out *= chorus->level; - } /* foreach sample */ + /* 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 ... (replaces sample data instead of mixing) */ -void fluid_chorus_processreplace(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 sample_index; - int i; - fluid_real_t d_in, d_out; + int sample_index; + int i; + fluid_real_t d_in, d_out; - for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { - d_in = in[sample_index]; - d_out = 0.0f; + 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; + /* 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; + /* 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]]); - for (i = 0; i < chorus->number_blocks; i++) { - int ii; - /* Calculate the delay in subsamples for the delay line of chorus block nr. */ + int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; - /* 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]]); + /* modulo divide by INTERPOLATION_SUBSAMPLES */ + pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; - int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; + 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!*/ - /* modulo divide by INTERPOLATION_SUBSAMPLES */ - pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; + /* 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]; - 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!*/ + pos_samples--; + }; - /* 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]; + /* Cycle the phase of the modulating LFO */ + chorus->phase[i]++; - pos_samples--; - }; - /* Cycle the phase of the modulating LFO */ - chorus->phase[i]++; - chorus->phase[i] %= (chorus->modulation_period_samples); - } /* foreach chorus block */ + chorus->phase[i] %= (chorus->modulation_period_samples); + } /* foreach chorus block */ - d_out *= chorus->level; + d_out *= chorus->level; - /* Store the chorus sum d_out to output */ - left_out[sample_index] = d_out; - right_out[sample_index] = d_out; + /* Store 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; + /* Move forward in circular buffer */ + chorus->counter++; + chorus->counter %= MAX_SAMPLES; - } /* foreach sample */ + } /* foreach sample */ } /* Purpose: @@ -474,15 +540,25 @@ void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, static 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]); - } + int i; + double angle, incr, mult; + + /* Pre-calculate increment between angles. */ + incr = (2. * M_PI) / (double)len; + + /* Pre-calculate 'depth' multiplier. */ + mult = (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES; + + /* Initialize to zero degrees. */ + angle = 0.; + + /* Build sine modulation waveform */ + for(i = 0; i < len; i++) + { + buf[i] = (int)((1. + sin(angle)) * mult) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; + + angle += incr; + } } /* Purpose: @@ -492,15 +568,26 @@ fluid_chorus_sine(int *buf, int len, int depth) static 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; - } + int *il = buf; + int *ir = buf + len - 1; + int ival; + double val, incr; + + /* Pre-calculate increment for the ramp. */ + incr = 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES; + + /* Initialize first value */ + val = 0. - 3. * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; + + /* Build triangular modulation waveform */ + while(il <= ir) + { + /* Assume 'val' to be always negative for rounding mode */ + ival = (int)(val - 0.5); + + *il++ = ival; + *ir-- = ival; + + val += incr; + } } diff --git a/libs/fluidsynth/src/fluid_chorus.h b/libs/fluidsynth/src/fluid_chorus.h index 3422fa94b2..8a6734aa0a 100644 --- a/libs/fluidsynth/src/fluid_chorus.h +++ b/libs/fluidsynth/src/fluid_chorus.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -30,31 +30,35 @@ typedef struct _fluid_chorus_t fluid_chorus_t; /** Flags for fluid_chorus_set() */ typedef enum { - FLUID_CHORUS_SET_NR = 1 << 0, - FLUID_CHORUS_SET_LEVEL = 1 << 1, - FLUID_CHORUS_SET_SPEED = 1 << 2, - FLUID_CHORUS_SET_DEPTH = 1 << 3, - FLUID_CHORUS_SET_TYPE = 1 << 4, -} fluid_chorus_set_t; + FLUID_CHORUS_SET_NR = 1 << 0, + FLUID_CHORUS_SET_LEVEL = 1 << 1, + FLUID_CHORUS_SET_SPEED = 1 << 2, + FLUID_CHORUS_SET_DEPTH = 1 << 3, + FLUID_CHORUS_SET_TYPE = 1 << 4, -/** Value for fluid_chorus_set() which sets all chorus parameters. */ -#define FLUID_CHORUS_SET_ALL 0x1F + /** Value for fluid_chorus_set() which sets all chorus parameters. */ + FLUID_CHORUS_SET_ALL = FLUID_CHORUS_SET_NR + | FLUID_CHORUS_SET_LEVEL + | FLUID_CHORUS_SET_SPEED + | FLUID_CHORUS_SET_DEPTH + | FLUID_CHORUS_SET_TYPE, +} fluid_chorus_set_t; /* * chorus */ -fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate); -void delete_fluid_chorus(fluid_chorus_t* chorus); -int fluid_chorus_init(fluid_chorus_t* chorus); -void fluid_chorus_reset(fluid_chorus_t* chorus); - -void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, - float speed, float depth_ms, int type); - -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); +fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate); +void delete_fluid_chorus(fluid_chorus_t *chorus); +int fluid_chorus_init(fluid_chorus_t *chorus); +void fluid_chorus_reset(fluid_chorus_t *chorus); + +void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type); + +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); diff --git a/libs/fluidsynth/src/fluid_conv.c b/libs/fluidsynth/src/fluid_conv.c index 1a790cfbfb..555dd61367 100644 --- a/libs/fluidsynth/src/fluid_conv.c +++ b/libs/fluidsynth/src/fluid_conv.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -20,15 +20,17 @@ #include "fluid_conv.h" +#define FLUID_CENTS_HZ_SIZE 1200 +#define FLUID_VEL_CB_SIZE 128 +#define FLUID_CB_AMP_SIZE 1441 +#define FLUID_PAN_SIZE 1002 /* 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]; +static fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; +static fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; +static fluid_real_t fluid_concave_tab[FLUID_VEL_CB_SIZE]; +static fluid_real_t fluid_convex_tab[FLUID_VEL_CB_SIZE]; +static fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; /* * void fluid_synth_init @@ -38,58 +40,52 @@ fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; 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); - } + 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); + } + + /* 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[FLUID_VEL_CB_SIZE - 1] = 1.0; + + /* convex unipolar positive transform curve */ + fluid_convex_tab[0] = 0; + fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.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 < FLUID_VEL_CB_SIZE - 1; i++) + { + x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((i * i) / (fluid_real_t)((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10; + fluid_convex_tab[i] = (fluid_real_t)(1.0 - x); + fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = (fluid_real_t) x; + } + + /* initialize the pan conversion table */ + x = M_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); + } } /* @@ -98,35 +94,62 @@ fluid_conversion_config(void) 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 */ - } + 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 */ + } } /* @@ -135,55 +158,46 @@ fluid_ct2hz_real(fluid_real_t cents) 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); + /* 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 + * in: a value between 0 and 1440, 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]; -} + /* + * 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. + */ -/* - * 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]; + /* 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]; } /* @@ -192,21 +206,27 @@ fluid_atten2amp(fluid_real_t atten) 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); + /* 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); } /* @@ -215,14 +235,26 @@ fluid_tc2sec_delay(fluid_real_t tc) 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); + /* 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); } /* @@ -231,8 +263,8 @@ fluid_tc2sec_attack(fluid_real_t tc) fluid_real_t fluid_tc2sec(fluid_real_t tc) { - /* No range checking here! */ - return (fluid_real_t) pow(2.0, (double) tc / 1200.0); + /* No range checking here! */ + return (fluid_real_t) pow(2.0, (double) tc / 1200.0); } /* @@ -241,14 +273,26 @@ fluid_tc2sec(fluid_real_t tc) 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); + /* 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); } /* @@ -259,7 +303,7 @@ fluid_tc2sec_release(fluid_real_t tc) fluid_real_t fluid_act2hz(fluid_real_t c) { - return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0)); + return (fluid_real_t)(8.176 * pow(2.0, (double) c / 1200.0)); } /* @@ -270,7 +314,7 @@ fluid_act2hz(fluid_real_t c) fluid_real_t fluid_hz2ct(fluid_real_t f) { - return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0)); + return (fluid_real_t)(6900 + 1200 * log(f / 440.0) / M_LN2); } /* @@ -279,16 +323,53 @@ fluid_hz2ct(fluid_real_t f) 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)]; - } + 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)]; + } +} + +/* + * Return the amount of attenuation based on the balance for the specified + * channel. If balance is negative (turned toward left channel, only the right + * channel is attenuated. If balance is positive, only the left channel is + * attenuated. + * + * @params balance left/right balance, range [-960;960] in absolute centibels + * @return amount of attenuation [0.0;1.0] + */ +fluid_real_t fluid_balance(fluid_real_t balance, int left) +{ + /* This is the most common case */ + if(balance == 0) + { + return 1.0f; + } + + if((left && balance < 0) || (!left && balance > 0)) + { + return 1.0f; + } + + if(balance < 0) + { + balance = -balance; + } + + return fluid_cb2amp(balance); } /* @@ -297,12 +378,16 @@ fluid_pan(fluid_real_t c, int left) 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]; + if(val < 0) + { + return 0; + } + else if(val >= FLUID_VEL_CB_SIZE) + { + return 1; + } + + return fluid_concave_tab[(int) val]; } /* @@ -311,10 +396,14 @@ fluid_concave(fluid_real_t val) 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]; + if(val < 0) + { + return 0; + } + else if(val >= FLUID_VEL_CB_SIZE) + { + return 1; + } + + return fluid_convex_tab[(int) val]; } diff --git a/libs/fluidsynth/src/fluid_conv.h b/libs/fluidsynth/src/fluid_conv.h index 29793c3359..d84a321c6c 100644 --- a/libs/fluidsynth/src/fluid_conv.h +++ b/libs/fluidsynth/src/fluid_conv.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -23,24 +23,42 @@ #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 +/* + Attenuation range in centibels. + Attenuation range is the dynamic range of the volume envelope generator + from 0 to the end of attack segment. + fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. + However the spec makes no distinction between 16 or 24 bit synths, so use + 96 dB here. -/* 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. */ -/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/ -#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/ + Note about usefulness of 24 bits: + 1)Even fluidsynth is a 24 bit synth, this format is only relevant if + the sample format coming from the soundfont is 24 bits and the audio sample format + choosen by the application (audio.sample.format) is not 16 bits. + + 2)When the sample soundfont is 16 bits, the internal 24 bits number have + 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of + this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) + even if this sample is produced by the audio driver using an audio sample format + compatible for a 24 bit DAC. + + 3)When the audio sample format settings is 16 bits (audio.sample.format), the + audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB + even if the initial sample comes from a 24 bits soundfont. + + In both cases (2) or (3), the real dynamic range is only 96 dB. + + Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): + - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). + - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). + */ +#define FLUID_PEAK_ATTENUATION 960.0f 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); @@ -48,16 +66,8 @@ 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_balance(fluid_real_t balance, 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/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c index c395218411..6e19eb71af 100644 --- a/libs/fluidsynth/src/fluid_defsfont.c +++ b/libs/fluidsynth/src/fluid_defsfont.c @@ -6,16 +6,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -23,69 +23,92 @@ #include "fluid_defsfont.h" -/* Todo: Get rid of that 'include' */ +#include "fluid_sfont.h" #include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_samplecache.h" + +/* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and + * instrument level in a soundfont. We apply this factor when loading the generator values to stay + * compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */ +#define EMU_ATTENUATION_FACTOR (0.4f) + +/* Dynamic sample loading functions */ +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static void unload_sample(fluid_sample_t *sample); +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan); +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason); +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone); +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx); + /*************************************************************** * * SFONT LOADER */ -fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings) +/** + * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader(). + * By default every synth instance has an initial default soundfont loader instance. + * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks(). + * + * @param settings A settings instance obtained by new_fluid_settings() + * @return A default soundfont2 loader struct + */ +fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings) { - fluid_sfloader_t* loader; + fluid_sfloader_t *loader; + fluid_return_val_if_fail(settings != NULL, NULL); - loader = FLUID_NEW(fluid_sfloader_t); - if (loader == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } + loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); - loader->data = settings; - loader->free = delete_fluid_defsfloader; - loader->load = fluid_defsfloader_load; + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } - return loader; -} + fluid_sfloader_set_data(loader, settings); -int delete_fluid_defsfloader(fluid_sfloader_t* loader) -{ - if (loader) { - FLUID_FREE(loader); - } - return FLUID_OK; + return loader; } -fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename) +fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename) { - fluid_defsfont_t* defsfont; - fluid_sfont_t* sfont; + fluid_defsfont_t *defsfont; + fluid_sfont_t *sfont; - defsfont = new_fluid_defsfont(loader->data); + defsfont = new_fluid_defsfont(fluid_sfloader_get_data(loader)); - if (defsfont == NULL) { - return NULL; - } + if(defsfont == NULL) + { + return NULL; + } - if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) { - delete_fluid_defsfont(defsfont); - return NULL; - } + sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name, + fluid_defsfont_sfont_get_preset, + fluid_defsfont_sfont_iteration_start, + fluid_defsfont_sfont_iteration_next, + fluid_defsfont_sfont_delete); - sfont = FLUID_NEW(fluid_sfont_t); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } + if(sfont == NULL) + { + delete_fluid_defsfont(defsfont); + return NULL; + } + + fluid_sfont_set_data(sfont, defsfont); - 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; + defsfont->sfont = sfont; + + if(fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED) + { + fluid_sfont_delete_internal(sfont); + return NULL; + } - return sfont; + return sfont; } @@ -95,621 +118,487 @@ fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* file * PUBLIC INTERFACE */ -int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont) +int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont) { - if (delete_fluid_defsfont(sfont->data) != 0) { - return -1; - } - FLUID_FREE(sfont); - return 0; -} + if(delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK) + { + return -1; + } -char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont) -{ - return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data); + delete_fluid_sfont(sfont); + return 0; } -#if 0 -fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s) +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont) { - /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */ - return NULL; + return fluid_defsfont_get_name(fluid_sfont_get_data(sfont)); } -#endif -fluid_preset_t* -fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) +fluid_preset_t * +fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) { - fluid_preset_t* preset = NULL; - fluid_defpreset_t* defpreset; - fluid_defsfont_t* defsfont = sfont->data; - - defpreset = fluid_defsfont_get_preset(defsfont, bank, prenum); - - if (defpreset == NULL) { - return NULL; - } - - if (defsfont->preset_stack_size > 0) { - defsfont->preset_stack_size--; - preset = defsfont->preset_stack[defsfont->preset_stack_size]; - } - if (!preset) - preset = FLUID_NEW(fluid_preset_t); - if (!preset) { - 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; + return fluid_defsfont_get_preset(fluid_sfont_get_data(sfont), bank, prenum); } -void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont) +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont) { - fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data); + fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont)); } -int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont) { - 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); + return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont)); } -int fluid_defpreset_preset_delete(fluid_preset_t* preset) +void fluid_defpreset_preset_delete(fluid_preset_t *preset) { - fluid_defpreset_t* defpreset = preset ? preset->data : NULL; - fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL; + fluid_defsfont_t *defsfont; + fluid_defpreset_t *defpreset; - if (sfont && sfont->preset_stack_size < sfont->preset_stack_capacity) { - sfont->preset_stack[sfont->preset_stack_size] = preset; - sfont->preset_stack_size++; - } - else - FLUID_FREE(preset); + defsfont = fluid_sfont_get_data(preset->sfont); + defpreset = fluid_preset_get_data(preset); + + if(defsfont) + { + defsfont->preset = fluid_list_remove(defsfont->preset, defpreset); + } - return 0; + delete_fluid_defpreset(defpreset); + delete_fluid_preset(preset); } -char* fluid_defpreset_preset_get_name(fluid_preset_t* preset) +const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset) { - return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_name(fluid_preset_get_data(preset)); } -int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset) +int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset) { - return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_banknum(fluid_preset_get_data(preset)); } -int fluid_defpreset_preset_get_num(fluid_preset_t* preset) +int fluid_defpreset_preset_get_num(fluid_preset_t *preset) { - return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_num(fluid_preset_get_data(preset)); } -int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, - int chan, int key, int vel) +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); + return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel); } - - /*************************************************************** * - * CACHED SAMPLEDATA LOADER + * SFONT */ -typedef struct _fluid_cached_sampledata_t { - struct _fluid_cached_sampledata_t *next; - - char* filename; - time_t modification_time; - int num_references; - int mlock; - - const short* sampledata; - unsigned int samplesize; -} fluid_cached_sampledata_t; +/* + * new_fluid_defsfont + */ +fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings) +{ + fluid_defsfont_t *defsfont; -static fluid_cached_sampledata_t* all_cached_sampledata = NULL; -static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT; + defsfont = FLUID_NEW(fluid_defsfont_t); -static int fluid_get_file_modification_time(char *filename, time_t *modification_time) -{ -#if defined(WIN32) || defined(__OS2__) - *modification_time = 0; - return FLUID_OK; -#else - struct stat buf; + if(defsfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } - if (stat(filename, &buf) == -1) { - return FLUID_FAILED; - } + FLUID_MEMSET(defsfont, 0, sizeof(*defsfont)); - *modification_time = buf.st_mtime; - return FLUID_OK; -#endif -} + fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock); + fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples); -static int fluid_cached_sampledata_load(char *filename, unsigned int samplepos, - unsigned int samplesize, short **sampledata, int try_mlock) -{ - fluid_file fd = NULL; - short *loaded_sampledata = NULL; - fluid_cached_sampledata_t* cached_sampledata = NULL; - time_t modification_time; - - fluid_mutex_lock(cached_sampledata_mutex); - - if (fluid_get_file_modification_time(filename, &modification_time) == FLUID_FAILED) { - FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file."); - modification_time = 0; - } - - for (cached_sampledata = all_cached_sampledata; cached_sampledata; cached_sampledata = cached_sampledata->next) { - if (strcmp(filename, cached_sampledata->filename)) - continue; - if (cached_sampledata->modification_time != modification_time) - continue; - if (cached_sampledata->samplesize != samplesize) { - FLUID_LOG(FLUID_ERR, "Cached size of soundfont doesn't match actual size of soundfont (cached: %u. actual: %u)", - cached_sampledata->samplesize, samplesize); - continue; - } - - if (try_mlock && !cached_sampledata->mlock) { - if (fluid_mlock(cached_sampledata->sampledata, samplesize) != 0) - FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); - else - cached_sampledata->mlock = try_mlock; - } - - cached_sampledata->num_references++; - loaded_sampledata = (short*) cached_sampledata->sampledata; - goto success_exit; - } - - fd = FLUID_FOPEN(filename, "rb"); - if (fd == NULL) { - FLUID_LOG(FLUID_ERR, "Can't open soundfont file"); - goto error_exit; - } - if (FLUID_FSEEK(fd, samplepos, SEEK_SET) == -1) { - perror("error"); - FLUID_LOG(FLUID_ERR, "Failed to seek position in data file"); - goto error_exit; - } - - - loaded_sampledata = (short*) FLUID_MALLOC(samplesize); - if (loaded_sampledata == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_exit; - } - if (FLUID_FREAD(loaded_sampledata, 1, samplesize, fd) < samplesize) { - FLUID_LOG(FLUID_ERR, "Failed to read sample data"); - goto error_exit; - } - - FLUID_FCLOSE(fd); - fd = NULL; - - - cached_sampledata = (fluid_cached_sampledata_t*) FLUID_MALLOC(sizeof(fluid_cached_sampledata_t)); - if (cached_sampledata == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory."); - goto error_exit; - } - - /* Lock the memory to disable paging. It's okay if this fails. It - probably means that the user doesn't have to required permission. */ - cached_sampledata->mlock = 0; - if (try_mlock) { - if (fluid_mlock(loaded_sampledata, samplesize) != 0) - FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); - else - cached_sampledata->mlock = try_mlock; - } - - /* If this machine is big endian, the sample have to byte swapped */ - if (FLUID_IS_BIG_ENDIAN) { - unsigned char* cbuf; - unsigned char hi, lo; - unsigned int i, j; - short s; - cbuf = (unsigned char*)loaded_sampledata; - for (i = 0, j = 0; j < samplesize; i++) { - lo = cbuf[j++]; - hi = cbuf[j++]; - s = (hi << 8) | lo; - loaded_sampledata[i] = s; - } - } - - cached_sampledata->filename = (char*) FLUID_MALLOC(strlen(filename) + 1); - if (cached_sampledata->filename == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory."); - goto error_exit; - } - - sprintf(cached_sampledata->filename, "%s", filename); - cached_sampledata->modification_time = modification_time; - cached_sampledata->num_references = 1; - cached_sampledata->sampledata = loaded_sampledata; - cached_sampledata->samplesize = samplesize; - - cached_sampledata->next = all_cached_sampledata; - all_cached_sampledata = cached_sampledata; - - - success_exit: - fluid_mutex_unlock(cached_sampledata_mutex); - *sampledata = loaded_sampledata; - return FLUID_OK; - - error_exit: - if (fd != NULL) { - FLUID_FCLOSE(fd); - } - if (loaded_sampledata != NULL) { - FLUID_FREE(loaded_sampledata); - } - - if (cached_sampledata != NULL) { - if (cached_sampledata->filename != NULL) { - FLUID_FREE(cached_sampledata->filename); - } - FLUID_FREE(cached_sampledata); - } - - fluid_mutex_unlock(cached_sampledata_mutex); - *sampledata = NULL; - return FLUID_FAILED; + return defsfont; } -static int fluid_cached_sampledata_unload(const short *sampledata) +/* + * delete_fluid_defsfont + */ +int delete_fluid_defsfont(fluid_defsfont_t *defsfont) { - fluid_cached_sampledata_t* prev = NULL; - fluid_cached_sampledata_t* cached_sampledata; + fluid_list_t *list; + fluid_preset_t *preset; + fluid_sample_t *sample; - fluid_mutex_lock(cached_sampledata_mutex); - cached_sampledata = all_cached_sampledata; + fluid_return_val_if_fail(defsfont != NULL, FLUID_OK); - while (cached_sampledata != NULL) { - if (sampledata == cached_sampledata->sampledata) { - - cached_sampledata->num_references--; - - if (cached_sampledata->num_references == 0) { - if (cached_sampledata->mlock) - fluid_munlock(cached_sampledata->sampledata, cached_sampledata->samplesize); - FLUID_FREE((short*) cached_sampledata->sampledata); - FLUID_FREE(cached_sampledata->filename); + /* Check that no samples are currently used */ + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = (fluid_sample_t *) fluid_list_get(list); - if (prev != NULL) { - prev->next = cached_sampledata->next; - } else { - all_cached_sampledata = cached_sampledata->next; + if(sample->refcount != 0) + { + return FLUID_FAILED; } + } - FLUID_FREE(cached_sampledata); - } + if(defsfont->filename != NULL) + { + FLUID_FREE(defsfont->filename); + } - goto success_exit; + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + delete_fluid_sample((fluid_sample_t *) fluid_list_get(list)); } - prev = cached_sampledata; - cached_sampledata = cached_sampledata->next; - } + if(defsfont->sample) + { + delete_fluid_list(defsfont->sample); + } - FLUID_LOG(FLUID_ERR, "Trying to free sampledata not found in cache."); - goto error_exit; - - success_exit: - fluid_mutex_unlock(cached_sampledata_mutex); - return FLUID_OK; + if(defsfont->sampledata != NULL) + { + fluid_samplecache_unload(defsfont->sampledata); + } - error_exit: - fluid_mutex_unlock(cached_sampledata_mutex); - return FLUID_FAILED; -} + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + fluid_defpreset_preset_delete(preset); + } + delete_fluid_list(defsfont->preset); + for(list = defsfont->inst; list; list = fluid_list_next(list)) + { + delete_fluid_inst(fluid_list_get(list)); + } + delete_fluid_list(defsfont->inst); -/*************************************************************** - * - * SFONT - */ + FLUID_FREE(defsfont); + return FLUID_OK; +} /* - * new_fluid_defsfont + * fluid_defsfont_get_name */ -fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings) +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont) { - fluid_defsfont_t* sfont; - int i; + return defsfont->filename; +} - 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; - fluid_settings_getint(settings, "synth.lock-memory", &sfont->mlock); - - /* Initialise preset cache, so we don't have to call malloc on program changes. - Usually, we have at most one preset per channel plus one temporarily used, - so optimise for that case. */ - fluid_settings_getint(settings, "synth.midi-channels", &sfont->preset_stack_capacity); - sfont->preset_stack_capacity++; - sfont->preset_stack_size = 0; - sfont->preset_stack = FLUID_ARRAY(fluid_preset_t*, sfont->preset_stack_capacity); - if (!sfont->preset_stack) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE(sfont); - return NULL; - } +/* Load sample data for a single sample from the Soundfont file. + * Returns FLUID_OK on error, otherwise FLUID_FAILED + */ +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample) +{ + int num_samples; + unsigned int source_end = sample->source_end; - for (i = 0; i < sfont->preset_stack_capacity; i++) { - sfont->preset_stack[i] = FLUID_NEW(fluid_preset_t); - if (!sfont->preset_stack[i]) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - delete_fluid_defsfont(sfont); - return NULL; + /* For uncompressed samples we want to include the 46 zero sample word area following each sample + * in the Soundfont. Otherwise samples with loopend > end, which we have decided not to correct, would + * be corrected after all in fluid_sample_sanitize_loop */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + source_end += 46; /* Length of zero sample word after each sample, according to SF specs */ + + /* Safeguard against Soundfonts that are not quite valid and don't include 46 sample words after the + * last sample */ + if(source_end >= (defsfont->samplesize / sizeof(short))) + { + source_end = defsfont->samplesize / sizeof(short); + } } - sfont->preset_stack_size++; - } - return sfont; -} + num_samples = fluid_samplecache_load( + sfdata, sample->source_start, source_end, sample->sampletype, + defsfont->mlock, &sample->data, &sample->data24); -/* - * 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_cached_sampledata_unload(sfont->sampledata); - } - - while (sfont->preset_stack_size > 0) - FLUID_FREE(sfont->preset_stack[--sfont->preset_stack_size]); - FLUID_FREE(sfont->preset_stack); - - preset = sfont->preset; - while (preset != NULL) { - sfont->preset = preset->next; - delete_fluid_defpreset(preset); - preset = sfont->preset; - } - - FLUID_FREE(sfont); - return FLUID_OK; + if(num_samples < 0) + { + return FLUID_FAILED; + } + + if(num_samples == 0) + { + sample->start = sample->end = 0; + sample->loopstart = sample->loopend = 0; + return FLUID_OK; + } + + /* Ogg Vorbis samples already have loop pointers relative to the invididual decompressed sample, + * but SF2 samples are relative to sample chunk start, so they need to be adjusted */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + sample->loopstart = sample->source_loopstart - sample->source_start; + sample->loopend = sample->source_loopend - sample->source_start; + } + + /* As we've just loaded an individual sample into it's own buffer, we need to adjust the start + * and end pointers */ + sample->start = 0; + sample->end = num_samples - 1; + + return FLUID_OK; } -/* - * fluid_defsfont_get_name +/* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in + * one large block. For SF3 files, each compressed sample gets loaded individually. + * Returns FLUID_OK on success, otherwise FLUID_FAILED */ -char* fluid_defsfont_get_name(fluid_defsfont_t* sfont) +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata) { - return sfont->filename; -} + fluid_list_t *list; + fluid_sample_t *sample; + int sf3_file = (sfdata->version.major == 3); + + /* For SF2 files, we load the sample data in one large block */ + if(!sf3_file) + { + int read_samples; + int num_samples = sfdata->samplesize / sizeof(short); + + read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock, + &defsfont->sampledata, &defsfont->sample24data); + + if(read_samples != num_samples) + { + FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead", + num_samples, read_samples); + return FLUID_FAILED; + } + } + + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = fluid_list_get(list); + + if(sf3_file) + { + /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format + * anyway */ + if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); + return FLUID_FAILED; + } + + fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + } + else + { + /* Data pointers of SF2 samples point to large sample data block loaded above */ + sample->data = defsfont->sampledata; + sample->data24 = defsfont->sample24data; + fluid_sample_sanitize_loop(sample, defsfont->samplesize); + } + + fluid_voice_optimize_sample(sample); + } + return FLUID_OK; +} /* * fluid_defsfont_load */ -int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file) +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *fcbs, const char *file) { - SFData* sfdata; - fluid_list_t *p; - SFPreset* sfpreset; - SFSample* sfsample; - fluid_sample_t* sample; - fluid_defpreset_t* preset = NULL; - - 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); + SFData *sfdata; + fluid_list_t *p; + SFPreset *sfpreset; + SFSample *sfsample; + fluid_sample_t *sample; + fluid_defpreset_t *defpreset = NULL; - /* 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; - } + defsfont->filename = FLUID_STRDUP(file); + + if(defsfont->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + defsfont->fcbs = fcbs; + + /* The actual loading is done in the sfont and sffile files */ + sfdata = fluid_sffile_open(file, fcbs); + + if(sfdata == NULL) + { + FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file"); + return FLUID_FAILED; + } + + if(fluid_sffile_parse_presets(sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file"); + goto err_exit; + } + + /* Keep track of the position and size of the sample data because + it's loaded separately (and might be unoaded/reloaded in future) */ + defsfont->samplepos = sfdata->samplepos; + defsfont->samplesize = sfdata->samplesize; + defsfont->sample24pos = sfdata->sample24pos; + defsfont->sample24size = sfdata->sample24size; - /* 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; + /* Create all samples from sample headers */ + p = sfdata->sample; - /* load sample data in one block */ - if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK) - goto err_exit; + while(p != NULL) + { + sfsample = (SFSample *)fluid_list_get(p); + + sample = new_fluid_sample(); + + if(sample == NULL) + { + goto err_exit; + } + + if(fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK) + { + fluid_defsfont_add_sample(defsfont, sample); + } + else + { + delete_fluid_sample(sample); + sample = NULL; + } + + /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ + sfsample->fluid_sample = sample; - /* Create all the sample headers */ - p = sfdata->sample; - while (p != NULL) { - sfsample = (SFSample *) p->data; + p = fluid_list_next(p); + } + + /* If dynamic sample loading is disabled, load all samples in the Soundfont */ + if(!defsfont->dynamic_samples) + { + if(fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Unable to load all sample data"); + goto err_exit; + } + } - sample = new_fluid_sample(); - if (sample == NULL) goto err_exit; + /* Load all the presets */ + p = sfdata->preset; - if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK) - goto err_exit; + while(p != NULL) + { + sfpreset = (SFPreset *)fluid_list_get(p); + defpreset = new_fluid_defpreset(defsfont); - /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ - sfsample->fluid_sample = sample; + if(defpreset == NULL) + { + goto err_exit; + } - fluid_defsfont_add_sample(sfont, sample); - fluid_voice_optimize_sample(sample); - p = fluid_list_next(p); - } + if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont) != FLUID_OK) + { + goto err_exit; + } - /* Load all the presets */ - p = sfdata->preset; - while (p != NULL) { - sfpreset = (SFPreset *) p->data; - preset = new_fluid_defpreset(sfont); - if (preset == NULL) goto err_exit; + if(fluid_defsfont_add_preset(defsfont, defpreset) == FLUID_FAILED) + { + goto err_exit; + } - if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK) - goto err_exit; + p = fluid_list_next(p); + } - fluid_defsfont_add_preset(sfont, preset); - p = fluid_list_next(p); - } - sfont_close (sfdata); + fluid_sffile_close(sfdata); - return FLUID_OK; + return FLUID_OK; err_exit: - sfont_close (sfdata); - if (preset != NULL) - delete_fluid_defpreset(preset); - return FLUID_FAILED; + fluid_sffile_close(sfdata); + delete_fluid_defpreset(defpreset); + return FLUID_FAILED; } /* fluid_defsfont_add_sample * * Add a sample to the SoundFont */ -int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample) +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample) { - sfont->sample = fluid_list_append(sfont->sample, sample); - return FLUID_OK; + defsfont->sample = fluid_list_append(defsfont->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) +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset) { - 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_preset_t *preset; -/* - * fluid_defsfont_load_sampledata - */ -int -fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont) -{ - return fluid_cached_sampledata_load(sfont->filename, sfont->samplepos, - sfont->samplesize, &sfont->sampledata, sfont->mlock); + preset = new_fluid_preset(defsfont->sfont, + fluid_defpreset_preset_get_name, + fluid_defpreset_preset_get_banknum, + fluid_defpreset_preset_get_num, + fluid_defpreset_preset_noteon, + fluid_defpreset_preset_delete); + + if(defsfont->dynamic_samples) + { + preset->notify = dynamic_samples_preset_notify; + } + + if(preset == NULL) + { + return FLUID_FAILED; + } + + fluid_preset_set_data(preset, defpreset); + + defsfont->preset = fluid_list_append(defsfont->preset, preset); + + return FLUID_OK; } /* * fluid_defsfont_get_preset */ -fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num) +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, 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_preset_t *preset; + fluid_list_t *list; + + for(list = defsfont->preset; list != NULL; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + + if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == num)) + { + return preset; + } + } + + return NULL; } /* * fluid_defsfont_iteration_start */ -void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont) +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont) { - sfont->iter_cur = sfont->preset; + defsfont->preset_iter_cur = defsfont->preset; } /* * fluid_defsfont_iteration_next */ -int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset) +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont) { - if (sfont->iter_cur == NULL) { - return 0; - } + fluid_preset_t *preset = (fluid_preset_t *)fluid_list_get(defsfont->preset_iter_cur); + + defsfont->preset_iter_cur = fluid_list_next(defsfont->preset_iter_cur); - preset->data = (void*) sfont->iter_cur; - sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur); - return 1; + return preset; } /*************************************************************** @@ -720,75 +609,77 @@ int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* prese /* * new_fluid_defpreset */ -fluid_defpreset_t* -new_fluid_defpreset(fluid_defsfont_t* sfont) +fluid_defpreset_t * +new_fluid_defpreset(fluid_defsfont_t *defsfont) { - 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; + fluid_defpreset_t *defpreset = FLUID_NEW(fluid_defpreset_t); + + if(defpreset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + defpreset->next = NULL; + defpreset->defsfont = defsfont; + defpreset->name[0] = 0; + defpreset->bank = 0; + defpreset->num = 0; + defpreset->global_zone = NULL; + defpreset->zone = NULL; + return defpreset; } /* * delete_fluid_defpreset */ -int -delete_fluid_defpreset(fluid_defpreset_t* preset) +void +delete_fluid_defpreset(fluid_defpreset_t *defpreset) { - 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; + fluid_preset_zone_t *zone; + + fluid_return_if_fail(defpreset != NULL); + + delete_fluid_preset_zone(defpreset->global_zone); + defpreset->global_zone = NULL; + + zone = defpreset->zone; + + while(zone != NULL) + { + defpreset->zone = zone->next; + delete_fluid_preset_zone(zone); + zone = defpreset->zone; + } + + FLUID_FREE(defpreset); } int -fluid_defpreset_get_banknum(fluid_defpreset_t* preset) +fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset) { - return preset->bank; + return defpreset->bank; } int -fluid_defpreset_get_num(fluid_defpreset_t* preset) +fluid_defpreset_get_num(fluid_defpreset_t *defpreset) { - return preset->num; + return defpreset->num; } -char* -fluid_defpreset_get_name(fluid_defpreset_t* preset) +const char * +fluid_defpreset_get_name(fluid_defpreset_t *defpreset) { - return preset->name; + return defpreset->name; } /* * fluid_defpreset_next */ -fluid_defpreset_t* -fluid_defpreset_next(fluid_defpreset_t* preset) +fluid_defpreset_t * +fluid_defpreset_next(fluid_defpreset_t *defpreset) { - return preset->next; + return defpreset->next; } @@ -796,2637 +687,1505 @@ fluid_defpreset_next(fluid_defpreset_t* preset) * fluid_defpreset_noteon */ int -fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) +fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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; - 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; + fluid_preset_zone_t *preset_zone, *global_preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone; + fluid_voice_zone_t *voice_zone; + fluid_list_t *list; + 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); + global_preset_zone = fluid_defpreset_get_global_zone(defpreset); - /* run thru all the zones of this preset */ - preset_zone = fluid_defpreset_get_zone(preset); - while (preset_zone != NULL) { + /* run thru all the zones of this preset */ + preset_zone = fluid_defpreset_get_zone(defpreset); - /* check if the note falls into the key and velocity range of this - preset */ - if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { + while(preset_zone != NULL) + { + + /* check if the note falls into the key and velocity range of this + preset */ + if(fluid_zone_inside_range(&preset_zone->range, 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 that could start a voice */ + for(list = preset_zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + voice_zone = fluid_list_get(list); - inst = fluid_preset_zone_get_inst(preset_zone); - global_inst_zone = fluid_inst_get_global_zone(inst); + /* check if the instrument zone is ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ + if(fluid_zone_inside_range(&voice_zone->range, key, vel)) + { + + inst_zone = voice_zone->inst_zone; - /* run thru all the zones of this instrument */ - inst_zone = fluid_inst_get_zone(inst); - while (inst_zone != NULL) { + /* this is a good zone. allocate a new synthesis process and initialize it */ + voice = fluid_synth_alloc_voice_LOCAL(synth, inst_zone->sample, chan, key, vel, &voice_zone->range); + + if(voice == NULL) + { + return FLUID_FAILED; + } + + + /* 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(mod_list[i] && 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. + However this check is not necessary when the soundfont + loader has ignored invalid preset generators. + Actually load_pgen()has ignored these invalid preset + generators: + GEN_STARTADDROFS, GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS,GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS,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. + */ + } + } /* 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(mod_list[i] && 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) + */ + } + + } + } - /* make sure this instrument zone has a valid sample */ - sample = fluid_inst_zone_get_sample(inst_zone); - if ((sample == NULL) || fluid_sample_in_rom(sample)) { - inst_zone = fluid_inst_zone_next(inst_zone); - continue; - } + preset_zone = fluid_preset_zone_next(preset_zone); + } - /* 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; - } - - - /* 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 (mod_list[i] && 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 (mod_list[i] && 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; + return FLUID_OK; } /* * fluid_defpreset_set_global_zone */ int -fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) +fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone) { - preset->global_zone = zone; - return FLUID_OK; + defpreset->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_defpreset_import_sfont(fluid_defpreset_t *defpreset, + SFPreset *sfpreset, + fluid_defsfont_t *defsfont) { - 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) { - delete_fluid_preset_zone(zone); - 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_list_t *p; + SFZone *sfzone; + fluid_preset_zone_t *zone; + int count; + char zone_name[256]; + + if(FLUID_STRLEN(sfpreset->name) > 0) + { + FLUID_STRCPY(defpreset->name, sfpreset->name); + } + else + { + FLUID_SNPRINTF(defpreset->name, sizeof(defpreset->name), "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum); + } + + defpreset->bank = sfpreset->bank; + defpreset->num = sfpreset->prenum; + p = sfpreset->zone; + count = 0; + + while(p != NULL) + { + sfzone = (SFZone *)fluid_list_get(p); + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", defpreset->name, count); + zone = new_fluid_preset_zone(zone_name); + + if(zone == NULL) + { + return FLUID_FAILED; + } + + if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont) != FLUID_OK) + { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + if((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) + { + fluid_defpreset_set_global_zone(defpreset, zone); + } + else if(fluid_defpreset_add_zone(defpreset, 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) +fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, 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; + if(defpreset->zone == NULL) + { + zone->next = NULL; + defpreset->zone = zone; + } + else + { + zone->next = defpreset->zone; + defpreset->zone = zone; + } + + return FLUID_OK; } /* * fluid_defpreset_get_zone */ -fluid_preset_zone_t* -fluid_defpreset_get_zone(fluid_defpreset_t* preset) +fluid_preset_zone_t * +fluid_defpreset_get_zone(fluid_defpreset_t *defpreset) { - return preset->zone; + return defpreset->zone; } /* * fluid_defpreset_get_global_zone */ -fluid_preset_zone_t* -fluid_defpreset_get_global_zone(fluid_defpreset_t* preset) +fluid_preset_zone_t * +fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset) { - return preset->global_zone; + return defpreset->global_zone; } +/*************************************************************** + * + * PRESET_ZONE + */ + /* * fluid_preset_zone_next */ -fluid_preset_zone_t* -fluid_preset_zone_next(fluid_preset_zone_t* preset) +fluid_preset_zone_t * +fluid_preset_zone_next(fluid_preset_zone_t *zone) { - return preset->next; + return zone->next; } /* * new_fluid_preset_zone */ -fluid_preset_zone_t* +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; -} + fluid_preset_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_preset_zone_t); -/*************************************************************** - * - * PRESET_ZONE - */ + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } -/* - * delete_fluid_preset_zone - */ -int -delete_fluid_preset_zone(fluid_preset_zone_t* zone) -{ - fluid_mod_t *mod, *tmp; + zone->next = NULL; + zone->voice_zone = NULL; + zone->name = FLUID_STRDUP(name); - mod = zone->mod; - while (mod) /* delete the modulators */ + if(zone->name == NULL) { - tmp = mod; - mod = mod->next; - fluid_mod_delete (tmp); + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; } - if (zone->name) FLUID_FREE (zone->name); - if (zone->inst) delete_fluid_inst (zone->inst); - FLUID_FREE(zone); - return FLUID_OK; -} + zone->inst = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; -/* - * 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; + /* 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; } /* - * fluid_preset_zone_get_inst + * delete_fluid_preset_zone */ -fluid_inst_t* -fluid_preset_zone_get_inst(fluid_preset_zone_t* zone) +void +delete_fluid_preset_zone(fluid_preset_zone_t *zone) { - return zone->inst; -} + fluid_mod_t *mod, *tmp; + fluid_list_t *list; -/* - * 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)); -} + fluid_return_if_fail(zone != NULL); -/*************************************************************** - * - * INST - */ + mod = zone->mod; -/* - * 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; + while(mod) /* delete the modulators */ + { + tmp = mod; + mod = mod->next; + delete_fluid_mod(tmp); } - 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; + + for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + FLUID_FREE(fluid_list_get(list)); } - 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; + delete_fluid_list(zone->voice_zone); + + FLUID_FREE(zone->name); + FLUID_FREE(zone); } -/* - * fluid_inst_import_sfont - */ -int -fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone) { - fluid_list_t *p; - SFZone* sfzone; - fluid_inst_zone_t* zone; - char zone_name[256]; - int count; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + fluid_voice_zone_t *voice_zone; + fluid_zone_range_t *irange; + fluid_zone_range_t *prange = &preset_zone->range; - p = sfinst->zone; - if ((sfinst->name != NULL) && (FLUID_STRLEN(sfinst->name) > 0)) { - FLUID_STRCPY(inst->name, sfinst->name); - } else { - FLUID_STRCPY(inst->name, ""); - } + fluid_return_val_if_fail(preset_zone->inst != NULL, FLUID_FAILED); - count = 0; - while (p != NULL) { + inst_zone = fluid_inst_get_zone(preset_zone->inst); - sfzone = (SFZone *) p->data; - FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count); + while(inst_zone != NULL) + { - zone = new_fluid_inst_zone(zone_name); - if (zone == NULL) { - return FLUID_FAILED; - } + /* We only create voice ranges for zones that could actually start a voice, + * i.e. that have a sample and don't point to ROM */ + sample = fluid_inst_zone_get_sample(inst_zone); - if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { - delete_fluid_inst_zone(zone); - return FLUID_FAILED; - } + if((sample == NULL) || fluid_sample_in_rom(sample)) + { + inst_zone = fluid_inst_zone_next(inst_zone); + continue; + } - if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) { - fluid_inst_set_global_zone(inst, zone); + voice_zone = FLUID_NEW(fluid_voice_zone_t); - } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) { - return FLUID_FAILED; + if(voice_zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + voice_zone->inst_zone = inst_zone; + + irange = &inst_zone->range; + + voice_zone->range.keylo = (prange->keylo > irange->keylo) ? prange->keylo : irange->keylo; + voice_zone->range.keyhi = (prange->keyhi < irange->keyhi) ? prange->keyhi : irange->keyhi; + voice_zone->range.vello = (prange->vello > irange->vello) ? prange->vello : irange->vello; + voice_zone->range.velhi = (prange->velhi < irange->velhi) ? prange->velhi : irange->velhi; + voice_zone->range.ignore = FALSE; + + preset_zone->voice_zone = fluid_list_append(preset_zone->voice_zone, voice_zone); + + inst_zone = fluid_inst_zone_next(inst_zone); } - p = fluid_list_next(p); - count++; - } - return FLUID_OK; + return FLUID_OK; } /* - * fluid_inst_add_zone + * fluid_preset_zone_import_sfont */ int -fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) +fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont) { - if (inst->zone == NULL) { - zone->next = NULL; - inst->zone = zone; - } else { - zone->next = inst->zone; - inst->zone = zone; - } - return FLUID_OK; -} + fluid_list_t *r; + SFGen *sfgen; + SFInst *sfinst; + int count; -/* - * fluid_inst_get_zone - */ -fluid_inst_zone_t* -fluid_inst_get_zone(fluid_inst_t* inst) -{ - return inst->zone; + for(count = 0, r = sfzone->gen; r != NULL; count++) + { + sfgen = (SFGen *)fluid_list_get(r); + + switch(sfgen->id) + { + case GEN_KEYRANGE: + zone->range.keylo = sfgen->amount.range.lo; + zone->range.keyhi = sfgen->amount.range.hi; + break; + + case GEN_VELRANGE: + zone->range.vello = sfgen->amount.range.lo; + zone->range.velhi = sfgen->amount.range.hi; + break; + + case GEN_ATTENUATION: + /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at + * preset and instrument level */ + zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; + zone->gen[sfgen->id].flags = GEN_SET; + 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)) + { + sfinst = sfzone->instsamp->data; + + zone->inst = find_inst_by_idx(defsfont, sfinst->idx); + + if(zone->inst == NULL) + { + zone->inst = fluid_inst_import_sfont(zone, sfinst, defsfont); + } + + if(zone->inst == NULL) + { + return FLUID_FAILED; + } + + if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED) + { + return FLUID_FAILED; + } + } + + /* Import the modulators (only SF2.1 and higher) */ + for(count = 0, r = sfzone->mod; r != NULL; count++) + { + + SFMod *mod_src = (SFMod *)fluid_list_get(r); + fluid_mod_t *mod_dest = new_fluid_mod(); + 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_inst_get_global_zone + * fluid_preset_zone_get_inst */ -fluid_inst_zone_t* -fluid_inst_get_global_zone(fluid_inst_t* inst) +fluid_inst_t * +fluid_preset_zone_get_inst(fluid_preset_zone_t *zone) { - return inst->global_zone; + return zone->inst; } + /*************************************************************** * - * INST_ZONE + * INST */ /* - * new_fluid_inst_zone + * new_fluid_inst */ -fluid_inst_zone_t* -new_fluid_inst_zone(char* name) +fluid_inst_t * +new_fluid_inst() { - 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; + 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_zone + * delete_fluid_inst */ -int -delete_fluid_inst_zone(fluid_inst_zone_t* zone) +void +delete_fluid_inst(fluid_inst_t *inst) { - fluid_mod_t *mod, *tmp; + fluid_inst_zone_t *zone; - mod = zone->mod; - while (mod) /* delete the modulators */ + fluid_return_if_fail(inst != NULL); + + delete_fluid_inst_zone(inst->global_zone); + inst->global_zone = NULL; + + zone = inst->zone; + + while(zone != NULL) { - tmp = mod; - mod = mod->next; - fluid_mod_delete (tmp); + inst->zone = zone->next; + delete_fluid_inst_zone(zone); + zone = inst->zone; } - if (zone->name) FLUID_FREE (zone->name); - FLUID_FREE(zone); - return FLUID_OK; + FLUID_FREE(inst); } /* - * fluid_inst_zone_next + * fluid_inst_set_global_zone */ -fluid_inst_zone_t* -fluid_inst_zone_next(fluid_inst_zone_t* zone) +int +fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) { - return zone->next; + inst->global_zone = zone; + return FLUID_OK; } /* - * fluid_inst_zone_import_sfont + * fluid_inst_import_sfont + */ +fluid_inst_t * +fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont) +{ + fluid_list_t *p; + fluid_inst_t *inst; + SFZone *sfzone; + fluid_inst_zone_t *inst_zone; + char zone_name[256]; + int count; + + inst = (fluid_inst_t *) new_fluid_inst(); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + inst->source_idx = sfinst->idx; + + p = sfinst->zone; + + if(FLUID_STRLEN(sfinst->name) > 0) + { + FLUID_STRCPY(inst->name, sfinst->name); + } + else + { + FLUID_STRCPY(inst->name, ""); + } + + count = 0; + + while(p != NULL) + { + + sfzone = (SFZone *)fluid_list_get(p); + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", inst->name, count); + + inst_zone = new_fluid_inst_zone(zone_name); + + if(inst_zone == NULL) + { + return NULL; + } + + if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont) != FLUID_OK) + { + delete_fluid_inst_zone(inst_zone); + return NULL; + } + + if((count == 0) && (fluid_inst_zone_get_sample(inst_zone) == NULL)) + { + fluid_inst_set_global_zone(inst, inst_zone); + + } + else if(fluid_inst_add_zone(inst, inst_zone) != FLUID_OK) + { + return NULL; + } + + p = fluid_list_next(p); + count++; + } + + defsfont->inst = fluid_list_append(defsfont->inst, inst); + return inst; +} + +/* + * fluid_inst_add_zone */ int -fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) +fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) { - 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); */ -/* } */ - - /* fixup sample pointer */ - if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) - zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample; - - /* 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; + if(inst->zone == NULL) + { + zone->next = NULL; + inst->zone = zone; + } + else + { + zone->next = inst->zone; + inst->zone = zone; + } + + return FLUID_OK; } /* - * fluid_inst_zone_get_sample + * fluid_inst_get_zone */ -fluid_sample_t* -fluid_inst_zone_get_sample(fluid_inst_zone_t* zone) +fluid_inst_zone_t * +fluid_inst_get_zone(fluid_inst_t *inst) { - return zone->sample; + return inst->zone; } /* - * fluid_inst_zone_inside_range + * fluid_inst_get_global_zone */ -int -fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) +fluid_inst_zone_t * +fluid_inst_get_global_zone(fluid_inst_t *inst) { - return ((zone->keylo <= key) && - (zone->keyhi >= key) && - (zone->vello <= vel) && - (zone->velhi >= vel)); + return inst->global_zone; } /*************************************************************** * - * SAMPLE + * INST_ZONE */ /* - * new_fluid_sample + * new_fluid_inst_zone */ -fluid_sample_t* -new_fluid_sample() +fluid_inst_zone_t * +new_fluid_inst_zone(char *name) { - fluid_sample_t* sample = NULL; + fluid_inst_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_inst_zone_t); - sample = FLUID_NEW(fluid_sample_t); - if (sample == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } - memset(sample, 0, sizeof(fluid_sample_t)); - sample->valid = 1; + zone->next = NULL; + zone->name = FLUID_STRDUP(name); - return sample; + if(zone->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + + zone->sample = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; + /* 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_sample + * delete_fluid_inst_zone */ -int -delete_fluid_sample(fluid_sample_t* sample) +void +delete_fluid_inst_zone(fluid_inst_zone_t *zone) { - FLUID_FREE(sample); - return FLUID_OK; + fluid_mod_t *mod, *tmp; + + fluid_return_if_fail(zone != NULL); + + mod = zone->mod; + + while(mod) /* delete the modulators */ + { + tmp = mod; + mod = mod->next; + delete_fluid_mod(tmp); + } + + FLUID_FREE(zone->name); + FLUID_FREE(zone); } /* - * fluid_sample_in_rom + * fluid_inst_zone_next */ -int -fluid_sample_in_rom(fluid_sample_t* sample) +fluid_inst_zone_t * +fluid_inst_zone_next(fluid_inst_zone_t *zone) { - return (sample->sampletype & FLUID_SAMPLETYPE_ROM); + return zone->next; } /* - * fluid_sample_import_sfont + * fluid_inst_zone_import_sfont */ int -fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont) +fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont) { - 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; -} + fluid_list_t *r; + SFGen *sfgen; + int count; + for(count = 0, r = sfzone->gen; r != NULL; count++) + { + sfgen = (SFGen *)fluid_list_get(r); + + switch(sfgen->id) + { + case GEN_KEYRANGE: + inst_zone->range.keylo = sfgen->amount.range.lo; + inst_zone->range.keyhi = sfgen->amount.range.hi; + break; + + case GEN_VELRANGE: + inst_zone->range.vello = sfgen->amount.range.lo; + inst_zone->range.velhi = sfgen->amount.range.hi; + break; + + case GEN_ATTENUATION: + /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at + * preset and instrument level */ + inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; + inst_zone->gen[sfgen->id].flags = GEN_SET; + break; + + default: + /* FIXME: some generators have an unsigned word amount value but + i don't know which ones */ + inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + inst_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); */ + /* } */ + /* fixup sample pointer */ + if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) + { + inst_zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample; + } + /* Import the modulators (only SF2.1 and higher) */ + for(count = 0, r = sfzone->mod; r != NULL; count++) + { + SFMod *mod_src = (SFMod *)fluid_list_get(r); + int type; + fluid_mod_t *mod_dest; -/*=================================sfload.c======================== - Borrowed from Smurf SoundFont Editor by Josh Green - =================================================================*/ + mod_dest = new_fluid_mod(); -/* - 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 -*/ - -#if FLUID_IS_BIG_ENDIAN - -#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 - -#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 - -#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 - -#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 - -#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 - -#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 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 - -#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 (unsigned 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; + if(mod_dest == NULL) + { + return FLUID_FAILED; + } - p = (unsigned int *) & idlist; - for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1) - if (*p == id) - return (i + 1); + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ - return (UNKN_ID); -} + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; -SFData * -sfload_file (const char * fname) -{ - SFData *sf = NULL; - FILE *fd; - int fsize = 0; - int err = FALSE; + /* *** 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; - if (!(fd = fopen (fname, "rb"))) - { - FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname); - return (NULL); - } - - if (!(sf = FLUID_NEW (SFData))) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - fclose(fd); - err = TRUE; - } + /* 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; + } - if (!err) - { - memset (sf, 0, sizeof (SFData)); /* zero sfdata */ - sf->fname = FLUID_STRDUP (fname); /* copy file name */ - sf->sffd = fd; - } + /* 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; + } - /* 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); + /* 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; + } - if (!err && !load_body (fsize, sf, fd)) - err = TRUE; /* load the sfont */ + /* 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 (err) - { - if (sf) - sfont_close (sf); - return (NULL); - } + 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; + } - return (sf); -} + /* *** Dest *** */ + mod_dest->dest = mod_src->dest; /* index of controlled generator */ -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 SoundFont file")); - return (FAIL); - } - - if (chunk.size != size - 8) { - gerr (ErrCorr, _("SoundFont 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); -} + /* *** 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; -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); -} + /* 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; + } -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 = FLUID_MALLOC (chunk.size + 1))) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - 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); -} + /* 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; + } -static int -process_sdta (unsigned int size, SFData * sf, FILE * fd) -{ - SFChunk chunk; + /* 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; + } - if (size == 0) - return (OK); /* no sample data? */ + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = (mod_src->amtsrc) >> 10; + type &= 63; /* type is a 6-bit value */ - /* read sub chunk */ - READCHUNK (&chunk, fd); - size -= 8; + 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; + } - if (chunkid (chunk.id) != SMPL_ID) - return (gerr (ErrCorr, - _("Expected SMPL chunk found invalid id instead"))); + /* *** 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; + } - /* SDTA chunk may also contain sm24 chunk for 24 bit samples - * (not yet supported), only an error if SMPL chunk size is - * greater than SDTA. */ - if (chunk.size > size) - return (gerr (ErrCorr, _("SDTA chunk size mismatch"))); + /* 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) + { + inst_zone->mod = mod_dest; + } + else + { + fluid_mod_t *last_mod = inst_zone->mod; - /* sample data follows */ - sf->samplepos = ftell (fd); + /* Find the end of the list */ + while(last_mod->next != NULL) + { + last_mod = last_mod->next; + } - /* used in fixup_sample() to check validity of sample headers */ - sdtachunk_size = chunk.size; - sf->samplesize = chunk.size; + last_mod->next = mod_dest; + } - FSKIP (size, fd); + r = fluid_list_next(r); + } /* foreach modulator */ - return (OK); + return FLUID_OK; } -static int -pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, - int * size, FILE * fd) +/* + * fluid_inst_zone_get_sample + */ +fluid_sample_t * +fluid_inst_zone_get_sample(fluid_inst_zone_t *zone) { - 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); + return zone->sample; } -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 +fluid_zone_inside_range(fluid_zone_range_t *range, int key, int vel) { - 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); -} + /* ignoreInstrumentZone is set in mono legato playing */ + int ignore_zone = range->ignore; -/* 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); -} + /* Reset the 'ignore' request */ + range->ignore = FALSE; -/* 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); + return !ignore_zone && ((range->keylo <= key) && + (range->keyhi >= key) && + (range->vello <= vel) && + (range->velhi >= vel)); } -/* ------------------------------------------------------------------- - * 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); -} + * SAMPLE + */ -/* instrument bag loader */ -static int -load_ibag (int size, SFData * sf, FILE * fd) +/* + * fluid_sample_in_rom + */ +int +fluid_sample_in_rom(fluid_sample_t *sample) { - 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); + return (sample->sampletype & FLUID_SAMPLETYPE_ROM); } -/* 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_sample_import_sfont + */ +int +fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont) { - 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); -} + FLUID_STRCPY(sample->name, sfsample->name); -/* sample header loader */ -static int -load_shdr (unsigned int size, SFData * sf, FILE * fd) -{ - unsigned int i; - SFSample *p; + sample->source_start = sfsample->start; + sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */ + sample->source_loopstart = sfsample->loopstart; + sample->source_loopend = sfsample->loopend; - if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */ - return (gerr (ErrCorr, _("Sample header has invalid size"))); + sample->start = sample->source_start; + sample->end = sample->source_end; + sample->loopstart = sample->source_loopstart; + sample->loopend = sample->source_loopend; + sample->samplerate = sfsample->samplerate; + sample->origpitch = sfsample->origpitch; + sample->pitchadj = sfsample->pitchadj; + sample->sampletype = sfsample->sampletype; - 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); + if(defsfont->dynamic_samples) + { + sample->notify = dynamic_samples_sample_notify; } - /* load all sample headers */ - for (i = 0; i < size; i++) + if(fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED) { - 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; + return FLUID_FAILED; } - FSKIP (SFSHDRSIZE, fd); /* skip terminal shdr */ - - return (OK); + return FLUID_OK; } -/* "fixup" (inst # -> inst ptr) instrument references in preset list */ -static int -fixup_pgen (SFData * sf) +/* Called if a sample is no longer used by a voice. Used by dynamic sample loading + * to unload a sample that is not used by any loaded presets anymore but couldn't + * be unloaded straight away because it was still in use by a voice. */ +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason) { - 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); -} + if(reason == FLUID_SAMPLE_DONE && sample->preset_count == 0) + { + unload_sample(sample); + } -/* "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); + return FLUID_OK; } -/* convert sample end, loopstart and loopend to offsets and check if valid */ -static int -fixup_sample (SFData * sf) +/* Called if a preset has been selected for or unselected from a channel. Used by + * dynamic sample loading to load and unload samples on demand. */ +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan) { - 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 - ================================================================*/ - + fluid_defsfont_t *defsfont; -/* 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 + if(reason == FLUID_PRESET_SELECTED) + { + FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + load_preset_samples(defsfont, preset); + } + else if(reason == FLUID_PRESET_UNSELECTED) + { + FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + unload_preset_samples(defsfont, preset); + } -unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4, - Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0 -}; + return FLUID_OK; +} -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 -}; -/* close SoundFont file and delete a SoundFont structure */ -void -sfont_close (SFData * sf) +/* Walk through all samples used by the passed in preset and make sure that the + * sample data is loaded for each sample. Used by dynamic sample loading. */ +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { - fluid_list_t *p, *p2; - - if (sf->sffd) - fclose (sf->sffd); - - 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->inst); - sf->inst = NULL; - - p = sf->sample; - while (p) - { - FLUID_FREE (p->data); - p = fluid_list_next (p); - } - delete_fluid_list (sf->sample); - sf->sample = NULL; - - FLUID_FREE (sf); -} + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + SFData *sffile = NULL; -/* free all elements of a zone (Preset or Instrument) */ -void -sfont_free_zone (SFZone * zone) -{ - fluid_list_t *p; + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); - if (!zone) - return; + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->start != sample->end)) + { + sample->preset_count++; + + /* If this is the first time this sample has been selected, + * load the sampledata */ + if(sample->preset_count == 1) + { + /* Make sure we have an open Soundfont file. Do this here + * to avoid having to open the file if no loading is necessary + * for a preset */ + if(sffile == NULL) + { + sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs); + + if(sffile == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file"); + return FLUID_FAILED; + } + } + + if(fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK) + { + fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + fluid_voice_optimize_sample(sample); + } + else + { + FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name); + sample->start = sample->end = 0; + } + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } - p = zone->gen; - while (p) - { /* Free gen chunks for this zone */ - if (p->data) - FLUID_FREE (p->data); - p = fluid_list_next (p); + preset_zone = fluid_preset_zone_next(preset_zone); } - 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); + if(sffile != NULL) + { + fluid_sffile_close(sffile); } - delete_fluid_list (zone->mod); /* free modlist */ - FLUID_FREE (zone); /* free zone chunk */ + return FLUID_OK; } -/* preset sort function, first by bank, then by preset # */ -int -sfont_preset_compare_func (void* a, void* b) +/* Walk through all samples used by the passed in preset and unload the sample data + * of each sample that is not used by any selected preset anymore. Used by dynamic + * sample loading. */ +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) { - int aval, bval; + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); - aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum; - bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum; + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->preset_count > 0)) + { + sample->preset_count--; + + /* If the sample is not used by any preset or used by a + * sounding voice, unload it from the sample cache. If it's + * still in use by a voice, dynamic_samples_sample_notify will + * take care of unloading the sample as soon as the voice is + * finished with it (but only on the next API call). */ + if(sample->preset_count == 0 && sample->refcount == 0) + { + unload_sample(sample); + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } - return (aval - bval); + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; } -/* delete zone from zone list */ -void -sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone) +/* Unload an unused sample from the samplecache */ +static void unload_sample(fluid_sample_t *sample) { - *zlist = fluid_list_remove (*zlist, (void*) zone); - sfont_free_zone (zone); -} + fluid_return_if_fail(sample != NULL); + fluid_return_if_fail(sample->data != NULL); + fluid_return_if_fail(sample->preset_count == 0); + fluid_return_if_fail(sample->refcount == 0); -/* 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; + FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name); - p = genlist; - while (p) + if(fluid_samplecache_unload(sample->data) == FLUID_FAILED) { - if (p->data == NULL) - return (NULL); - if (gen == ((SFGen *) p->data)->id) - break; - p = fluid_list_next (p); + FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name); + } + else + { + sample->data = NULL; + sample->data24 = NULL; } - 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, ...) +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx) { - va_list args; - - va_start (args, fmt); - vprintf(fmt, args); - va_end (args); - - printf("\n"); + fluid_list_t *list; + fluid_inst_t *inst; - return (FAIL); -} + for(list = defsfont->inst; list != NULL; list = fluid_list_next(list)) + { + inst = fluid_list_get(list); -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); -} + if(inst->source_idx == idx) + { + return inst; + } + } -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); + return NULL; } diff --git a/libs/fluidsynth/src/fluid_defsfont.h b/libs/fluidsynth/src/fluid_defsfont.h index 29f3fd9e86..8b24934d81 100644 --- a/libs/fluidsynth/src/fluid_defsfont.h +++ b/libs/fluidsynth/src/fluid_defsfont.h @@ -5,16 +5,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -27,16 +27,13 @@ #include "fluidsynth.h" #include "fluidsynth_priv.h" +#include "fluid_sffile.h" #include "fluid_list.h" +#include "fluid_mod.h" +#include "fluid_gen.h" -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ -/********************************************************************************/ - /*-----------------------------------sfont.h----------------------------*/ #define SF_SAMPMODES_LOOP 1 @@ -47,318 +44,6 @@ #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 */ - fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */ -} -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_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 02110-1301, USA. - */ - -#include - - -/*-----------------------------------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, ...); -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 @@ -367,7 +52,26 @@ 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; +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /**< Soundfont Instrument Zone */ +typedef struct _fluid_voice_zone_t fluid_voice_zone_t; + +/* defines the velocity and key range for a zone */ +struct _fluid_zone_range_t +{ + int keylo; + int keyhi; + int vello; + int velhi; + unsigned char ignore; /* set to TRUE for legato playing to ignore this range zone */ +}; + +/* Stored on a preset zone to keep track of the inst zones that could start a voice + * and their combined preset zone/instument zone ranges */ +struct _fluid_voice_zone_t +{ + fluid_inst_zone_t *inst_zone; + fluid_zone_range_t range; +}; /* @@ -375,57 +79,62 @@ typedef struct _fluid_inst_zone_t fluid_inst_zone_t; */ -fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings); -int delete_fluid_defsfloader(fluid_sfloader_t* loader); -fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename); +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_defsfont_sfont_delete(fluid_sfont_t *sfont); +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont); -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); +void fluid_defpreset_preset_delete(fluid_preset_t *preset); +const 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); +int fluid_zone_inside_range(fluid_zone_range_t *zone_range, 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 */ - int mlock; /* Should we try memlock (avoid swapping)? */ - - fluid_preset_t iter_preset; /* preset interface used in the iteration */ - fluid_defpreset_t* iter_cur; /* the current preset in the iteration */ - - fluid_preset_t** preset_stack; /* List of presets that are available to use */ - int preset_stack_capacity; /* Length of preset_stack array */ - int preset_stack_size; /* Current number of items in the stack */ + const fluid_file_callbacks_t *fcbs; /* the file callbacks used to load this Soundfont */ + 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 in bytes */ + short *sampledata; /* the sample data, loaded in ram */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + char *sample24data; /* if not NULL, the least significant byte of the 24bit sample data, loaded in ram */ + + fluid_sfont_t *sfont; /* pointer to parent sfont */ + fluid_list_t *sample; /* the samples in this soundfont */ + fluid_list_t *preset; /* the presets of this soundfont */ + fluid_list_t *inst; /* the instruments of this soundfont */ + int mlock; /* Should we try memlock (avoid swapping)? */ + int dynamic_samples; /* Enables dynamic sample loading if set */ + + fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ }; -fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings); -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_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings); +int delete_fluid_defsfont(fluid_defsfont_t *defsfont); +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *file_callbacks, const char *file); +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int prenum); +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont); +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample); +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata); + +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample); +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset); /* @@ -433,98 +142,90 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset */ 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 *next; + fluid_defsfont_t *defsfont; /* 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_defpreset_t *new_fluid_defpreset(fluid_defsfont_t *defsfont); +void delete_fluid_defpreset(fluid_defpreset_t *defpreset); +fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset); +int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont); +int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset); +fluid_preset_zone_t *fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_num(fluid_defpreset_t *defpreset); +const char *fluid_defpreset_get_name(fluid_defpreset_t *defpreset); +int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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 *next; + char *name; + fluid_inst_t *inst; + fluid_list_t *voice_zone; + fluid_zone_range_t range; + 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_preset_zone_t *new_fluid_preset_zone(char *name); +void delete_fluid_preset_zone(fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone); +int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont); +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; + char name[21]; + int source_idx; /* Index of instrument in source Soundfont */ + 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_t *new_fluid_inst(void); +fluid_inst_t *fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont); +void delete_fluid_inst(fluid_inst_t *inst); +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 *next; + char *name; + fluid_sample_t *sample; + fluid_zone_range_t range; + 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_inst_zone_t *new_fluid_inst_zone(char *name); +void 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 *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont); +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); +int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont); +int fluid_sample_in_rom(fluid_sample_t *sample); #endif /* _FLUID_SFONT_H */ diff --git a/libs/fluidsynth/src/fluid_event.c b/libs/fluidsynth/src/fluid_event.c index b3b0608345..d7962eac98 100644 --- a/libs/fluidsynth/src/fluid_event.c +++ b/libs/fluidsynth/src/fluid_event.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -28,7 +28,7 @@ */ -#include "fluid_event_priv.h" +#include "fluid_event.h" #include "fluidsynth_priv.h" /*************************************************************** @@ -39,33 +39,36 @@ /* Event alloc/free */ void -fluid_event_clear(fluid_event_t* evt) +fluid_event_clear(fluid_event_t *evt) { - FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); + FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); - // by default, no type - evt->dest = -1; - evt->src = -1; - evt->type = -1; + // by default, no type + evt->dest = -1; + evt->src = -1; + evt->type = -1; } /** * Create a new sequencer event structure. * @return New sequencer event structure or NULL if out of memory */ -fluid_event_t* +fluid_event_t * new_fluid_event() { - fluid_event_t* evt; + 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_event_clear(evt); + evt = FLUID_NEW(fluid_event_t); - return(evt); + if(evt == NULL) + { + FLUID_LOG(FLUID_PANIC, "event: Out of memory\n"); + return NULL; + } + + fluid_event_clear(evt); + + return(evt); } /** @@ -73,14 +76,11 @@ new_fluid_event() * @param evt Sequencer event structure created by new_fluid_event(). */ void -delete_fluid_event(fluid_event_t* evt) +delete_fluid_event(fluid_event_t *evt) { + fluid_return_if_fail(evt != NULL); - if (evt == NULL) { - return; - } - - FLUID_FREE(evt); + FLUID_FREE(evt); } /** @@ -90,43 +90,43 @@ delete_fluid_event(fluid_event_t* evt) * @param time Time value to assign */ void -fluid_event_set_time(fluid_event_t* evt, unsigned int time) +fluid_event_set_time(fluid_event_t *evt, unsigned int time) { - evt->time = time; + evt->time = time; } /** - * Set source of a sequencer event (DOCME). + * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set. * @param evt Sequencer event structure - * @param src DOCME + * @param src Unique sequencer ID */ void -fluid_event_set_source(fluid_event_t* evt, short src) +fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src) { - evt->src = src; + evt->src = src; } /** - * Set destination of a sequencer event (DOCME). + * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID. * @param evt Sequencer event structure - * @param dest DOCME + * @param dest The destination unique sequencer ID */ void -fluid_event_set_dest(fluid_event_t* evt, short dest) +fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest) { - evt->dest = dest; + evt->dest = dest; } /** * Set a sequencer event to be a timer event. * @param evt Sequencer event structure - * @param data DOCME + * @param data User supplied data pointer */ void -fluid_event_timer(fluid_event_t* evt, void* data) +fluid_event_timer(fluid_event_t *evt, void *data) { - evt->type = FLUID_SEQ_TIMER; - evt->data = data; + evt->type = FLUID_SEQ_TIMER; + evt->data = data; } /** @@ -137,12 +137,12 @@ fluid_event_timer(fluid_event_t* evt, void* data) * @param vel MIDI velocity value (0-127) */ void -fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel) +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; + evt->type = FLUID_SEQ_NOTEON; + evt->channel = channel; + evt->key = key; + evt->vel = vel; } /** @@ -152,11 +152,11 @@ fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel) * @param key MIDI note number (0-127) */ void -fluid_event_noteoff(fluid_event_t* evt, int channel, short key) +fluid_event_noteoff(fluid_event_t *evt, int channel, short key) { - evt->type = FLUID_SEQ_NOTEOFF; - evt->channel = channel; - evt->key = key; + evt->type = FLUID_SEQ_NOTEOFF; + evt->channel = channel; + evt->key = key; } /** @@ -165,16 +165,16 @@ fluid_event_noteoff(fluid_event_t* evt, int channel, short key) * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param vel MIDI velocity value (0-127) - * @param duration Duration of note (DOCME units?) + * @param duration Duration of note in the time scale used by the sequencer (by default milliseconds) */ void -fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned int duration) +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; + evt->type = FLUID_SEQ_NOTE; + evt->channel = channel; + evt->key = key; + evt->vel = vel; + evt->duration = duration; } /** @@ -183,10 +183,10 @@ fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned * @param channel MIDI channel number */ void -fluid_event_all_sounds_off(fluid_event_t* evt, int channel) +fluid_event_all_sounds_off(fluid_event_t *evt, int channel) { - evt->type = FLUID_SEQ_ALLSOUNDSOFF; - evt->channel = channel; + evt->type = FLUID_SEQ_ALLSOUNDSOFF; + evt->channel = channel; } /** @@ -195,10 +195,10 @@ fluid_event_all_sounds_off(fluid_event_t* evt, int channel) * @param channel MIDI channel number */ void -fluid_event_all_notes_off(fluid_event_t* evt, int channel) +fluid_event_all_notes_off(fluid_event_t *evt, int channel) { - evt->type = FLUID_SEQ_ALLNOTESOFF; - evt->channel = channel; + evt->type = FLUID_SEQ_ALLNOTESOFF; + evt->channel = channel; } /** @@ -208,11 +208,11 @@ fluid_event_all_notes_off(fluid_event_t* evt, int channel) * @param bank_num MIDI bank number (0-16383) */ void -fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num) +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; + evt->type = FLUID_SEQ_BANKSELECT; + evt->channel = channel; + evt->control = bank_num; } /** @@ -222,11 +222,11 @@ fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num) * @param val MIDI program number (0-127) */ void -fluid_event_program_change(fluid_event_t* evt, int channel, short val) +fluid_event_program_change(fluid_event_t *evt, int channel, short val) { - evt->type = FLUID_SEQ_PROGRAMCHANGE; - evt->channel = channel; - evt->value = val; + evt->type = FLUID_SEQ_PROGRAMCHANGE; + evt->channel = channel; + evt->value = val; } /** @@ -238,27 +238,26 @@ fluid_event_program_change(fluid_event_t* evt, int channel, short val) * @param preset_num MIDI preset number (0-127) */ void -fluid_event_program_select(fluid_event_t* evt, int channel, - unsigned int sfont_id, short bank_num, short preset_num) +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; + evt->type = FLUID_SEQ_PROGRAMSELECT; + evt->channel = channel; + evt->duration = sfont_id; + evt->value = preset_num; + evt->control = bank_num; } /** - * Set a sequencer event to be an any control change event. + * Set a sequencer event to be an any control change event (for internal use). * @param evt Sequencer event structure * @param channel MIDI channel number - * DOCME */ void -fluid_event_any_control_change(fluid_event_t* evt, int channel) +fluid_event_any_control_change(fluid_event_t *evt, int channel) { - evt->type = FLUID_SEQ_ANYCONTROLCHANGE; - evt->channel = channel; + evt->type = FLUID_SEQ_ANYCONTROLCHANGE; + evt->channel = channel; } /** @@ -268,27 +267,36 @@ fluid_event_any_control_change(fluid_event_t* evt, int channel) * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend) */ void -fluid_event_pitch_bend(fluid_event_t* evt, int channel, int pitch) +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; + evt->type = FLUID_SEQ_PITCHBEND; + evt->channel = channel; + + if(pitch < 0) + { + pitch = 0; + } + + if(pitch > 16383) + { + pitch = 16383; + } + + evt->pitch = pitch; } /** * Set a sequencer event to be a pitch wheel sensitivity event. * @param evt Sequencer event structure * @param channel MIDI channel number - * @param value MIDI pitch wheel sensitivity value (DOCME units?) + * @param value MIDI pitch wheel sensitivity value in semitones */ void -fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value) +fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, short value) { - evt->type = FLUID_SEQ_PITCHWHHELSENS; - evt->channel = channel; - evt->value = value; + evt->type = FLUID_SEQ_PITCHWHEELSENS; + evt->channel = channel; + evt->value = value; } /** @@ -298,13 +306,22 @@ fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value) * @param val MIDI modulation value (0-127) */ void -fluid_event_modulation(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_MODULATION; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } /** @@ -314,13 +331,22 @@ fluid_event_modulation(fluid_event_t* evt, int channel, short val) * @param val MIDI sustain value (0-127) */ void -fluid_event_sustain(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_SUSTAIN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } /** @@ -328,15 +354,15 @@ fluid_event_sustain(fluid_event_t* evt, int channel, short val) * @param evt Sequencer event structure * @param channel MIDI channel number * @param control MIDI control number (0-127) - * @param val MIDI control value (0-16383 DOCME is that true?) + * @param val MIDI control value (0-127) */ void -fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val) +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; + evt->type = FLUID_SEQ_CONTROLCHANGE; + evt->channel = channel; + evt->control = control; + evt->value = val; } /** @@ -346,13 +372,22 @@ fluid_event_control_change(fluid_event_t* evt, int channel, short control, short * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right) */ void -fluid_event_pan(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_PAN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } /** @@ -362,13 +397,22 @@ fluid_event_pan(fluid_event_t* evt, int channel, short val) * @param val Volume value (0-127) */ void -fluid_event_volume(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_VOLUME; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } /** @@ -378,13 +422,22 @@ fluid_event_volume(fluid_event_t* evt, int channel, short val) * @param val Reverb amount (0-127) */ void -fluid_event_reverb_send(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_REVERBSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } /** @@ -394,13 +447,22 @@ fluid_event_reverb_send(fluid_event_t* evt, int channel, short val) * @param val Chorus amount (0-127) */ void -fluid_event_chorus_send(fluid_event_t* evt, int channel, short val) +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; + evt->type = FLUID_SEQ_CHORUSSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; } @@ -410,9 +472,9 @@ fluid_event_chorus_send(fluid_event_t* evt, int channel, short val) * @since 1.1.0 */ void -fluid_event_unregistering(fluid_event_t* evt) +fluid_event_unregistering(fluid_event_t *evt) { - evt->type = FLUID_SEQ_UNREGISTERING; + evt->type = FLUID_SEQ_UNREGISTERING; } /** @@ -422,14 +484,61 @@ fluid_event_unregistering(fluid_event_t* evt) * @param val Aftertouch amount (0-127) * @since 1.1.0 */ -void -fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val) +void +fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val) +{ + evt->type = FLUID_SEQ_CHANNELPRESSURE; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a polyphonic aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param val Aftertouch amount (0-127) + * @since 2.0.0 + */ +void +fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val) { - evt->type = FLUID_SEQ_CHANNELPRESSURE; - evt->channel = channel; - if (val < 0) val = 0; - if (val > 127) val = 127; - evt->value = val; + evt->type = FLUID_SEQ_KEYPRESSURE; + evt->channel = channel; + + if(key < 0) + { + key = 0; + } + + if(key > 127) + { + key = 127; + } + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->key = key; + evt->value = val; } /** @@ -437,10 +546,10 @@ fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val) * @param evt Sequencer event structure * @since 1.1.0 */ -void -fluid_event_system_reset(fluid_event_t* evt) +void +fluid_event_system_reset(fluid_event_t *evt) { - evt->type = FLUID_SEQ_SYSTEMRESET; + evt->type = FLUID_SEQ_SYSTEMRESET; } @@ -454,49 +563,50 @@ fluid_event_system_reset(fluid_event_t* evt) * @param evt Sequencer event structure * @return Event type (#fluid_seq_event_type). */ -int fluid_event_get_type(fluid_event_t* evt) +int fluid_event_get_type(fluid_event_t *evt) { - return evt->type; + return evt->type; } /** + * @internal * Get the time field from a sequencer event structure. * @param evt Sequencer event structure - * @return Time value (DOCME units?) + * @return Time value */ -unsigned int fluid_event_get_time(fluid_event_t* evt) +unsigned int fluid_event_get_time(fluid_event_t *evt) { - return evt->time; + return evt->time; } /** - * Get the source field from a sequencer event structure. + * Get the source sequencer client from a sequencer event structure. * @param evt Sequencer event structure - * @return DOCME + * @return source field of the sequencer event */ -short fluid_event_get_source(fluid_event_t* evt) +fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt) { - return evt->src; + return evt->src; } /** - * Get the dest field from a sequencer event structure. + * Get the dest sequencer client from a sequencer event structure. * @param evt Sequencer event structure - * @return DOCME + * @return dest field of the sequencer event */ -short fluid_event_get_dest(fluid_event_t* evt) +fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt) { - return evt->dest; + return evt->dest; } /** * Get the MIDI channel field from a sequencer event structure. * @param evt Sequencer event structure - * @return MIDI channel number (DOCME 0-15 or more?) + * @return MIDI zero-based channel number */ -int fluid_event_get_channel(fluid_event_t* evt) +int fluid_event_get_channel(fluid_event_t *evt) { - return evt->channel; + return evt->channel; } /** @@ -504,9 +614,9 @@ int fluid_event_get_channel(fluid_event_t* evt) * @param evt Sequencer event structure * @return MIDI note number (0-127) */ -short fluid_event_get_key(fluid_event_t* evt) +short fluid_event_get_key(fluid_event_t *evt) { - return evt->key; + return evt->key; } /** @@ -514,10 +624,10 @@ short fluid_event_get_key(fluid_event_t* evt) * @param evt Sequencer event structure * @return MIDI velocity value (0-127) */ -short fluid_event_get_velocity(fluid_event_t* evt) +short fluid_event_get_velocity(fluid_event_t *evt) { - return evt->vel; + return evt->vel; } /** @@ -525,9 +635,9 @@ short fluid_event_get_velocity(fluid_event_t* evt) * @param evt Sequencer event structure * @return MIDI control number (0-127) */ -short fluid_event_get_control(fluid_event_t* evt) +short fluid_event_get_control(fluid_event_t *evt) { - return evt->control; + return evt->control; } /** @@ -537,13 +647,13 @@ short fluid_event_get_control(fluid_event_t* evt) * * The Value field is used by the following event types: * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num), - * #FLUID_SEQ_PITCHWHHELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, + * #FLUID_SEQ_PITCHWHEELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. */ -short fluid_event_get_value(fluid_event_t* evt) +short fluid_event_get_value(fluid_event_t *evt) { - return evt->value; + return evt->value; } /** @@ -553,21 +663,21 @@ short fluid_event_get_value(fluid_event_t* evt) * * Used by the #FLUID_SEQ_TIMER event type. */ -void* fluid_event_get_data(fluid_event_t* evt) +void *fluid_event_get_data(fluid_event_t *evt) { - return evt->data; + return evt->data; } /** * Get the duration field from a sequencer event structure. * @param evt Sequencer event structure - * @return Note duration value (DOCME units?) + * @return Note duration value in the time scale used by the sequencer (by default milliseconds) * * Used by the #FLUID_SEQ_NOTE event type. */ -unsigned int fluid_event_get_duration(fluid_event_t* evt) +unsigned int fluid_event_get_duration(fluid_event_t *evt) { - return evt->duration; + return evt->duration; } /** @@ -578,9 +688,9 @@ unsigned int fluid_event_get_duration(fluid_event_t* evt) * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT * event types. */ -short fluid_event_get_bank(fluid_event_t* evt) +short fluid_event_get_bank(fluid_event_t *evt) { - return evt->control; + return evt->control; } /** @@ -590,9 +700,9 @@ short fluid_event_get_bank(fluid_event_t* evt) * * Used by the #FLUID_SEQ_PITCHBEND event type. */ -int fluid_event_get_pitch(fluid_event_t* evt) +int fluid_event_get_pitch(fluid_event_t *evt) { - return evt->pitch; + return evt->pitch; } /** @@ -604,9 +714,9 @@ int fluid_event_get_pitch(fluid_event_t* evt) * event types. */ short -fluid_event_get_program(fluid_event_t* evt) +fluid_event_get_program(fluid_event_t *evt) { - return evt->value; + return evt->value; } /** @@ -617,9 +727,9 @@ fluid_event_get_program(fluid_event_t* evt) * Used by the #FLUID_SEQ_PROGRAMSELECT event type. */ unsigned int -fluid_event_get_sfont_id(fluid_event_t* evt) +fluid_event_get_sfont_id(fluid_event_t *evt) { - return evt->duration; + return evt->duration; } @@ -628,154 +738,176 @@ fluid_event_get_sfont_id(fluid_event_t* evt) /* heap management */ /********************/ -fluid_evt_heap_t* +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; + 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 = FLUID_NEW(fluid_evt_heap_t); - heap->freelist = NULL; - fluid_mutex_init(heap->mutex); + 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); + /* 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; - } + /* 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); + /* 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); - } + 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); + return (heap); } void -_fluid_evt_heap_free(fluid_evt_heap_t* heap) +_fluid_evt_heap_free(fluid_evt_heap_t *heap) { #ifdef HEAP_WITH_DYNALLOC - fluid_evt_entry *tmp, *next; + fluid_evt_entry *tmp, *next; + + /* LOCK */ + fluid_mutex_lock(heap->mutex); - /* LOCK */ - fluid_mutex_lock(heap->mutex); + tmp = heap->freelist; - tmp = heap->freelist; - while (tmp) { - next = tmp->next; - FLUID_FREE(tmp); - tmp = next; - } + while(tmp) + { + next = tmp->next; + FLUID_FREE(tmp); + tmp = next; + } - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); - fluid_mutex_destroy(heap->mutex); + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); + fluid_mutex_destroy(heap->mutex); - FLUID_FREE(heap); + FLUID_FREE(heap); #else - FLUID_FREE(heap); + FLUID_FREE(heap); #endif } -fluid_evt_entry* -_fluid_seq_heap_get_free(fluid_evt_heap_t* heap) +fluid_evt_entry * +_fluid_seq_heap_get_free(fluid_evt_heap_t *heap) { #ifdef HEAP_WITH_DYNALLOC - fluid_evt_entry* evt = NULL; + fluid_evt_entry *evt = NULL; - /* LOCK */ - fluid_mutex_lock(heap->mutex); + /* 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; + + if(heap->freelist == NULL) + { + heap->freelist = FLUID_NEW(fluid_evt_entry); + + if(heap->freelist != NULL) + { + heap->freelist->next = NULL; + } } - } + #endif - evt = heap->freelist; + evt = heap->freelist; - if (evt != NULL) { - heap->freelist = heap->freelist->next; - evt->next = NULL; - } + if(evt != NULL) + { + heap->freelist = heap->freelist->next; + evt->next = NULL; + } - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); + /* UNLOCK */ + fluid_mutex_unlock(heap->mutex); - return evt; + return evt; #else - fluid_evt_entry* evt; - if (heap->head == NULL) return NULL; + fluid_evt_entry *evt; - /* take from head of the heap */ - /* critical - should threadlock ? */ - evt = heap->head; - heap->head = heap->head->next; + if(heap->head == NULL) + { + return NULL; + } - return evt; + /* 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) +_fluid_seq_heap_set_free(fluid_evt_heap_t *heap, fluid_evt_entry *evt) { #ifdef HEAP_WITH_DYNALLOC - /* LOCK */ - fluid_mutex_lock(heap->mutex); + /* LOCK */ + fluid_mutex_lock(heap->mutex); - evt->next = heap->freelist; - heap->freelist = evt; + evt->next = heap->freelist; + heap->freelist = evt; - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); + /* 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; + /* append to the end of the heap */ + /* critical - should threadlock ? */ + heap->tail->next = evt; + heap->tail = evt; + evt->next = NULL; #endif } diff --git a/libs/fluidsynth/src/fluid_event.h b/libs/fluidsynth/src/fluid_event.h new file mode 100644 index 0000000000..4beb0147e5 --- /dev/null +++ b/libs/fluidsynth/src/fluid_event.h @@ -0,0 +1,87 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, 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; + fluid_seq_id_t src; + fluid_seq_id_t 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); + +void fluid_event_clear(fluid_event_t *evt); + +/* 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/libs/fluidsynth/src/fluid_gen.c b/libs/fluidsynth/src/fluid_gen.c index 0f1413eab2..f01f941942 100644 --- a/libs/fluidsynth/src/fluid_gen.c +++ b/libs/fluidsynth/src/fluid_gen.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,89 +24,94 @@ /* 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 } +static const 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 }, + { GEN_CUSTOM_BALANCE, 1, 0, -960.0f, 960.0f, 0.0f }, + { GEN_CUSTOM_FILTERFC, 1, 2, 0.0f, 22050.0f, 0.0f }, + { GEN_CUSTOM_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f } }; /** * Set an array of generators to their default values. * @param gen Array of generators (should be #GEN_LAST in size). - * @return Always returns 0 + * @return Always returns #FLUID_OK */ int -fluid_gen_set_default_values(fluid_gen_t* gen) +fluid_gen_set_default_values(fluid_gen_t *gen) { - int i; + 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; - } + 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; + return FLUID_OK; } @@ -115,35 +120,37 @@ fluid_gen_set_default_values(fluid_gen_t* gen) * Set an array of generators to their initial value */ int -fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel) +fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) { - int i; + int i; - fluid_gen_set_default_values(gen); + fluid_gen_set_default_values(gen); - for (i = 0; i < GEN_LAST; i++) { - gen[i].nrpn = fluid_channel_get_gen(channel, i); + 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; - } - } + /* 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; + 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)); + 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; + 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/libs/fluidsynth/src/fluid_gen.h b/libs/fluidsynth/src/fluid_gen.h index f54d0490fc..d156b807b9 100644 --- a/libs/fluidsynth/src/fluid_gen.h +++ b/libs/fluidsynth/src/fluid_gen.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,21 +24,44 @@ #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()) */ +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; +/* + * SoundFont generator structure. + */ +typedef struct _fluid_gen_t +{ + unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ + double val; /**< The nominal value */ + double mod; /**< Change by modulators */ + double nrpn; /**< Change by NRPN messages */ +} fluid_gen_t; + +/* + * Enum value for 'flags' field of #fluid_gen_t (not really flags). + */ +enum fluid_gen_flags +{ + GEN_UNUSED, /**< Generator value is not set */ + GEN_SET, /**< Generator value is set */ + GEN_ABS_NRPN /**< Generator is an absolute value */ +}; + #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); +int fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel); +int fluid_gen_set_default_values(fluid_gen_t *gen); #endif /* _FLUID_GEN_H */ diff --git a/libs/fluidsynth/src/fluid_hash.c b/libs/fluidsynth/src/fluid_hash.c index 9d5a92009e..b6586895b5 100644 --- a/libs/fluidsynth/src/fluid_hash.c +++ b/libs/fluidsynth/src/fluid_hash.c @@ -42,67 +42,71 @@ typedef struct { - fluid_hashtable_t *hashtable; - fluid_hashnode_t *prev_node; - fluid_hashnode_t *node; - int position; - int pre_advanced; // Boolean - int version; + fluid_hashtable_t *hashtable; + fluid_hashnode_t *prev_node; + fluid_hashnode_t *node; + int position; + int pre_advanced; // Boolean + int version; } RealIter; /* Excerpt from glib gprimes.c */ -static const guint primes[] = +static const unsigned int primes[] = { - 11, - 19, - 37, - 73, - 109, - 163, - 251, - 367, - 557, - 823, - 1237, - 1861, - 2777, - 4177, - 6247, - 9371, - 14057, - 21089, - 31627, - 47431, - 71143, - 106721, - 160073, - 240101, - 360163, - 540217, - 810343, - 1215497, - 1823231, - 2734867, - 4102283, - 6153409, - 9230113, - 13845163, + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, }; -static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]); +static const unsigned int nprimes = FLUID_N_ELEMENTS(primes); static unsigned int -spaced_primes_closest (unsigned int num) +spaced_primes_closest(unsigned int num) { - unsigned int i; + unsigned int i; - for (i = 0; i < nprimes; i++) - if (primes[i] > num) - return primes[i]; + for(i = 0; i < nprimes; i++) + { + if(primes[i] > num) + { + return primes[i]; + } + } - return primes[nprimes - 1]; + return primes[nprimes - 1]; } /* End excerpt from glib gprimes.c */ @@ -136,51 +140,57 @@ spaced_primes_closest (unsigned int num) * save insertions from having to compute the hash record again for * the new record. */ -static inline fluid_hashnode_t ** -fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key, - unsigned int *hash_return) +static FLUID_INLINE fluid_hashnode_t ** +fluid_hashtable_lookup_node(fluid_hashtable_t *hashtable, const void *key, + unsigned int *hash_return) { - fluid_hashnode_t **node_ptr, *node; - unsigned int hash_value; - - hash_value = (* hashtable->hash_func)(key); - node_ptr = &hashtable->nodes[hash_value % hashtable->size]; - - if (hash_return) - *hash_return = hash_value; - - /* Hash table lookup needs to be fast. - * We therefore remove the extra conditional of testing - * whether to call the key_equal_func or not from - * the inner loop. - * - * Additional optimisation: first check if our full hash - * values are equal so we can avoid calling the full-blown - * key equality function in most cases. - */ - if (hashtable->key_equal_func) + fluid_hashnode_t **node_ptr, *node; + unsigned int hash_value; + + hash_value = (* hashtable->hash_func)(key); + node_ptr = &hashtable->nodes[hash_value % hashtable->size]; + + if(hash_return) { - while ((node = *node_ptr)) + *hash_return = hash_value; + } + + /* Hash table lookup needs to be fast. + * We therefore remove the extra conditional of testing + * whether to call the key_equal_func or not from + * the inner loop. + * + * Additional optimisation: first check if our full hash + * values are equal so we can avoid calling the full-blown + * key equality function in most cases. + */ + if(hashtable->key_equal_func) + { + while((node = *node_ptr)) { - if (node->key_hash == hash_value && - hashtable->key_equal_func (node->key, key)) - break; + if(node->key_hash == hash_value && + hashtable->key_equal_func(node->key, key)) + { + break; + } - node_ptr = &(*node_ptr)->next; + node_ptr = &(*node_ptr)->next; } } - else + else { - while ((node = *node_ptr)) + while((node = *node_ptr)) { - if (node->key == key) - break; + if(node->key == key) + { + break; + } - node_ptr = &(*node_ptr)->next; + node_ptr = &(*node_ptr)->next; } } - return node_ptr; + return node_ptr; } /* @@ -213,25 +223,29 @@ fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key, * modified at all. Stay tuned. :) */ static void -fluid_hashtable_remove_node (fluid_hashtable_t *hashtable, - fluid_hashnode_t ***node_ptr_ptr, int notify) +fluid_hashtable_remove_node(fluid_hashtable_t *hashtable, + fluid_hashnode_t ***node_ptr_ptr, int notify) { - fluid_hashnode_t **node_ptr, *node; + fluid_hashnode_t **node_ptr, *node; - node_ptr = *node_ptr_ptr; - node = *node_ptr; + node_ptr = *node_ptr_ptr; + node = *node_ptr; - *node_ptr = node->next; + *node_ptr = node->next; - if (notify && hashtable->key_destroy_func) - hashtable->key_destroy_func (node->key); + if(notify && hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } - if (notify && hashtable->value_destroy_func) - hashtable->value_destroy_func (node->value); + if(notify && hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } - FLUID_FREE (node); + FLUID_FREE(node); - hashtable->nnodes--; + hashtable->nnodes--; } /* @@ -246,16 +260,20 @@ fluid_hashtable_remove_node (fluid_hashtable_t *hashtable, * for the key and value of the hash node. */ static void -fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify) +fluid_hashtable_remove_all_nodes(fluid_hashtable_t *hashtable, int notify) { - fluid_hashnode_t **node_ptr; - int i; + fluid_hashnode_t **node_ptr; + int i; - for (i = 0; i < hashtable->size; i++) - for (node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) - fluid_hashtable_remove_node (hashtable, &node_ptr, notify); + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + } + } - hashtable->nnodes = 0; + hashtable->nnodes = 0; } /* @@ -268,43 +286,45 @@ fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify) * fluid_hashtable_maybe_resize() instead. */ static void -fluid_hashtable_resize (fluid_hashtable_t *hashtable) +fluid_hashtable_resize(fluid_hashtable_t *hashtable) { - fluid_hashnode_t **new_nodes; - fluid_hashnode_t *node; - fluid_hashnode_t *next; - unsigned int hash_val; - int new_size; - int i; + fluid_hashnode_t **new_nodes; + fluid_hashnode_t *node; + fluid_hashnode_t *next; + unsigned int hash_val; + int new_size; + int i; - new_size = spaced_primes_closest (hashtable->nnodes); - new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : - ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); + new_size = spaced_primes_closest(hashtable->nnodes); + new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : + ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); - new_nodes = FLUID_ARRAY (fluid_hashnode_t *, new_size); + new_nodes = FLUID_ARRAY(fluid_hashnode_t *, new_size); - if (!new_nodes) - { - FLUID_LOG (FLUID_ERR, "Out of memory"); - return; - } + if(!new_nodes) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } - FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *)); + FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t *)); - for (i = 0; i < hashtable->size; i++) - for (node = hashtable->nodes[i]; node; node = next) - { - next = node->next; + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = next) + { + next = node->next; - hash_val = node->key_hash % new_size; + hash_val = node->key_hash % new_size; - node->next = new_nodes[hash_val]; - new_nodes[hash_val] = node; - } + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + } - FLUID_FREE (hashtable->nodes); - hashtable->nodes = new_nodes; - hashtable->size = new_size; + FLUID_FREE(hashtable->nodes); + hashtable->nodes = new_nodes; + hashtable->size = new_size; } /* @@ -316,15 +336,17 @@ fluid_hashtable_resize (fluid_hashtable_t *hashtable) * Essentially, calls fluid_hashtable_resize() if the table has strayed * too far from its ideal size for its number of nodes. */ -static inline void -fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable) +static FLUID_INLINE void +fluid_hashtable_maybe_resize(fluid_hashtable_t *hashtable) { - int nnodes = hashtable->nnodes; - int size = hashtable->size; + int nnodes = hashtable->nnodes; + int size = hashtable->size; - if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || - (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) - fluid_hashtable_resize (hashtable); + if((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || + (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) + { + fluid_hashtable_resize(hashtable); + } } /** @@ -345,10 +367,10 @@ fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable) * * Return value: a new #fluid_hashtable_t. **/ -fluid_hashtable_t* -new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) +fluid_hashtable_t * +new_fluid_hashtable(fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) { - return new_fluid_hashtable_full (hash_func, key_equal_func, NULL, NULL); + return new_fluid_hashtable_full(hash_func, key_equal_func, NULL, NULL); } @@ -369,33 +391,39 @@ new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_f * * Return value: a new #fluid_hashtable_t. **/ -fluid_hashtable_t* -new_fluid_hashtable_full (fluid_hash_func_t hash_func, - fluid_equal_func_t key_equal_func, - fluid_destroy_notify_t key_destroy_func, - fluid_destroy_notify_t value_destroy_func) +fluid_hashtable_t * +new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func) { - fluid_hashtable_t *hashtable; + fluid_hashtable_t *hashtable; - hashtable = FLUID_NEW (fluid_hashtable_t); + hashtable = FLUID_NEW(fluid_hashtable_t); - if (!hashtable) - { - FLUID_LOG (FLUID_ERR, "Out of memory"); - return NULL; - } - - hashtable->size = HASH_TABLE_MIN_SIZE; - hashtable->nnodes = 0; - hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; - hashtable->key_equal_func = key_equal_func; - hashtable->ref_count = 1; - hashtable->key_destroy_func = key_destroy_func; - hashtable->value_destroy_func = value_destroy_func; - hashtable->nodes = FLUID_ARRAY (fluid_hashnode_t*, hashtable->size); - FLUID_MEMSET (hashtable->nodes, 0, hashtable->size * sizeof (fluid_hashnode_t *)); - - return hashtable; + if(!hashtable) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + hashtable->size = HASH_TABLE_MIN_SIZE; + hashtable->nnodes = 0; + hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; + hashtable->key_equal_func = key_equal_func; + fluid_atomic_int_set(&hashtable->ref_count, 1); + hashtable->key_destroy_func = key_destroy_func; + hashtable->value_destroy_func = value_destroy_func; + hashtable->nodes = FLUID_ARRAY(fluid_hashnode_t *, hashtable->size); + if(hashtable->nodes == NULL) + { + delete_fluid_hashtable(hashtable); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes)); + + return hashtable; } /** @@ -411,7 +439,7 @@ new_fluid_hashtable_full (fluid_hash_func_t hash_func, * gpointer key, value; * * fluid_hashtable_iter_init (&iter, hashtable); - * while (fluid_hashtable_iter_next (&iter, &key, &value)) + * while (fluid_hashtable_iter_next (&iter, &key, &value)) * { * /* do something with key and value */ * } @@ -420,19 +448,19 @@ new_fluid_hashtable_full (fluid_hash_func_t hash_func, * Since: 2.16 **/ void -fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, - fluid_hashtable_t *hashtable) +fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, + fluid_hashtable_t *hashtable) { - RealIter *ri = (RealIter *) iter; + RealIter *ri = (RealIter *) iter; - fluid_return_if_fail (iter != NULL); - fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail(iter != NULL); + fluid_return_if_fail(hashtable != NULL); - ri->hashtable = hashtable; - ri->prev_node = NULL; - ri->node = NULL; - ri->position = -1; - ri->pre_advanced = FALSE; + ri->hashtable = hashtable; + ri->prev_node = NULL; + ri->node = NULL; + ri->position = -1; + ri->pre_advanced = FALSE; } /** @@ -450,45 +478,55 @@ fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, * Since: 2.16 **/ int -fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, - void **value) +fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, + void **value) { - RealIter *ri = (RealIter *) iter; + RealIter *ri = (RealIter *) iter; - fluid_return_val_if_fail (iter != NULL, FALSE); + fluid_return_val_if_fail(iter != NULL, FALSE); - if (ri->pre_advanced) + if(ri->pre_advanced) { - ri->pre_advanced = FALSE; + ri->pre_advanced = FALSE; - if (ri->node == NULL) - return FALSE; + if(ri->node == NULL) + { + return FALSE; + } } - else + else { - if (ri->node != NULL) - { - ri->prev_node = ri->node; - ri->node = ri->node->next; - } - - while (ri->node == NULL) - { - ri->position++; - if (ri->position >= ri->hashtable->size) - return FALSE; - - ri->prev_node = NULL; - ri->node = ri->hashtable->nodes[ri->position]; - } + if(ri->node != NULL) + { + ri->prev_node = ri->node; + ri->node = ri->node->next; + } + + while(ri->node == NULL) + { + ri->position++; + + if(ri->position >= ri->hashtable->size) + { + return FALSE; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } } - if (key != NULL) - *key = ri->node->key; - if (value != NULL) - *value = ri->node->value; + if(key != NULL) + { + *key = ri->node->key; + } + + if(value != NULL) + { + *value = ri->node->value; + } - return TRUE; + return TRUE; } /** @@ -502,62 +540,74 @@ fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, * Since: 2.16 **/ fluid_hashtable_t * -fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter) +fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter) { - fluid_return_val_if_fail (iter != NULL, NULL); + fluid_return_val_if_fail(iter != NULL, NULL); - return ((RealIter *) iter)->hashtable; + return ((RealIter *) iter)->hashtable; } static void -iter_remove_or_steal (RealIter *ri, int notify) +iter_remove_or_steal(RealIter *ri, int notify) { - fluid_hashnode_t *prev; - fluid_hashnode_t *node; - int position; + fluid_hashnode_t *prev; + fluid_hashnode_t *node; + int position; - fluid_return_if_fail (ri != NULL); - fluid_return_if_fail (ri->node != NULL); + fluid_return_if_fail(ri != NULL); + fluid_return_if_fail(ri->node != NULL); - prev = ri->prev_node; - node = ri->node; - position = ri->position; + prev = ri->prev_node; + node = ri->node; + position = ri->position; - /* pre-advance the iterator since we will remove the node */ + /* pre-advance the iterator since we will remove the node */ - ri->node = ri->node->next; - /* ri->prev_node is still the correct previous node */ + ri->node = ri->node->next; + /* ri->prev_node is still the correct previous node */ - while (ri->node == NULL) + while(ri->node == NULL) { - ri->position++; - if (ri->position >= ri->hashtable->size) - break; + ri->position++; - ri->prev_node = NULL; - ri->node = ri->hashtable->nodes[ri->position]; + if(ri->position >= ri->hashtable->size) + { + break; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; } - ri->pre_advanced = TRUE; + ri->pre_advanced = TRUE; - /* remove the node */ + /* remove the node */ - if (prev != NULL) - prev->next = node->next; - else - ri->hashtable->nodes[position] = node->next; + if(prev != NULL) + { + prev->next = node->next; + } + else + { + ri->hashtable->nodes[position] = node->next; + } - if (notify) + if(notify) { - if (ri->hashtable->key_destroy_func) - ri->hashtable->key_destroy_func(node->key); - if (ri->hashtable->value_destroy_func) - ri->hashtable->value_destroy_func(node->value); + if(ri->hashtable->key_destroy_func) + { + ri->hashtable->key_destroy_func(node->key); + } + + if(ri->hashtable->value_destroy_func) + { + ri->hashtable->value_destroy_func(node->value); + } } - FLUID_FREE (node); + FLUID_FREE(node); - ri->hashtable->nnodes--; + ri->hashtable->nnodes--; } /** @@ -571,15 +621,15 @@ iter_remove_or_steal (RealIter *ri, int notify) * * 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 + * you have to make sure that any dynamically allocated values are freed * yourself. * * Since: 2.16 **/ void -fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter) +fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter) { - iter_remove_or_steal ((RealIter *) iter, TRUE); + iter_remove_or_steal((RealIter *) iter, TRUE); } /** @@ -595,9 +645,9 @@ fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter) * Since: 2.16 **/ void -fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter) +fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter) { - iter_remove_or_steal ((RealIter *) iter, FALSE); + iter_remove_or_steal((RealIter *) iter, FALSE); } @@ -612,14 +662,14 @@ fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter) * * Since: 2.10 **/ -fluid_hashtable_t* -fluid_hashtable_ref (fluid_hashtable_t *hashtable) +fluid_hashtable_t * +fluid_hashtable_ref(fluid_hashtable_t *hashtable) { - fluid_return_val_if_fail (hashtable != NULL, NULL); - fluid_return_val_if_fail (hashtable->ref_count > 0, hashtable); + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0, hashtable); - fluid_atomic_int_add (&hashtable->ref_count, 1); - return hashtable; + fluid_atomic_int_add(&hashtable->ref_count, 1); + return hashtable; } /** @@ -634,16 +684,16 @@ fluid_hashtable_ref (fluid_hashtable_t *hashtable) * Since: 2.10 **/ void -fluid_hashtable_unref (fluid_hashtable_t *hashtable) +fluid_hashtable_unref(fluid_hashtable_t *hashtable) { - fluid_return_if_fail (hashtable != NULL); - fluid_return_if_fail (hashtable->ref_count > 0); + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); - if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 0) + if(fluid_atomic_int_exchange_and_add(&hashtable->ref_count, -1) - 1 == 0) { - fluid_hashtable_remove_all_nodes (hashtable, TRUE); - FLUID_FREE (hashtable->nodes); - FLUID_FREE (hashtable); + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + FLUID_FREE(hashtable->nodes); + FLUID_FREE(hashtable); } } @@ -659,13 +709,13 @@ fluid_hashtable_unref (fluid_hashtable_t *hashtable) * destruction phase. **/ void -delete_fluid_hashtable (fluid_hashtable_t *hashtable) +delete_fluid_hashtable(fluid_hashtable_t *hashtable) { - fluid_return_if_fail (hashtable != NULL); - fluid_return_if_fail (hashtable->ref_count > 0); + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); - fluid_hashtable_remove_all (hashtable); - fluid_hashtable_unref (hashtable); + fluid_hashtable_remove_all(hashtable); + fluid_hashtable_unref(hashtable); } /** @@ -681,15 +731,15 @@ delete_fluid_hashtable (fluid_hashtable_t *hashtable) * Return value: the associated value, or %NULL if the key is not found. **/ void * -fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key) +fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key) { - fluid_hashnode_t *node; + fluid_hashnode_t *node; - fluid_return_val_if_fail (hashtable != NULL, NULL); + fluid_return_val_if_fail(hashtable != NULL, NULL); - node = *fluid_hashtable_lookup_node (hashtable, key, NULL); + node = *fluid_hashtable_lookup_node(hashtable, key, NULL); - return node ? node->value : NULL; + return node ? node->value : NULL; } /** @@ -707,26 +757,32 @@ fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key) * Return value: %TRUE if the key was found in the #fluid_hashtable_t. **/ int -fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, - const void *lookup_key, - void **orig_key, void **value) +fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, + const void *lookup_key, + void **orig_key, void **value) { - fluid_hashnode_t *node; + fluid_hashnode_t *node; - fluid_return_val_if_fail (hashtable != NULL, FALSE); + fluid_return_val_if_fail(hashtable != NULL, FALSE); - node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL); + node = *fluid_hashtable_lookup_node(hashtable, lookup_key, NULL); - if (node == NULL) - return FALSE; + if(node == NULL) + { + return FALSE; + } - if (orig_key) - *orig_key = node->key; + if(orig_key) + { + *orig_key = node->key; + } - if (value) - *value = node->value; + if(value) + { + *value = node->value; + } - return TRUE; + return TRUE; } /* @@ -746,54 +802,61 @@ fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, * new node. */ static void -fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key, - void *value, int keep_new_key) +fluid_hashtable_insert_internal(fluid_hashtable_t *hashtable, void *key, + void *value, int keep_new_key) { - fluid_hashnode_t **node_ptr, *node; - unsigned int key_hash; + fluid_hashnode_t **node_ptr, *node; + unsigned int key_hash; - fluid_return_if_fail (hashtable != NULL); - fluid_return_if_fail (hashtable->ref_count > 0); + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); - node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash); + node_ptr = fluid_hashtable_lookup_node(hashtable, key, &key_hash); - if ((node = *node_ptr)) + if((node = *node_ptr)) { - if (keep_new_key) + if(keep_new_key) { - if (hashtable->key_destroy_func) - hashtable->key_destroy_func (node->key); - node->key = key; + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } + + node->key = key; } - else + else { - if (hashtable->key_destroy_func) - hashtable->key_destroy_func (key); + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(key); + } } - if (hashtable->value_destroy_func) - hashtable->value_destroy_func (node->value); + if(hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } - node->value = value; + node->value = value; } - else + else { - node = FLUID_NEW (fluid_hashnode_t); + node = FLUID_NEW(fluid_hashnode_t); - if (!node) - { - FLUID_LOG (FLUID_ERR, "Out of memory"); - return; - } + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } - node->key = key; - node->value = value; - node->key_hash = key_hash; - node->next = NULL; + node->key = key; + node->value = value; + node->key_hash = key_hash; + node->next = NULL; - *node_ptr = node; - hashtable->nnodes++; - fluid_hashtable_maybe_resize (hashtable); + *node_ptr = node; + hashtable->nnodes++; + fluid_hashtable_maybe_resize(hashtable); } } @@ -812,9 +875,9 @@ fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key, * using that function. **/ void -fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value) +fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value) { - fluid_hashtable_insert_internal (hashtable, key, value, FALSE); + fluid_hashtable_insert_internal(hashtable, key, value, FALSE); } /** @@ -831,9 +894,9 @@ fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value) * #fluid_hashtable_t, the old key is freed using that function. **/ void -fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value) +fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value) { - fluid_hashtable_insert_internal (hashtable, key, value, TRUE); + fluid_hashtable_insert_internal(hashtable, key, value, TRUE); } /* @@ -850,21 +913,24 @@ fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value) * destroy notify handlers only if @notify is %TRUE. */ static int -fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key, - int notify) +fluid_hashtable_remove_internal(fluid_hashtable_t *hashtable, const void *key, + int notify) { - fluid_hashnode_t **node_ptr; + fluid_hashnode_t **node_ptr; + + fluid_return_val_if_fail(hashtable != NULL, FALSE); - fluid_return_val_if_fail (hashtable != NULL, FALSE); + node_ptr = fluid_hashtable_lookup_node(hashtable, key, NULL); - node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL); - if (*node_ptr == NULL) - return FALSE; + if(*node_ptr == NULL) + { + return FALSE; + } - fluid_hashtable_remove_node (hashtable, &node_ptr, notify); - fluid_hashtable_maybe_resize (hashtable); + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + fluid_hashtable_maybe_resize(hashtable); - return TRUE; + return TRUE; } /** @@ -882,9 +948,9 @@ fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key, * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. **/ int -fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key) +fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key) { - return fluid_hashtable_remove_internal (hashtable, key, TRUE); + return fluid_hashtable_remove_internal(hashtable, key, TRUE); } /** @@ -898,9 +964,9 @@ fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key) * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. **/ int -fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key) +fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key) { - return fluid_hashtable_remove_internal (hashtable, key, FALSE); + return fluid_hashtable_remove_internal(hashtable, key, FALSE); } /** @@ -917,14 +983,15 @@ fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key) * Since: 2.12 **/ void -fluid_hashtable_remove_all (fluid_hashtable_t *hashtable) +fluid_hashtable_remove_all(fluid_hashtable_t *hashtable) { - fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail(hashtable != NULL); - fluid_hashtable_remove_all_nodes (hashtable, TRUE); - fluid_hashtable_maybe_resize (hashtable); + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + fluid_hashtable_maybe_resize(hashtable); } +#if 0 /** * fluid_hashtable_steal_all: * @hashtable: a #fluid_hashtable_t. @@ -935,13 +1002,14 @@ fluid_hashtable_remove_all (fluid_hashtable_t *hashtable) * Since: 2.12 **/ void -fluid_hashtable_steal_all (fluid_hashtable_t *hashtable) +fluid_hashtable_steal_all(fluid_hashtable_t *hashtable) { - fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail(hashtable != NULL); - fluid_hashtable_remove_all_nodes (hashtable, FALSE); - fluid_hashtable_maybe_resize (hashtable); + fluid_hashtable_remove_all_nodes(hashtable, FALSE); + fluid_hashtable_maybe_resize(hashtable); } +#endif /* * fluid_hashtable_foreach_remove_or_steal: @@ -961,27 +1029,33 @@ fluid_hashtable_steal_all (fluid_hashtable_t *hashtable) * for each removed node. */ static unsigned int -fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable, - fluid_hr_func_t func, void *user_data, - int notify) +fluid_hashtable_foreach_remove_or_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data, + int notify) { - fluid_hashnode_t *node, **node_ptr; - unsigned int deleted = 0; - int i; + fluid_hashnode_t *node, **node_ptr; + unsigned int deleted = 0; + int i; - for (i = 0; i < hashtable->size; i++) - for (node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) - if ((* func) (node->key, node->value, user_data)) + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) { - fluid_hashtable_remove_node (hashtable, &node_ptr, notify); - deleted++; + if((* func)(node->key, node->value, user_data)) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + deleted++; + } + else + { + node_ptr = &node->next; + } } - else - node_ptr = &node->next; + } - fluid_hashtable_maybe_resize (hashtable); + fluid_hashtable_maybe_resize(hashtable); - return deleted; + return deleted; } #if 0 @@ -997,19 +1071,19 @@ fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable, * the #fluid_hashtable_t, they are used to free the memory allocated for the removed * keys and values. * - * See #fluid_hashtable_iter_t for an alternative way to loop over the + * See #fluid_hashtable_iter_t for an alternative way to loop over the * key/value pairs in the hash table. * * Return value: the number of key/value pairs removed. **/ static unsigned int -fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable, - fluid_hr_func_t func, void *user_data) +fluid_hashtable_foreach_remove(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) { - fluid_return_val_if_fail (hashtable != NULL, 0); - fluid_return_val_if_fail (func != NULL, 0); + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); - return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE); + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, TRUE); } #endif @@ -1023,19 +1097,19 @@ fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable, * If the function returns %TRUE, then the key/value pair is removed from the * #fluid_hashtable_t, but no key or value destroy functions are called. * - * See #fluid_hashtable_iter_t for an alternative way to loop over the + * See #fluid_hashtable_iter_t for an alternative way to loop over the * key/value pairs in the hash table. * * Return value: the number of key/value pairs removed. **/ unsigned int -fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, - fluid_hr_func_t func, void *user_data) +fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) { - fluid_return_val_if_fail (hashtable != NULL, 0); - fluid_return_val_if_fail (func != NULL, 0); + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); - return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, FALSE); + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, FALSE); } /** @@ -1055,18 +1129,22 @@ fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, * order searches in contrast to fluid_hashtable_lookup(). **/ void -fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, - void *user_data) +fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data) { - fluid_hashnode_t *node; - int i; + fluid_hashnode_t *node; + int i; - fluid_return_if_fail (hashtable != NULL); - fluid_return_if_fail (func != NULL); + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(func != NULL); - for (i = 0; i < hashtable->size; i++) - for (node = hashtable->nodes[i]; node; node = node->next) - (* func) (node->key, node->value, user_data); + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + (* func)(node->key, node->value, user_data); + } + } } /** @@ -1096,20 +1174,27 @@ fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, * Since: 2.4 **/ void * -fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, - void *user_data) +fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data) { - fluid_hashnode_t *node; - int i; + fluid_hashnode_t *node; + int i; - fluid_return_val_if_fail (hashtable != NULL, NULL); - fluid_return_val_if_fail (predicate != NULL, NULL); + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(predicate != NULL, NULL); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + if(predicate(node->key, node->value, user_data)) + { + return node->value; + } + } + } - for (i = 0; i < hashtable->size; i++) - for (node = hashtable->nodes[i]; node; node = node->next) - if (predicate (node->key, node->value, user_data)) - return node->value; - return NULL; + return NULL; } /** @@ -1121,11 +1206,11 @@ fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, * Return value: the number of key/value pairs in the #fluid_hashtable_t. **/ unsigned int -fluid_hashtable_size (fluid_hashtable_t *hashtable) +fluid_hashtable_size(fluid_hashtable_t *hashtable) { - fluid_return_val_if_fail (hashtable != NULL, 0); + fluid_return_val_if_fail(hashtable != NULL, 0); - return hashtable->nnodes; + return hashtable->nnodes; } /** @@ -1143,20 +1228,25 @@ fluid_hashtable_size (fluid_hashtable_t *hashtable) * Since: 2.14 */ fluid_list_t * -fluid_hashtable_get_keys (fluid_hashtable_t *hashtable) +fluid_hashtable_get_keys(fluid_hashtable_t *hashtable) { - fluid_hashnode_t *node; - int i; - fluid_list_t *retval; + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; + + fluid_return_val_if_fail(hashtable != NULL, NULL); - fluid_return_val_if_fail (hashtable != NULL, NULL); + retval = NULL; - retval = NULL; - for (i = 0; i < hashtable->size; i++) - for (node = hashtable->nodes[i]; node; node = node->next) - retval = fluid_list_prepend (retval, node->key); + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->key); + } + } - return retval; + return retval; } /** @@ -1174,20 +1264,25 @@ fluid_hashtable_get_keys (fluid_hashtable_t *hashtable) * Since: 2.14 */ fluid_list_t * -fluid_hashtable_get_values (fluid_hashtable_t *hashtable) +fluid_hashtable_get_values(fluid_hashtable_t *hashtable) { - fluid_hashnode_t *node; - int i; - fluid_list_t *retval; + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; - fluid_return_val_if_fail (hashtable != NULL, NULL); + fluid_return_val_if_fail(hashtable != NULL, NULL); - retval = NULL; - for (i = 0; i < hashtable->size; i++) - for (node = hashtable->nodes[i]; node; node = node->next) - retval = fluid_list_prepend (retval, node->value); + retval = NULL; - return retval; + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->value); + } + } + + return retval; } @@ -1198,20 +1293,20 @@ fluid_hashtable_get_values (fluid_hashtable_t *hashtable) * fluid_str_equal: * @v1: a key * @v2: a key to compare with @v1 - * - * Compares two strings for byte-by-byte equality and returns %TRUE - * if they are equal. It can be passed to new_fluid_hashtable() as the + * + * Compares two strings for byte-by-byte equality and returns %TRUE + * if they are equal. It can be passed to new_fluid_hashtable() as the * @key_equal_func parameter, when using strings as keys in a #Ghashtable. * * Returns: %TRUE if the two keys match */ int -fluid_str_equal (const void *v1, const void *v2) +fluid_str_equal(const void *v1, const void *v2) { - const char *string1 = v1; - const char *string2 = v2; - - return strcmp (string1, string2) == 0; + const char *string1 = v1; + const char *string2 = v2; + + return FLUID_STRCMP(string1, string2) == 0; } /** @@ -1219,23 +1314,27 @@ fluid_str_equal (const void *v1, const void *v2) * @v: a string key * * Converts a string to a hash value. - * It can be passed to new_fluid_hashtable() as the @hash_func + * It can be passed to new_fluid_hashtable() as the @hash_func * parameter, when using strings as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key */ unsigned int -fluid_str_hash (const void *v) +fluid_str_hash(const void *v) { - /* 31 bit hash function */ - const signed char *p = v; - uint32 h = *p; + /* 31 bit hash function */ + const signed char *p = v; + uint32_t h = *p; - if (h) - for (p += 1; *p != '\0'; p++) - h = (h << 5) - h + *p; + if(h) + { + for(p += 1; *p != '\0'; p++) + { + h = (h << 5) - h + *p; + } + } - return h; + return h; } @@ -1250,13 +1349,13 @@ fluid_str_hash (const void *v) * Compares two #gpointer arguments and returns %TRUE if they are equal. * It can be passed to new_fluid_hashtable() as the @key_equal_func * parameter, when using pointers as keys in a #fluid_hashtable_t. - * + * * Returns: %TRUE if the two keys match. */ int -fluid_direct_equal (const void *v1, const void *v2) +fluid_direct_equal(const void *v1, const void *v2) { - return v1 == v2; + return v1 == v2; } /** @@ -1264,15 +1363,15 @@ fluid_direct_equal (const void *v1, const void *v2) * @v: a void * key * * Converts a gpointer to a hash value. - * It can be passed to g_hashtable_new() as the @hash_func parameter, + * It can be passed to g_hashtable_new() as the @hash_func parameter, * when using pointers as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key. */ unsigned int -fluid_direct_hash (const void *v) +fluid_direct_hash(const void *v) { - return FLUID_POINTER_TO_UINT (v); + return FLUID_POINTER_TO_UINT(v); } /** @@ -1280,17 +1379,17 @@ fluid_direct_hash (const void *v) * @v1: a pointer to a int key. * @v2: a pointer to a int key to compare with @v1. * - * Compares the two #gint values being pointed to and returns + * Compares the two #gint values being pointed to and returns * %TRUE if they are equal. * It can be passed to g_hashtable_new() as the @key_equal_func * parameter, when using pointers to integers as keys in a #fluid_hashtable_t. - * + * * Returns: %TRUE if the two keys match. */ int -fluid_int_equal (const void *v1, const void *v2) +fluid_int_equal(const void *v1, const void *v2) { - return *((const int*) v1) == *((const int*) v2); + return *((const int *) v1) == *((const int *) v2); } /** @@ -1298,13 +1397,13 @@ fluid_int_equal (const void *v1, const void *v2) * @v: a pointer to a int key * * Converts a pointer to a #gint to a hash value. - * It can be passed to g_hashtable_new() as the @hash_func parameter, + * It can be passed to g_hashtable_new() as the @hash_func parameter, * when using pointers to integers values as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key. */ unsigned int -fluid_int_hash (const void *v) +fluid_int_hash(const void *v) { - return *(const int*) v; + return *(const int *) v; } diff --git a/libs/fluidsynth/src/fluid_hash.h b/libs/fluidsynth/src/fluid_hash.h index 3beff0607e..96b2471be5 100644 --- a/libs/fluidsynth/src/fluid_hash.h +++ b/libs/fluidsynth/src/fluid_hash.h @@ -27,7 +27,7 @@ /* * Adapted for FluidSynth use by Josh Green * September 8, 2009 from glib 2.18.4 - * + * * - Self contained (no dependencies on glib) * - changed names to fluid_hashtable_... */ @@ -52,80 +52,80 @@ typedef struct _fluid_hashnode_t fluid_hashnode_t; struct _fluid_hashnode_t { - void *key; - void *value; - fluid_hashnode_t *next; - unsigned int key_hash; + void *key; + void *value; + fluid_hashnode_t *next; + unsigned int key_hash; }; struct _fluid_hashtable_t { - int size; - int nnodes; - fluid_hashnode_t **nodes; - fluid_hash_func_t hash_func; - fluid_equal_func_t key_equal_func; - volatile int ref_count; - fluid_destroy_notify_t key_destroy_func; - fluid_destroy_notify_t value_destroy_func; - fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) + int size; + int nnodes; + fluid_hashnode_t **nodes; + fluid_hash_func_t hash_func; + fluid_equal_func_t key_equal_func; + fluid_atomic_int_t ref_count; + fluid_destroy_notify_t key_destroy_func; + fluid_destroy_notify_t value_destroy_func; + fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) }; struct _fluid_hashtable_iter_t { - /*< private >*/ - void * dummy1; - void * dummy2; - void * dummy3; - int dummy4; - int dummy5; // Bool - void * dummy6; + /*< private >*/ + void *dummy1; + void *dummy2; + void *dummy3; + int dummy4; + int dummy5; // Bool + void *dummy6; }; -fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func, - fluid_equal_func_t key_equal_func); -fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func, - fluid_equal_func_t key_equal_func, - fluid_destroy_notify_t key_destroy_func, - fluid_destroy_notify_t value_destroy_func); +fluid_hashtable_t *new_fluid_hashtable(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func); +fluid_hashtable_t *new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func); void delete_fluid_hashtable(fluid_hashtable_t *hashtable); -void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); -int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value); -fluid_hashtable_t *fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter); -void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter); -void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter); - -fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable); -void fluid_hashtable_unref (fluid_hashtable_t *hashtable); - -void *fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key); -int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key, - void **orig_key, void **value); - -void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value); -void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value); - -int fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key); -int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key); -void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable); -void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable); -unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, - fluid_hr_func_t func, void *user_data); -void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, - void *user_data); -void *fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, +void fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); +int fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, void **value); +fluid_hashtable_t *fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter); + +fluid_hashtable_t *fluid_hashtable_ref(fluid_hashtable_t *hashtable); +void fluid_hashtable_unref(fluid_hashtable_t *hashtable); + +void *fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, const void *lookup_key, + void **orig_key, void **value); + +void fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value); +void fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value); + +int fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key); +void fluid_hashtable_remove_all(fluid_hashtable_t *hashtable); +void fluid_hashtable_steal_all(fluid_hashtable_t *hashtable); +unsigned int fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data); +void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data); -unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable); -fluid_list_t *fluid_hashtable_get_keys (fluid_hashtable_t *hashtable); -fluid_list_t *fluid_hashtable_get_values (fluid_hashtable_t *hashtable); - -int fluid_str_equal (const void *v1, const void *v2); -unsigned int fluid_str_hash (const void *v); -int fluid_direct_equal (const void *v1, const void *v2); -unsigned int fluid_direct_hash (const void *v); -int fluid_int_equal (const void *v1, const void *v2); -unsigned int fluid_int_hash (const void *v); +void *fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data); +unsigned int fluid_hashtable_size(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_keys(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_values(fluid_hashtable_t *hashtable); + +int fluid_str_equal(const void *v1, const void *v2); +unsigned int fluid_str_hash(const void *v); +int fluid_direct_equal(const void *v1, const void *v2); +unsigned int fluid_direct_hash(const void *v); +int fluid_int_equal(const void *v1, const void *v2); +unsigned int fluid_int_hash(const void *v); #endif /* _FLUID_HASH_H */ diff --git a/libs/fluidsynth/src/fluid_iir_filter.c b/libs/fluidsynth/src/fluid_iir_filter.c index 5b474692ae..0770ef62b6 100644 --- a/libs/fluidsynth/src/fluid_iir_filter.c +++ b/libs/fluidsynth/src/fluid_iir_filter.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -23,7 +23,12 @@ #include "fluid_conv.h" /** - * Applies a lowpass filter with variable cutoff frequency and quality factor. + * Applies a low- or high-pass filter with variable cutoff frequency and quality factor + * for a given biquad transfer function: + * b0 + b1*z^-1 + b2*z^-2 + * H(z) = ------------------------ + * a0 + a1*z^-1 + a2*z^-2 + * * Also modifies filter state accordingly. * @param iir_filter Filter parameter * @param dsp_buf Pointer to the synthesized audio data @@ -31,271 +36,384 @@ */ /* * Variable description: - * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients + * - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal + * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal + * - coefficients normalized to a0 * * 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 */ -void -fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter, +void +fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, fluid_real_t *dsp_buf, int count) { - /* IIR filter sample history */ - fluid_real_t dsp_hist1 = iir_filter->hist1; - fluid_real_t dsp_hist2 = iir_filter->hist2; - - /* IIR filter coefficients */ - fluid_real_t dsp_a1 = iir_filter->a1; - fluid_real_t dsp_a2 = iir_filter->a2; - fluid_real_t dsp_b02 = iir_filter->b02; - fluid_real_t dsp_b1 = iir_filter->b1; - int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; + if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0) + { + return; + } + else + { + /* IIR filter sample history */ + fluid_real_t dsp_hist1 = iir_filter->hist1; + fluid_real_t dsp_hist2 = iir_filter->hist2; + + /* IIR filter coefficients */ + fluid_real_t dsp_a1 = iir_filter->a1; + fluid_real_t dsp_a2 = iir_filter->a2; + fluid_real_t dsp_b02 = iir_filter->b02; + fluid_real_t dsp_b1 = iir_filter->b1; + int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; + + fluid_real_t dsp_centernode; + int dsp_i; + + /* filter (implement the voice filter according to SoundFont standard) */ + + /* Check for denormal number (too close to zero). */ + if(fabs(dsp_hist1) < 1e-20) + { + dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ + } - fluid_real_t dsp_centernode; - int dsp_i; + /* 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) + { + fluid_real_t dsp_a1_incr = iir_filter->a1_incr; + fluid_real_t dsp_a2_incr = iir_filter->a2_incr; + fluid_real_t dsp_b02_incr = iir_filter->b02_incr; + fluid_real_t dsp_b1_incr = iir_filter->b1_incr; + + + /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ + for(dsp_i = 0; dsp_i < count; 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) + { + fluid_real_t old_b02 = dsp_b02; + dsp_a1 += dsp_a1_incr; + dsp_a2 += dsp_a2_incr; + dsp_b02 += dsp_b02_incr; + dsp_b1 += dsp_b1_incr; + + /* Compensate history to avoid the filter going havoc with large frequency changes */ + if(iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) + { + fluid_real_t compensate = old_b02 / dsp_b02; + dsp_hist1 *= compensate; + dsp_hist2 *= compensate; + } + } + } /* for dsp_i */ + } + else /* The filter parameters are constant. This is duplicated to save time. */ + { + for(dsp_i = 0; dsp_i < count; 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; + } + } - /* filter (implement the voice filter according to SoundFont standard) */ + iir_filter->hist1 = dsp_hist1; + iir_filter->hist2 = dsp_hist2; + iir_filter->a1 = dsp_a1; + iir_filter->a2 = dsp_a2; + iir_filter->b02 = dsp_b02; + iir_filter->b1 = dsp_b1; + iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; - /* Check for denormal number (too close to zero). */ - if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ + fluid_check_fpe("voice_filter"); + } +} - /* 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) - { - fluid_real_t dsp_a1_incr = iir_filter->a1_incr; - fluid_real_t dsp_a2_incr = iir_filter->a2_incr; - fluid_real_t dsp_b02_incr = iir_filter->b02_incr; - fluid_real_t dsp_b1_incr = iir_filter->b1_incr; +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init) +{ + fluid_iir_filter_t *iir_filter = obj; + enum fluid_iir_filter_type type = param[0].i; + enum fluid_iir_filter_flags flags = param[1].i; + iir_filter->type = type; + iir_filter->flags = flags; - /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ - for (dsp_i = 0; dsp_i < count; dsp_i++) + if(type != FLUID_IIR_DISABLED) { - /* 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) - { - fluid_real_t old_b02 = dsp_b02; - dsp_a1 += dsp_a1_incr; - dsp_a2 += dsp_a2_incr; - dsp_b02 += dsp_b02_incr; - dsp_b1 += dsp_b1_incr; - - /* Compensate history to avoid the filter going havoc with large frequency changes */ - if (iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) { - fluid_real_t compensate = old_b02 / dsp_b02; - dsp_centernode *= compensate; - dsp_hist1 *= compensate; - dsp_hist2 *= compensate; - } - } - } /* for dsp_i */ - } - else /* The filter parameters are constant. This is duplicated to save time. */ - { - for (dsp_i = 0; dsp_i < count; 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; + fluid_iir_filter_reset(iir_filter); } - } - - iir_filter->hist1 = dsp_hist1; - iir_filter->hist2 = dsp_hist2; - iir_filter->a1 = dsp_a1; - iir_filter->a2 = dsp_a2; - iir_filter->b02 = dsp_b02; - iir_filter->b1 = dsp_b1; - iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; - - fluid_check_fpe ("voice_filter"); } - -void -fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter) +void +fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter) { - iir_filter->hist1 = 0; - iir_filter->hist2 = 0; - iir_filter->last_fres = -1.; - iir_filter->filter_startup = 1; + iir_filter->hist1 = 0; + iir_filter->hist2 = 0; + iir_filter->last_fres = -1.; + iir_filter->q_lin = 0; + iir_filter->filter_startup = 1; } -void -fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter, - fluid_real_t fres) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres) { - iir_filter->fres = fres; - iir_filter->last_fres = -1.; -} + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t fres = param[0].real; + iir_filter->fres = fres; + iir_filter->last_fres = -1.; +} -void -fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter, - fluid_real_t q_dB) +static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB) { + /* The generator contains 'centibels' (1/10 dB) => divide by 10 to + * obtain dB */ + q_dB /= 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. */ - iir_filter->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f)); + return 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. - */ - iir_filter->filter_gain = (fluid_real_t) (1.0 / sqrt(iir_filter->q_lin)); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q) +{ + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t q = param[0].real; + int flags = iir_filter->flags; + + if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0) + { + q = 0; + } + else if(flags & FLUID_IIR_Q_LINEAR) + { + /* q is linear (only for user-defined filter) + * increase to avoid Q being somewhere between zero and one, + * which results in some strange amplified lowpass signal + */ + q++; + } + else + { + q = fluid_iir_filter_q_from_dB(q); + } + + iir_filter->q_lin = q; + iir_filter->filter_gain = 1.0; + + if(!(flags & FLUID_IIR_NO_GAIN_AMP)) + { + /* 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. + */ + iir_filter->filter_gain /= sqrt(q); + } /* The synthesis loop will have to recalculate the filter coefficients. */ iir_filter->last_fres = -1.; - } - -static inline void -fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter, - int transition_samples, +static FLUID_INLINE void +fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter, + int transition_samples, fluid_real_t output_rate) { + /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */ + if(iir_filter->q_lin == 0) + { + return; + } + else + { + /* + * 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 * + (iir_filter->last_fres / ((float) output_rate))); + 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 * iir_filter->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: + * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; + * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; + * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */ + + /* "a" coeffs are same for all 3 available filter types */ + 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 b02_temp, b1_temp; + + switch(iir_filter->type) + { + case FLUID_IIR_HIGHPASS: + b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + + b1_temp *= -1.0f; + break; + + case FLUID_IIR_LOWPASS: + b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + break; + + default: + /* filter disabled, should never get here */ + return; + } - /* - * 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 * - (iir_filter->last_fres / ((float) output_rate))); - 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 * iir_filter->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: - * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; - * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; - * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->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 * iir_filter->filter_gain; - /* both b0 -and- b2 */ - fluid_real_t b02_temp = b1_temp * 0.5f; - - iir_filter->compensate_incr = 0; - - if (iir_filter->filter_startup || (transition_samples == 0)) - { - /* The filter is calculated, because the voice was started up. - * In this case set the filter coefficients without delay. - */ - iir_filter->a1 = a1_temp; - iir_filter->a2 = a2_temp; - iir_filter->b02 = b02_temp; - iir_filter->b1 = b1_temp; - iir_filter->filter_coeff_incr_count = 0; - iir_filter->filter_startup = 0; + iir_filter->compensate_incr = 0; + + if(iir_filter->filter_startup || (transition_samples == 0)) + { + /* The filter is calculated, because the voice was started up. + * In this case set the filter coefficients without delay. + */ + iir_filter->a1 = a1_temp; + iir_filter->a2 = a2_temp; + iir_filter->b02 = b02_temp; + iir_filter->b1 = b1_temp; + iir_filter->filter_coeff_incr_count = 0; + iir_filter->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. - */ - - iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; - iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; - iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; - iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; - if (fabs(iir_filter->b02) > 0.0001) { - fluid_real_t quota = b02_temp / iir_filter->b02; - iir_filter->compensate_incr = quota < 0.5 || quota > 2; + } + 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. + */ + + iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; + iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; + iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; + iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; + + if(fabs(iir_filter->b02) > 0.0001) + { + fluid_real_t quota = b02_temp / iir_filter->b02; + iir_filter->compensate_incr = quota < 0.5 || quota > 2; + } + + /* Have to add the increments filter_coeff_incr_count times. */ + iir_filter->filter_coeff_incr_count = transition_samples; + } + + fluid_check_fpe("voice_write filter calculation"); } - /* Have to add the increments filter_coeff_incr_count times. */ - iir_filter->filter_coeff_incr_count = transition_samples; - } - fluid_check_fpe ("voice_write filter calculation"); } -void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter, - fluid_real_t output_rate, +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, fluid_real_t fres_mod) { - fluid_real_t fres; - - /* calculate the frequency of the resonant filter in Hz */ - fres = fluid_ct2hz(iir_filter->fres + fres_mod); - - /* FIXME - Still potential for a click during turn on, can we interpolate - between 20khz cutoff and 0 Q? */ - - /* I removed the optimization of turning the filter off when the - * resonance frequence is above the maximum frequency. Instead, the - * filter frequency is set to a maximum of 0.45 times the sampling - * rate. For a 44100 kHz sampling rate, this amounts to 19845 - * Hz. The reason is that there 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 * output_rate) - fres = 0.45f * output_rate; - else if (fres < 5) - fres = 5; - - /* if filter enabled and there is a significant frequency change.. */ - if ((abs (fres - iir_filter->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. */ - iir_filter->last_fres = fres; - fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, - output_rate); - } - - - fluid_check_fpe ("voice_write DSP coefficients"); + fluid_real_t fres; + + /* calculate the frequency of the resonant filter in Hz */ + fres = fluid_ct2hz(iir_filter->fres + fres_mod); + + /* FIXME - Still potential for a click during turn on, can we interpolate + between 20khz cutoff and 0 Q? */ + + /* I removed the optimization of turning the filter off when the + * resonance frequence is above the maximum frequency. Instead, the + * filter frequency is set to a maximum of 0.45 times the sampling + * rate. For a 44100 kHz sampling rate, this amounts to 19845 + * Hz. The reason is that there 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 * output_rate) + { + fres = 0.45f * output_rate; + } + else if(fres < 5) + { + fres = 5; + } + + /* if filter enabled and there is a significant frequency change.. */ + if(iir_filter->type != FLUID_IIR_DISABLED && fabs(fres - iir_filter->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. */ + iir_filter->last_fres = fres; + fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, + output_rate); + } + + + fluid_check_fpe("voice_write DSP coefficients"); } diff --git a/libs/fluidsynth/src/fluid_iir_filter.h b/libs/fluidsynth/src/fluid_iir_filter.h index 7dc5de14a5..355d197f1c 100644 --- a/libs/fluidsynth/src/fluid_iir_filter.h +++ b/libs/fluidsynth/src/fluid_iir_filter.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -25,50 +25,50 @@ typedef struct _fluid_iir_filter_t fluid_iir_filter_t; +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q); -void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter, - fluid_real_t *dsp_buf, int dsp_buf_count); +void fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, + fluid_real_t *dsp_buf, int dsp_buf_count); -void fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter); +void fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter); -void fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter, - fluid_real_t q_dB); - -void fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter, - fluid_real_t fres); - -void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter, - fluid_real_t output_rate, - fluid_real_t fres_mod); +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, + fluid_real_t fres_mod); /* We can't do information hiding here, as fluid_voice_t includes the struct without a pointer. */ struct _fluid_iir_filter_t { - /* 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 */ + enum fluid_iir_filter_type type; /* specifies the type of this filter */ + enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */ + + /* 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; - int compensate_incr; /* Flag: If set, must compensate history */ - fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ - int filter_startup; /* Flag: If set, the filter will be set directly. + 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; + int compensate_incr; /* Flag: If set, must compensate history */ + 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. */ - 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 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 */ }; #endif diff --git a/libs/fluidsynth/src/fluid_lfo.c b/libs/fluidsynth/src/fluid_lfo.c index ff178e0012..ae21cdd0f7 100644 --- a/libs/fluidsynth/src/fluid_lfo.c +++ b/libs/fluidsynth/src/fluid_lfo.c @@ -1,13 +1,17 @@ #include "fluid_lfo.h" -void -fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr) { - lfo->increment = increment; + fluid_lfo_t *lfo = obj; + fluid_real_t increment = param[0].real; + + lfo->increment = increment; } -void -fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay) { - lfo->delay = delay; + fluid_lfo_t *lfo = obj; + unsigned int delay = param[0].i; + + lfo->delay = delay; } diff --git a/libs/fluidsynth/src/fluid_lfo.h b/libs/fluidsynth/src/fluid_lfo.h index e9440cf526..b9a9ca6eaa 100644 --- a/libs/fluidsynth/src/fluid_lfo.h +++ b/libs/fluidsynth/src/fluid_lfo.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -21,50 +21,53 @@ #ifndef _FLUID_LFO_H #define _FLUID_LFO_H -#include "fluidsynth_priv.h" +#include "fluid_sys.h" typedef struct _fluid_lfo_t fluid_lfo_t; -struct _fluid_lfo_t { - fluid_real_t val; /* the current value of the LFO */ - unsigned int delay; /* the delay of the lfo in samples */ - fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */ +struct _fluid_lfo_t +{ + fluid_real_t val; /* the current value of the LFO */ + unsigned int delay; /* the delay of the lfo in samples */ + fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */ }; -static inline void -fluid_lfo_reset(fluid_lfo_t* lfo) +static FLUID_INLINE void +fluid_lfo_reset(fluid_lfo_t *lfo) { - lfo->val = 0.0f; + lfo->val = 0.0f; } // These two cannot be inlined since they're used by event_dispatch -void fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment); -void fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay); -static inline fluid_real_t -fluid_lfo_get_val(fluid_lfo_t* lfo) +static FLUID_INLINE fluid_real_t +fluid_lfo_get_val(fluid_lfo_t *lfo) { - return lfo->val; + return lfo->val; } -static inline void -fluid_lfo_calc(fluid_lfo_t* lfo, unsigned int cur_delay) +static FLUID_INLINE void +fluid_lfo_calc(fluid_lfo_t *lfo, unsigned int cur_delay) { - if (cur_delay < lfo->delay) - return; - - lfo->val += lfo->increment; - - if (lfo->val > (fluid_real_t) 1.0) - { - lfo->increment = -lfo->increment; - lfo->val = (fluid_real_t) 2.0 - lfo->val; - } - else if (lfo->val < (fluid_real_t) -1.0) - { - lfo->increment = -lfo->increment; - lfo->val = (fluid_real_t) -2.0 - lfo->val; - } + if(cur_delay < lfo->delay) + { + return; + } + + lfo->val += lfo->increment; + + if(lfo->val > (fluid_real_t) 1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) 2.0 - lfo->val; + } + else if(lfo->val < (fluid_real_t) -1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) -2.0 - lfo->val; + } } diff --git a/libs/fluidsynth/src/fluid_list.c b/libs/fluidsynth/src/fluid_list.c index dd47a39e0e..c92d731f89 100644 --- a/libs/fluidsynth/src/fluid_list.c +++ b/libs/fluidsynth/src/fluid_list.c @@ -2,16 +2,16 @@ * 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 + * 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 - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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 02110-1301, USA. @@ -26,243 +26,296 @@ +#include "fluid_sys.h" #include "fluid_list.h" -fluid_list_t* +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; + 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; - } + fluid_list_t *next; + fluid_return_if_fail(list != NULL); + + 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 * +fluid_list_append(fluid_list_t *list, void *data) { - fluid_list_t *new_list; - fluid_list_t *last; + fluid_list_t *new_list; + fluid_list_t *last; - new_list = new_fluid_list(); - new_list->data = data; + new_list = new_fluid_list(); + new_list->data = data; - if (list) + if(list) { - last = fluid_list_last(list); - /* g_assert (last != NULL); */ - last->next = new_list; + last = fluid_list_last(list); + /* g_assert (last != NULL); */ + last->next = new_list; - return list; + return list; + } + else + { + return new_list; } - else - return new_list; } -fluid_list_t* -fluid_list_prepend(fluid_list_t *list, void* data) +fluid_list_t * +fluid_list_prepend(fluid_list_t *list, void *data) { - fluid_list_t *new_list; + fluid_list_t *new_list; - new_list = new_fluid_list(); - new_list->data = data; - new_list->next = list; + new_list = new_fluid_list(); + new_list->data = data; + new_list->next = list; - return new_list; + return new_list; } -fluid_list_t* +fluid_list_t * fluid_list_nth(fluid_list_t *list, int n) { - while ((n-- > 0) && list) { - list = list->next; - } + while((n-- > 0) && list) + { + list = list->next; + } - return list; + return list; } -fluid_list_t* -fluid_list_remove(fluid_list_t *list, void* data) +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; - } + fluid_list_t *tmp; + fluid_list_t *prev; - prev = tmp; - tmp = tmp->next; - } + prev = NULL; + tmp = list; - return 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_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; - } + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; - prev = tmp; - tmp = tmp->next; - } + 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; + return list; } -static fluid_list_t* +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; + fluid_list_t list, *l; - l = &list; + 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; + 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; + l->next = l1 ? l1 : l2; + + return list.next; } -fluid_list_t* +fluid_list_t * fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) { - fluid_list_t *l1, *l2; + 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); + 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_t * fluid_list_last(fluid_list_t *list) { - if (list) { - while (list->next) - list = list->next; - } + if(list) + { + while(list->next) + { + list = list->next; + } + } - return list; + return list; } int fluid_list_size(fluid_list_t *list) { - int n = 0; - while (list) { - n++; - list = list->next; - } - return n; + 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 *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; + fluid_list_t *new_list; + fluid_list_t *cur; + fluid_list_t *prev = NULL; - new_list = new_fluid_list(); - new_list->data = data; + new_list = new_fluid_list(); + new_list->data = data; - cur = list; - while ((n-- > 0) && cur) { - prev = cur; - cur = cur->next; - } + cur = list; - new_list->next = cur; + while((n-- > 0) && cur) + { + prev = cur; + cur = cur->next; + } - if (prev) { - prev->next = new_list; - return list; - } else { - return new_list; - } + new_list->next = cur; + + if(prev) + { + prev->next = new_list; + return list; + } + else + { + return new_list; + } } /* Compare function to sort strings alphabetically, * for use with fluid_list_sort(). */ int -fluid_list_str_compare_func (void *a, void *b) +fluid_list_str_compare_func(void *a, void *b) { - if (a && b) return FLUID_STRCMP ((char *)a, (char *)b); - if (!a && !b) return 0; - if (a) return -1; - return 1; + if(a && b) + { + return FLUID_STRCMP((char *)a, (char *)b); + } + + if(!a && !b) + { + return 0; + } + + if(a) + { + return -1; + } + + return 1; } diff --git a/libs/fluidsynth/src/fluid_list.h b/libs/fluidsynth/src/fluid_list.h index bdc32918b8..5be1cc3923 100644 --- a/libs/fluidsynth/src/fluid_list.h +++ b/libs/fluidsynth/src/fluid_list.h @@ -2,16 +2,16 @@ * 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 + * 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 - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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 02110-1301, USA. @@ -33,30 +33,30 @@ typedef struct _fluid_list_t fluid_list_t; -typedef int (*fluid_compare_func_t)(void* a, void* b); +typedef int (*fluid_compare_func_t)(void *a, void *b); struct _fluid_list_t { - void* data; - fluid_list_t *next; + void *data; + fluid_list_t *next; }; -fluid_list_t* new_fluid_list(void); +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); +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) -int fluid_list_str_compare_func (void *a, void *b); +int fluid_list_str_compare_func(void *a, void *b); #endif /* _FLUID_LIST_H */ diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c index fc58d839ef..bdf72dd681 100644 --- a/libs/fluidsynth/src/fluid_midi.c +++ b/libs/fluidsynth/src/fluid_midi.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -25,15 +25,56 @@ static int fluid_midi_event_length(unsigned char event); +static int fluid_isasciistring(char *s); +static long fluid_getlength(unsigned char *s); + /* Read the entire contents of a file into memory, allocating enough memory * for the file, and returning the length and the buffer. * Note: This rewinds the file to the start before reading. * Returns NULL if there was an error reading or allocating memory. */ -static char* fluid_file_read_full(fluid_file fp, size_t* length); +static char *fluid_file_read_full(fluid_file fp, size_t *length); +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); #define READ_FULL_INITIAL_BUFLEN 1024 +static fluid_track_t *new_fluid_track(int num); +static void delete_fluid_track(fluid_track_t *track); +static int fluid_track_set_name(fluid_track_t *track, char *name); +static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt); +static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); +static int fluid_track_get_duration(fluid_track_t *track); +static int fluid_track_reset(fluid_track_t *track); + +static int fluid_track_send_events(fluid_track_t *track, + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks); + + +static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); +static int fluid_player_callback(void *data, unsigned int msec); +static int fluid_player_reset(fluid_player_t *player); +static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); +static void fluid_player_advancefile(fluid_player_t *player); +static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); + +static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); +static void delete_fluid_midi_file(fluid_midi_file *mf); +static int fluid_midi_file_read_mthd(fluid_midi_file *midifile); +static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player); +static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num); +static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track); +static int fluid_midi_file_read_varlen(fluid_midi_file *mf); +static int fluid_midi_file_getc(fluid_midi_file *mf); +static int fluid_midi_file_push(fluid_midi_file *mf, int c); +static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len); +static int fluid_midi_file_skip(fluid_midi_file *mf, int len); +static int fluid_midi_file_eof(fluid_midi_file *mf); +static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); +static int fluid_midi_file_eot(fluid_midi_file *mf); +static int fluid_midi_file_get_division(fluid_midi_file *midifile); + #if 0 // disable file I/O with Ardour /*************************************************************** * @@ -49,15 +90,18 @@ static char* fluid_file_read_full(fluid_file fp, size_t* length); * @return New MIDI file handle or NULL on error. */ fluid_midi_file * -new_fluid_midi_file(const char* buffer, size_t length) +new_fluid_midi_file(const char *buffer, size_t length) { fluid_midi_file *mf; mf = FLUID_NEW(fluid_midi_file); - if (mf == NULL) { + + if(mf == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); mf->c = -1; @@ -68,45 +112,58 @@ new_fluid_midi_file(const char* buffer, size_t length) mf->buf_pos = 0; mf->eof = FALSE; - if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { + if(fluid_midi_file_read_mthd(mf) != FLUID_OK) + { FLUID_FREE(mf); return NULL; } + return mf; } -static char* -fluid_file_read_full(fluid_file fp, size_t* length) +static char * +fluid_file_read_full(fluid_file fp, size_t *length) { size_t buflen; - char* buffer; + char *buffer; size_t n; + /* Work out the length of the file in advance */ - if (FLUID_FSEEK(fp, 0, SEEK_END) != 0) + if(FLUID_FSEEK(fp, 0, SEEK_END) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } + buflen = ftell(fp); - if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0) + + if(FLUID_FSEEK(fp, 0, SEEK_SET) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } + FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen); buffer = FLUID_MALLOC(buflen); - if (buffer == NULL) { + + if(buffer == NULL) + { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } + n = FLUID_FREAD(buffer, 1, buflen, fp); - if (n != buflen) { + + if(n != buflen) + { FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n, buflen); FLUID_FREE(buffer); return NULL; }; + *length = n; + return buffer; } @@ -116,35 +173,40 @@ fluid_file_read_full(fluid_file fp, size_t* length) * @param mf MIDI file handle to close and free. */ void -delete_fluid_midi_file (fluid_midi_file *mf) +delete_fluid_midi_file(fluid_midi_file *mf) { - if (mf == NULL) { - return; - } + fluid_return_if_fail(mf != NULL); + FLUID_FREE(mf); - return; } /* * Gets the next byte in a MIDI file, taking into account previous running status. * - * returns FLUID_FAILED if EOF or read error + * returns -1 if EOF or read error */ int -fluid_midi_file_getc (fluid_midi_file *mf) +fluid_midi_file_getc(fluid_midi_file *mf) { unsigned char c; - if (mf->c >= 0) { + + if(mf->c >= 0) + { c = mf->c; mf->c = -1; - } else { - if (mf->buf_pos >= mf->buf_len) { + } + else + { + if(mf->buf_pos >= mf->buf_len) + { mf->eof = TRUE; - return FLUID_FAILED; + return -1; } + c = mf->buffer[mf->buf_pos++]; mf->trackpos++; } + return (int) c; } @@ -166,23 +228,35 @@ int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) { int num = len < mf->buf_len - mf->buf_pos - ? len : mf->buf_len - mf->buf_pos; - if (num != len) { + ? len : mf->buf_len - mf->buf_pos; + + if(num != len) + { mf->eof = TRUE; } - if (num < 0) { + + if(num < 0) + { num = 0; } + /* Note: Read bytes, even if there aren't enough, but only increment * trackpos if successful (emulates old behaviour of fluid_midi_file_read) */ - FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num); + FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num); mf->buf_pos += num; - if (num == len) + + if(num == len) + { mf->trackpos += num; + } + #if DEBUG else + { FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes"); + } + #endif return (num != len) ? FLUID_FAILED : FLUID_OK; } @@ -191,15 +265,18 @@ fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) * fluid_midi_file_skip */ int -fluid_midi_file_skip (fluid_midi_file *mf, int skip) +fluid_midi_file_skip(fluid_midi_file *mf, int skip) { int new_pos = mf->buf_pos + skip; + /* Mimic the behaviour of fseek: Error to seek past the start of file, but * OK to seek past end (this just puts it into the EOF state). */ - if (new_pos < 0) { + if(new_pos < 0) + { FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); return FLUID_FAILED; } + /* Clear the EOF flag, even if moved past the end of the file (this is * consistent with the behaviour of fseek). */ mf->eof = FALSE; @@ -210,15 +287,15 @@ fluid_midi_file_skip (fluid_midi_file *mf, int skip) /* * fluid_midi_file_eof */ -int fluid_midi_file_eof(fluid_midi_file* mf) +int fluid_midi_file_eof(fluid_midi_file *mf) { - /* Note: This does not simply test whether the file read pointer is past - * the end of the file. It mimics the behaviour of feof by actually - * testing the stateful EOF condition, which is set to TRUE if getc or - * fread have attempted to read past the end (but not if they have - * precisely reached the end), but reset to FALSE upon a successful seek. - */ - return mf->eof; + /* Note: This does not simply test whether the file read pointer is past + * the end of the file. It mimics the behaviour of feof by actually + * testing the stateful EOF condition, which is set to TRUE if getc or + * fread have attempted to read past the end (but not if they have + * precisely reached the end), but reset to FALSE upon a successful seek. + */ + return mf->eof; } /* @@ -227,30 +304,40 @@ int fluid_midi_file_eof(fluid_midi_file* mf) int fluid_midi_file_read_mthd(fluid_midi_file *mf) { - char mthd[15]; - if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) { + char mthd[14]; + + if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK) + { return FLUID_FAILED; } - if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) - || (mthd[9] > 2)) { + + 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"); + "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->ntracks += (unsigned int)(mthd[10]) << 16; + + if((signed char)mthd[12] < 0) + { mf->uses_smpte = 1; - mf->smpte_fps = -mthd[12]; + mf->smpte_fps = -(signed char)mthd[12]; mf->smpte_res = (unsigned) mthd[13]; FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); return FLUID_FAILED; - } else { + } + else + { mf->uses_smpte = 0; - mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); + mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff); FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); } + return FLUID_OK; } @@ -261,11 +348,15 @@ 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) { + + for(i = 0; i < mf->ntracks; i++) + { + if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK) + { return FLUID_FAILED; } } + return FLUID_OK; } @@ -275,14 +366,23 @@ fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) int fluid_isasciistring(char *s) { + /* From ctype.h */ +#define fluid_isascii(c) (((c) & ~0x7f) == 0) + int i; int len = (int) FLUID_STRLEN(s); - for (i = 0; i < len; i++) { - if (!fluid_isascii(s[i])) { + + for(i = 0; i < len; i++) + { + if(!fluid_isascii(s[i])) + { return 0; } } + return 1; + +#undef fluid_isascii } /* @@ -303,9 +403,12 @@ int fluid_midi_file_read_tracklen(fluid_midi_file *mf) { unsigned char length[5]; - if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { return FLUID_FAILED; } + mf->tracklen = fluid_getlength(length); mf->trackpos = 0; mf->eot = 0; @@ -319,9 +422,12 @@ int fluid_midi_file_eot(fluid_midi_file *mf) { #if DEBUG - if (mf->trackpos > mf->tracklen) { + + if(mf->trackpos > mf->tracklen) + { printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); } + #endif return mf->eot || (mf->trackpos >= mf->tracklen); } @@ -337,69 +443,93 @@ fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) int found_track = 0; int skip; - if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) { + if(fluid_midi_file_read(mf, id, 4) != FLUID_OK) + { return FLUID_FAILED; } + id[4] = '\0'; mf->dtime = 0; - while (!found_track) { + while(!found_track) + { - if (fluid_isasciistring((char *) id) == 0) { + if(fluid_isasciistring((char *) id) == 0) + { FLUID_LOG(FLUID_ERR, - "An non-ascii track header found, corrupt file"); + "An non-ascii track header found, corrupt file"); return FLUID_FAILED; - } else if (strcmp((char *) id, "MTrk") == 0) { + } + else if(FLUID_STRCMP((char *) id, "MTrk") == 0) + { found_track = 1; - if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) { + if(fluid_midi_file_read_tracklen(mf) != FLUID_OK) + { return FLUID_FAILED; } track = new_fluid_track(num); - if (track == NULL) { + + 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) { + while(!fluid_midi_file_eot(mf)) + { + if(fluid_midi_file_read_event(mf, track) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } } /* Skip remaining track data, if any */ - if (mf->trackpos < mf->tracklen) { - if (fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) { + if(mf->trackpos < mf->tracklen) + { + if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } } - if (fluid_player_add_track(player, track) != FLUID_OK) { + if(fluid_player_add_track(player, track) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } - } else { + } + else + { found_track = 0; - if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + + 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) { + if(fluid_midi_file_skip(mf, skip) != FLUID_OK) + { return FLUID_FAILED; } } } - if (fluid_midi_file_eof(mf)) { + + if(fluid_midi_file_eof(mf)) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } + return FLUID_OK; } @@ -412,24 +542,35 @@ fluid_midi_file_read_varlen(fluid_midi_file *mf) int i; int c; mf->varlen = 0; - for (i = 0;; i++) { - if (i == 4) { + + 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) { + + if(c < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } - if (c & 0x80) { - mf->varlen |= (int) (c & 0x7F); + + if(c & 0x80) + { + mf->varlen |= (int)(c & 0x7F); mf->varlen <<= 7; - } else { + } + else + { mf->varlen += c; break; } } + return FLUID_OK; } @@ -453,24 +594,31 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) int size; /* read the delta-time of the event */ - if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } + mf->dtime += mf->varlen; /* read the status byte */ status = fluid_midi_file_getc(mf); - if (status < 0) { + + 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) { + 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; } @@ -479,40 +627,49 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) mf->running_status = status; - if ((status == MIDI_SYSEX)) { /* system exclusif */ + if(status == MIDI_SYSEX) /* system exclusif */ + { /* read the length of the message */ - if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } - if (mf->varlen) { + if(mf->varlen) + { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, - __LINE__, mf->varlen); + __LINE__, mf->varlen); metadata = FLUID_MALLOC(mf->varlen + 1); - if (metadata == NULL) { + if(metadata == NULL) + { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } /* read the data of the message */ - if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { - FLUID_FREE (metadata); + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + FLUID_FREE(metadata); return FLUID_FAILED; } evt = new_fluid_midi_event(); - if (evt == NULL) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE (metadata); + FLUID_FREE(metadata); return FLUID_FAILED; } evt->dtime = mf->dtime; size = mf->varlen; - if (metadata[mf->varlen - 1] == MIDI_EOX) + if(metadata[mf->varlen - 1] == MIDI_EOX) + { size--; + } /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */ fluid_midi_event_set_sysex(evt, metadata, size, TRUE); @@ -522,232 +679,318 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) return FLUID_OK; - } else if (status == MIDI_META_EVENT) { /* meta events */ + } + 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) { + + 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) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } - if (mf->varlen < 255) { + if(mf->varlen < 255) + { metadata = &static_buf[0]; - } else { + } + else + { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, - __LINE__, mf->varlen); + __LINE__, mf->varlen); dyn_buf = FLUID_MALLOC(mf->varlen + 1); - if (dyn_buf == NULL) { + + 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) { + 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) { + switch(type) + { - case MIDI_COPYRIGHT: - metadata[mf->varlen] = 0; - break; + 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_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_INST_NAME: + metadata[mf->varlen] = 0; + break; + + case MIDI_LYRIC: + case MIDI_TEXT: + { + void *tmp; + int size = mf->varlen + 1; + + /* NULL terminate strings for safety */ + metadata[size - 1] = '\0'; - case MIDI_LYRIC: + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; break; + } - case MIDI_MARKER: + evt->dtime = mf->dtime; + + tmp = FLUID_MALLOC(size); + + if(tmp == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + delete_fluid_midi_event(evt); + evt = NULL; + result = FLUID_FAILED; break; + } - case MIDI_CUE_POINT: - break; /* don't care much for text events */ + FLUID_MEMCPY(tmp, metadata, size); - case MIDI_EOT: - if (mf->varlen != 0) { - FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); - result = FLUID_FAILED; - break; - } - mf->eot = 1; - evt = new_fluid_midi_event(); - if (evt == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - result = FLUID_FAILED; - break; - } - evt->dtime = mf->dtime; - evt->type = MIDI_EOT; - fluid_track_add_event(track, evt); - mf->dtime = 0; + fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + 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; + } - 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 = mf->dtime; - evt->type = MIDI_SET_TEMPO; - evt->channel = 0; - evt->param1 = tempo; - evt->param2 = 0; - fluid_track_add_event(track, evt); - mf->dtime = 0; + mf->eot = 1; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; 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]; + evt->dtime = mf->dtime; + evt->type = MIDI_EOT; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SET_TEMPO: + if(mf->varlen != 3) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SetTempo meta event"); + result = FLUID_FAILED; + break; + } - FLUID_LOG(FLUID_DBG, - "signature=%d/%d, metronome=%d, 32nd-notes=%d", - nominator, denominator, clocks, notes); + 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; + } - case MIDI_KEY_SIGNATURE: - if (mf->varlen != 2) { - FLUID_LOG(FLUID_ERR, - "Invalid length for KeySignature meta event"); - result = FLUID_FAILED; - break; - } - /* We don't care about key signatures anyway */ - /* sf = metadata[0]; - mi = metadata[1]; */ + evt->dtime = mf->dtime; + evt->type = MIDI_SET_TEMPO; + evt->channel = 0; + evt->param1 = tempo; + evt->param2 = 0; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SMPTE_OFFSET: + if(mf->varlen != 5) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SMPTE Offset meta event"); + result = FLUID_FAILED; break; + } - case MIDI_SEQUENCER_EVENT: + 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); - default: + break; + + case MIDI_KEY_SIGNATURE: + if(mf->varlen != 2) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for KeySignature meta event"); + result = FLUID_FAILED; break; + } + + /* We don't care about key signatures anyway */ + /* sf = metadata[0]; + mi = metadata[1]; */ + break; + + case MIDI_SEQUENCER_EVENT: + break; + + default: + break; } - if (dyn_buf) { + if(dyn_buf) + { FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); FLUID_FREE(dyn_buf); } return result; - } else { /* channel messages */ + } + 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) { + if((param1 = fluid_midi_file_getc(mf)) < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } - switch (type) { + 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_ON: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case NOTE_OFF: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + 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 NOTE_OFF: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case CONTROL_CHANGE: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + break; - case PROGRAM_CHANGE: - break; + case KEY_PRESSURE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case CHANNEL_PRESSURE: - break; + break; - case PITCH_BEND: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } + case CONTROL_CHANGE: + 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; + break; + + case PROGRAM_CHANGE: + break; + + case CHANNEL_PRESSURE: + break; - default: - /* Can't possibly happen !? */ - FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + 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) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } + evt->dtime = mf->dtime; evt->type = type; evt->channel = channel; @@ -756,6 +999,7 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) fluid_track_add_event(track, evt); mf->dtime = 0; } + return FLUID_OK; } @@ -779,14 +1023,17 @@ fluid_midi_file_get_division(fluid_midi_file *midifile) * @return New MIDI event structure or NULL when out of memory. */ fluid_midi_event_t * -new_fluid_midi_event () +new_fluid_midi_event() { - fluid_midi_event_t* evt; + fluid_midi_event_t *evt; evt = FLUID_NEW(fluid_midi_event_t); - if (evt == NULL) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + evt->dtime = 0; evt->type = 0; evt->channel = 0; @@ -800,24 +1047,27 @@ new_fluid_midi_event () /** * Delete MIDI event structure. * @param evt MIDI event structure - * @return Always returns #FLUID_OK */ -int +void delete_fluid_midi_event(fluid_midi_event_t *evt) { fluid_midi_event_t *temp; + fluid_return_if_fail(evt != NULL); - while (evt) { + while(evt) + { temp = evt->next; /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ - if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2) - FLUID_FREE (evt->paramptr); + if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) && + evt->paramptr && evt->param2) + { + FLUID_FREE(evt->paramptr); + } FLUID_FREE(evt); evt = temp; } - return FLUID_OK; } /** @@ -1016,22 +1266,65 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val) * Assign sysex data to a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to SYSEX data - * @param size Size of SYSEX data + * @param size Size of SYSEX data in bytes * @param dynamic TRUE if the SYSEX data has been dynamically allocated and * should be freed when the event is freed (only applies if event gets destroyed * with delete_fluid_midi_event()) * @return Always returns #FLUID_OK * - * NOTE: Unlike the other event assignment functions, this one sets evt->type. + * @note Unlike the other event assignment functions, this one sets evt->type. */ int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) { - evt->type = MIDI_SYSEX; - evt->paramptr = data; + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign text data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to text data + * @param size Size of text data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + * @note Unlike the other event assignment functions, this one sets evt->type. + */ +int +fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign lyric data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to lyric data + * @param size Size of lyric data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + * @note Unlike the other event assignment functions, this one sets evt->type. + */ +int +fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic); + return FLUID_OK; +} + +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) +{ + evt->type = type; + evt->paramptr = data; evt->param1 = size; evt->param2 = dynamic; - return FLUID_OK; } /****************************************************** @@ -1047,9 +1340,12 @@ new_fluid_track(int num) { fluid_track_t *track; track = FLUID_NEW(fluid_track_t); - if (track == NULL) { + + if(track == NULL) + { return NULL; } + track->name = NULL; track->num = num; track->first = NULL; @@ -1062,17 +1358,14 @@ new_fluid_track(int num) /* * delete_fluid_track */ -int +void 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_return_if_fail(track != NULL); + + FLUID_FREE(track->name); + delete_fluid_midi_event(track->first); FLUID_FREE(track); - return FLUID_OK; } /* @@ -1082,32 +1375,31 @@ int fluid_track_set_name(fluid_track_t *track, char *name) { int len; - if (track->name != NULL) { + + if(track->name != NULL) + { FLUID_FREE(track->name); } - if (name == NULL) { + + if(name == NULL) + { track->name = NULL; return FLUID_OK; } + len = FLUID_STRLEN(name); track->name = FLUID_MALLOC(len + 1); - if (track->name == NULL) { + + 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 */ @@ -1116,29 +1408,14 @@ fluid_track_get_duration(fluid_track_t *track) { int time = 0; fluid_midi_event_t *evt = track->first; - while (evt != NULL) { + + while(evt != NULL) + { time += evt->dtime; evt = evt->next; } - return time; -} -/* - * fluid_track_count_events - */ -static 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; + return time; } /* @@ -1148,25 +1425,20 @@ int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt) { evt->next = NULL; - if (track->first == NULL) { + + if(track->first == NULL) + { track->first = evt; track->cur = evt; track->last = evt; - } else { + } + 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; + return FLUID_OK; } /* @@ -1175,9 +1447,11 @@ fluid_track_first_event(fluid_track_t *track) fluid_midi_event_t * fluid_track_next_event(fluid_track_t *track) { - if (track->cur != NULL) { + if(track->cur != NULL) + { track->cur = track->cur->next; } + return track->cur; } @@ -1197,17 +1471,31 @@ 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) + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks) { int status = FLUID_OK; fluid_midi_event_t *event; + int seeking = player->seek_ticks >= 0; + + if(seeking) + { + ticks = player->seek_ticks; /* update target ticks */ + + if(track->ticks > ticks) + { + fluid_track_reset(track); /* reset track if seeking backwards */ + } + } - while (1) { + while(1) + { event = track->cur; - if (event == NULL) { + + if(event == NULL) + { return status; } @@ -1218,25 +1506,37 @@ fluid_track_send_events(fluid_track_t *track, /* event->dtime, */ /* track->ticks + event->dtime); */ - if (track->ticks + event->dtime > ticks) { + if(track->ticks + event->dtime > ticks) + { return status; } track->ticks += event->dtime; - if (!player || event->type == MIDI_EOT) { + if(!player || event->type == MIDI_EOT) + { } - else if (event->type == MIDI_SET_TEMPO) { - fluid_player_set_midi_tempo(player, event->param1); + else if(seeking && (event->type == NOTE_ON || event->type == NOTE_OFF)) + { + /* skip on/off messages */ } - else { - if (player->playback_callback) + else + { + if(player->playback_callback) + { player->playback_callback(player->playback_userdata, event); + } + } + + if(event->type == MIDI_SET_TEMPO) + { + fluid_player_set_midi_tempo(player, event->param1); } fluid_track_next_event(track); } + return status; } @@ -1244,6 +1544,14 @@ fluid_track_send_events(fluid_track_t *track, * * fluid_player */ +static void +fluid_player_handle_reset_synth(void *data, const char *name, int value) +{ + fluid_player_t *player = data; + fluid_return_if_fail(player != NULL); + + player->reset_synth_between_songs = value; +} /** * Create a new MIDI player. @@ -1256,16 +1564,22 @@ new_fluid_player(fluid_synth_t *synth) int i; fluid_player_t *player; player = FLUID_NEW(fluid_player_t); - if (player == NULL) { + + if(player == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + player->status = FLUID_PLAYER_READY; player->loop = 1; player->ntracks = 0; - for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { player->track[i] = NULL; } + player->synth = synth; player->system_timer = NULL; player->sample_timer = NULL; @@ -1277,13 +1591,16 @@ new_fluid_player(fluid_synth_t *synth) player->deltatime = 4.0; player->cur_msec = 0; player->cur_ticks = 0; + player->seek_ticks = -1; fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); - player->use_system_timer = fluid_settings_str_equal(synth->settings, - "player.timing-source", "system"); + "player.timing-source", "system"); fluid_settings_getint(synth->settings, "player.reset-synth", &i); - player->reset_synth_between_songs = i; + fluid_player_handle_reset_synth(player, NULL, i); + + fluid_settings_callback_int(synth->settings, "player.reset-synth", + fluid_player_handle_reset_synth, player); return player; } @@ -1291,23 +1608,22 @@ new_fluid_player(fluid_synth_t *synth) /** * Delete a MIDI player instance. * @param player MIDI player instance - * @return Always returns #FLUID_OK */ -int +void delete_fluid_player(fluid_player_t *player) { fluid_list_t *q; - fluid_playlist_item* pi; + fluid_playlist_item *pi; + + fluid_return_if_fail(player != NULL); - if (player == NULL) { - return FLUID_OK; - } fluid_player_stop(player); fluid_player_reset(player); - while (player->playlist != NULL) { + while(player->playlist != NULL) + { q = player->playlist->next; - pi = (fluid_playlist_item*) player->playlist->data; + pi = (fluid_playlist_item *) player->playlist->data; FLUID_FREE(pi->filename); FLUID_FREE(pi->buffer); FLUID_FREE(pi); @@ -1316,7 +1632,6 @@ delete_fluid_player(fluid_player_t *player) } FLUID_FREE(player); - return FLUID_OK; } /** @@ -1327,14 +1642,12 @@ fluid_player_settings(fluid_settings_t *settings) { /* player.timing-source can be either "system" (use system timer) or "sample" (use timer based on number of written samples) */ - fluid_settings_register_str(settings, "player.timing-source", "sample", 0, - NULL, NULL); + fluid_settings_register_str(settings, "player.timing-source", "sample", 0); fluid_settings_add_option(settings, "player.timing-source", "sample"); fluid_settings_add_option(settings, "player.timing-source", "system"); /* Selects whether the player should reset the synth between songs, or not. */ - fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); + fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED); } @@ -1343,12 +1656,15 @@ fluid_player_reset(fluid_player_t *player) { int i; - for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { - if (player->track[i] != NULL) { + 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 = 1; */ @@ -1366,52 +1682,33 @@ fluid_player_reset(fluid_player_t *player) int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) { - if (player->ntracks < MAX_NUMBER_OF_TRACKS) { + 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; + else + { + return FLUID_FAILED; } } /** - * Change the MIDI callback function. This is usually set to + * Change the MIDI callback function. This is usually set to * fluid_synth_handle_midi_event, but can optionally be changed * to a user-defined function instead, for intercepting all MIDI - * messages sent to the synth. You can also use a midi router as + * messages sent to the synth. You can also use a midi router as * the callback function to modify the MIDI messages before sending - * them to the synth. + * them to the synth. * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Parameter sent to the callback function * @returns FLUID_OK * @since 1.1.4 */ -int -fluid_player_set_playback_callback(fluid_player_t* player, - handle_midi_event_func_t handler, void* handler_data) +int +fluid_player_set_playback_callback(fluid_player_t *player, + handle_midi_event_func_t handler, void *handler_data) { player->playback_callback = handler; player->playback_userdata = handler_data; @@ -1428,8 +1725,10 @@ int fluid_player_add(fluid_player_t *player, const char *midifile) { fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); - char* f = FLUID_STRDUP(midifile); - if (!pi || !f) { + char *f = FLUID_STRDUP(midifile); + + if(!pi || !f) + { FLUID_FREE(pi); FLUID_FREE(f); FLUID_LOG(FLUID_PANIC, "Out of memory"); @@ -1453,12 +1752,14 @@ fluid_player_add(fluid_player_t *player, const char *midifile) * @return #FLUID_OK or #FLUID_FAILED */ int -fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len) +fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len) { /* Take a copy of the buffer, so the caller can free immediately. */ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); void *buf_copy = FLUID_MALLOC(len); - if (!pi || !buf_copy) { + + if(!pi || !buf_copy) + { FLUID_FREE(pi); FLUID_FREE(buf_copy); FLUID_LOG(FLUID_PANIC, "Out of memory"); @@ -1480,28 +1781,33 @@ int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) { fluid_midi_file *midifile; - char* buffer; + char *buffer; size_t buffer_length; int buffer_owned; - if (item->filename != NULL) + if(item->filename != NULL) { fluid_file fp; /* This file is specified by filename; load the file from disk */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, - item->filename); + item->filename); /* Read the entire contents of the file into the buffer */ fp = FLUID_FOPEN(item->filename, "rb"); - if (fp == NULL) { + + if(fp == NULL) + { FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); return FLUID_FAILED; } + buffer = fluid_file_read_full(fp, &buffer_length); - if (buffer == NULL) + + if(buffer == NULL) { FLUID_FCLOSE(fp); return FLUID_FAILED; } + buffer_owned = 1; FLUID_FCLOSE(fp); } @@ -1509,7 +1815,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) { /* This file is specified by a pre-loaded buffer; load from memory */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)", - __FILE__, __LINE__, item->buffer); + __FILE__, __LINE__, item->buffer); buffer = (char *) item->buffer; buffer_length = item->buffer_len; /* Do not free the buffer (it is owned by the playlist) */ @@ -1517,59 +1823,83 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) } midifile = new_fluid_midi_file(buffer, buffer_length); - if (midifile == NULL) { - if (buffer_owned) { + + if(midifile == NULL) + { + if(buffer_owned) + { FLUID_FREE(buffer); } + return FLUID_FAILED; } + player->division = fluid_midi_file_get_division(midifile); fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ - if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) { - if (buffer_owned) { + if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) + { + if(buffer_owned) + { FLUID_FREE(buffer); } + delete_fluid_midi_file(midifile); return FLUID_FAILED; } + delete_fluid_midi_file(midifile); - if (buffer_owned) { + + if(buffer_owned) + { FLUID_FREE(buffer); } + return FLUID_OK; } -static void +void fluid_player_advancefile(fluid_player_t *player) { - if (player->playlist == NULL) { + if(player->playlist == NULL) + { return; /* No files to play */ } - if (player->currentfile != NULL) { + + if(player->currentfile != NULL) + { player->currentfile = fluid_list_next(player->currentfile); } - if (player->currentfile == NULL) { - if (player->loop == 0) { + + if(player->currentfile == NULL) + { + if(player->loop == 0) + { return; /* We're done playing */ } - if (player->loop > 0) { + + if(player->loop > 0) + { player->loop--; } + player->currentfile = player->playlist; } } -static void +void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) { - fluid_playlist_item* current_playitem; + fluid_playlist_item *current_playitem; int i; - do { + do + { fluid_player_advancefile(player); - if (player->currentfile == NULL) { + + if(player->currentfile == NULL) + { /* Failed to find next song, probably since we're finished */ player->status = FLUID_PLAYER_DONE; return; @@ -1577,7 +1907,8 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) fluid_player_reset(player); current_playitem = (fluid_playlist_item *) player->currentfile->data; - } while (fluid_player_load(player, current_playitem) != FLUID_OK); + } + while(fluid_player_load(player, current_playitem) != FLUID_OK); /* Successfully loaded midi file */ @@ -1586,18 +1917,20 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) player->start_ticks = 0; player->cur_ticks = 0; - if (player->reset_synth_between_songs) { + if(player->reset_synth_between_songs) + { fluid_synth_system_reset(player->synth); } - for (i = 0; i < player->ntracks; i++) { - if (player->track[i] != NULL) { + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { fluid_track_reset(player->track[i]); } } } - /* * fluid_player_callback */ @@ -1613,36 +1946,61 @@ fluid_player_callback(void *data, unsigned int msec) synth = player->synth; loadnextfile = player->currentfile == NULL ? 1 : 0; - do { - if (loadnextfile) { + + do + { + if(loadnextfile) + { loadnextfile = 0; fluid_player_playlist_load(player, msec); - if (player->currentfile == NULL) { + + if(player->currentfile == NULL) + { return 0; } } player->cur_msec = msec; player->cur_ticks = (player->start_ticks - + (int) ((double) (player->cur_msec - player->start_msec) - / player->deltatime)); + + (int)((double)(player->cur_msec - player->start_msec) + / player->deltatime + 0.5)); /* 0.5 to average overall error when casting */ - for (i = 0; i < player->ntracks; i++) { - if (!fluid_track_eot(player->track[i])) { + if(player->seek_ticks >= 0) + { + fluid_synth_all_sounds_off(synth, -1); /* avoid hanging notes */ + } + + 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) { + + if(fluid_track_send_events(player->track[i], synth, player, + player->cur_ticks) != FLUID_OK) + { /* */ } } } - if (status == FLUID_PLAYER_DONE) { + if(player->seek_ticks >= 0) + { + player->start_ticks = player->seek_ticks; /* tick position of last tempo value (which is now) */ + player->cur_ticks = player->seek_ticks; + player->begin_msec = msec; /* only used to calculate the duration of playing */ + player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ + player->seek_ticks = -1; /* clear seek_ticks */ + } + + if(status == FLUID_PLAYER_DONE) + { FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, - __LINE__, (msec - player->begin_msec) / 1000.0); + __LINE__, (msec - player->begin_msec) / 1000.0); loadnextfile = 1; } - } while (loadnextfile); + } + while(loadnextfile); player->status = status; @@ -1657,30 +2015,39 @@ fluid_player_callback(void *data, unsigned int msec) int fluid_player_play(fluid_player_t *player) { - if (player->status == FLUID_PLAYER_PLAYING) { + if(player->status == FLUID_PLAYER_PLAYING) + { return FLUID_OK; } - if (player->playlist == NULL) { + if(player->playlist == NULL) + { return FLUID_OK; } player->status = FLUID_PLAYER_PLAYING; - if (player->use_system_timer) { + if(player->use_system_timer) + { player->system_timer = new_fluid_timer((int) player->deltatime, - fluid_player_callback, (void *) player, TRUE, FALSE, TRUE); - if (player->system_timer == NULL) { + fluid_player_callback, (void *) player, TRUE, FALSE, TRUE); + + if(player->system_timer == NULL) + { return FLUID_FAILED; } - } else { + } + else + { player->sample_timer = new_fluid_sample_timer(player->synth, - fluid_player_callback, (void *) player); + fluid_player_callback, (void *) player); - if (player->sample_timer == NULL) { + if(player->sample_timer == NULL) + { return FLUID_FAILED; } } + return FLUID_OK; } @@ -1692,12 +2059,16 @@ fluid_player_play(fluid_player_t *player) int fluid_player_stop(fluid_player_t *player) { - if (player->system_timer != NULL) { + if(player->system_timer != NULL) + { delete_fluid_timer(player->system_timer); } - if (player->sample_timer != NULL) { + + if(player->sample_timer != NULL) + { delete_fluid_sample_timer(player->synth, player->sample_timer); } + player->status = FLUID_PLAYER_DONE; player->sample_timer = NULL; player->system_timer = NULL; @@ -1717,13 +2088,35 @@ fluid_player_get_status(fluid_player_t *player) } /** - * Enable looping of a MIDI player + * Seek in the currently playing file. + * @param player MIDI player instance + * @param ticks the position to seek to in the current file + * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file, + * #FLUID_OK otherwise + * @since 2.0.0 + * + * The actual seek is performed during the player_callback. + */ +int fluid_player_seek(fluid_player_t *player, int ticks) +{ + if(ticks < 0 || ticks > fluid_player_get_total_ticks(player)) + { + return FLUID_FAILED; + } + + player->seek_ticks = ticks; + return FLUID_OK; +} + + +/** + * Enable looping of a MIDI player * @param player MIDI player instance * @param loop Times left to loop the playlist. -1 means loop infinitely. * @return Always returns #FLUID_OK * @since 1.1.0 * - * For example, if you want to loop the playlist twice, set loop to 2 + * For example, if you want to loop the playlist twice, set loop to 2 * and call this function before you start the player. */ int fluid_player_set_loop(fluid_player_t *player, int loop) @@ -1746,8 +2139,8 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) 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); + "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; } @@ -1758,10 +2151,9 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) * @param bpm Tempo in beats per minute * @return Always returns #FLUID_OK */ -int -fluid_player_set_bpm(fluid_player_t *player, int bpm) +int fluid_player_set_bpm(fluid_player_t *player, int bpm) { - return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm)); + return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm)); } /** @@ -1772,21 +2164,82 @@ fluid_player_set_bpm(fluid_player_t *player, int bpm) int fluid_player_join(fluid_player_t *player) { - if (player->system_timer) { + if(player->system_timer) + { return fluid_timer_join(player->system_timer); - } else if (player->sample_timer) { + } + else if(player->sample_timer) + { /* Busy-wait loop, since there's no thread to wait for... */ - while (player->status != FLUID_PLAYER_DONE) { -#if defined(WIN32) - Sleep(10); -#else - usleep(10000); -#endif + while(player->status != FLUID_PLAYER_DONE) + { + fluid_msleep(10); } } + return FLUID_OK; } +/** + * Get the number of tempo ticks passed. + * @param player MIDI player instance + * @return The number of tempo ticks passed + * @since 1.1.7 + */ +int fluid_player_get_current_tick(fluid_player_t *player) +{ + return player->cur_ticks; +} + +/** + * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play. + * @param player MIDI player instance + * @return Total tick count of the sequence + * @since 1.1.7 + */ +int fluid_player_get_total_ticks(fluid_player_t *player) +{ + int i; + int maxTicks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + int ticks = fluid_track_get_duration(player->track[i]); + + if(ticks > maxTicks) + { + maxTicks = ticks; + } + } + } + + return maxTicks; +} + +/** + * Get the tempo of a MIDI player in beats per minute. + * @param player MIDI player instance + * @return MIDI player tempo in BPM + * @since 1.1.7 + */ +int fluid_player_get_bpm(fluid_player_t *player) +{ + return (int)(60e6 / player->miditempo); +} + +/** + * Get the tempo of a MIDI player. + * @param player MIDI player instance + * @return Tempo of the MIDI player (in microseconds per quarter note, as per MIDI file spec) + * @since 1.1.7 + */ +int fluid_player_get_midi_tempo(fluid_player_t *player) +{ + return player->miditempo; +} + /************************************************************************ * MIDI PARSER * @@ -1796,14 +2249,17 @@ fluid_player_join(fluid_player_t *player) * new_fluid_midi_parser */ fluid_midi_parser_t * -new_fluid_midi_parser () +new_fluid_midi_parser() { fluid_midi_parser_t *parser; parser = FLUID_NEW(fluid_midi_parser_t); - if (parser == NULL) { + + 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; } @@ -1811,11 +2267,12 @@ new_fluid_midi_parser () /* * delete_fluid_midi_parser */ -int +void delete_fluid_midi_parser(fluid_midi_parser_t *parser) { + fluid_return_if_fail(parser != NULL); + FLUID_FREE(parser); - return FLUID_OK; } /** @@ -1824,6 +2281,11 @@ delete_fluid_midi_parser(fluid_midi_parser_t *parser) * @param c Next character in MIDI stream * @return A parsed MIDI event or NULL if none. Event is internal and should * not be modified or freed and is only valid until next call to this function. + * @internal Do not expose this function to the public API. It would allow downstream + * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out + * the needed midi information using the getter functions of fluid_midi_event_t. + * This parser however is incomplete as it e.g. only provides a limited buffer to + * store and process SYSEX data (i.e. doesnt allow arbitrary lengths) */ fluid_midi_event_t * fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) @@ -1832,8 +2294,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle * of another message. */ - if (c >= 0xF8) { - if (c == MIDI_SYSTEM_RESET) { + if(c >= 0xF8) + { + if(c == MIDI_SYSTEM_RESET) + { parser->event.type = c; parser->status = 0; /* clear the status */ return &parser->event; @@ -1843,30 +2307,40 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) } /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ - if (c & 0x80) { + if(c & 0x80) + { /* Any status byte terminates SYSEX messages (not just 0xF7) */ - if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) { + if(parser->status == MIDI_SYSEX && parser->nr_bytes > 0) + { event = &parser->event; fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes, - FALSE); - } else + FALSE); + } + else + { event = NULL; + } - if (c < 0xF0) /* Voice category message? */ + if(c < 0xF0) /* Voice category message? */ { 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; + - 1; parser->nr_bytes = 0; /* 0 bytes read so far */ - } else if (c == MIDI_SYSEX) { + } + else if(c == MIDI_SYSEX) + { parser->status = MIDI_SYSEX; parser->nr_bytes = 0; - } else - parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } + else + { + parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } return event; /* Return SYSEX event or NULL */ } @@ -1874,11 +2348,14 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) /* Data/parameter byte */ /* Discard data bytes for events we don't care about */ - if (parser->status == 0) + if(parser->status == 0) + { return NULL; + } /* Max data size exceeded? (SYSEX messages only really) */ - if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) { + if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) + { parser->status = 0; /* Discard the rest of the message */ return NULL; } @@ -1887,8 +2364,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) parser->data[parser->nr_bytes++] = c; /* Do we still need more data to get this event complete? */ - if (parser->nr_bytes < parser->nr_bytes_total) + if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total) + { return NULL; + } /* Event is complete, return it. * Running status byte MIDI feature is also handled here. */ @@ -1896,22 +2375,25 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) parser->event.channel = parser->channel; parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */ - 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->data[0]; /* For example key number */ - parser->event.param2 = parser->data[1]; /* For example velocity */ - break; - case PITCH_BEND: - /* Pitch-bend is transmitted with 14-bit precision. */ - parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; - break; - default: /* Unlikely */ - return NULL; + 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->data[0]; /* For example key number */ + parser->event.param2 = parser->data[1]; /* For example velocity */ + break; + + case PITCH_BEND: + /* Pitch-bend is transmitted with 14-bit precision. */ + parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; + break; + + default: /* Unlikely */ + return NULL; } return &parser->event; @@ -1922,28 +2404,35 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) static int fluid_midi_event_length(unsigned char event) { - switch (event & 0xF0) { - case NOTE_OFF: - case NOTE_ON: - case KEY_PRESSURE: - case CONTROL_CHANGE: - case PITCH_BEND: - return 3; - case PROGRAM_CHANGE: - case CHANNEL_PRESSURE: - return 2; - } - switch (event) { - case MIDI_TIME_CODE: - case MIDI_SONG_SELECT: - case 0xF4: - case 0xF5: - return 2; - case MIDI_TUNE_REQUEST: - return 1; - case MIDI_SONG_POSITION: - return 3; + switch(event & 0xF0) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PITCH_BEND: + return 3; + + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + return 2; } + + switch(event) + { + case MIDI_TIME_CODE: + case MIDI_SONG_SELECT: + case 0xF4: + case 0xF5: + return 2; + + case MIDI_TUNE_REQUEST: + return 1; + + case MIDI_SONG_POSITION: + return 3; + } + return 1; } #endif diff --git a/libs/fluidsynth/src/fluid_midi.h b/libs/fluidsynth/src/fluid_midi.h index 90fcef7c91..9e34a0ffee 100644 --- a/libs/fluidsynth/src/fluid_midi.h +++ b/libs/fluidsynth/src/fluid_midi.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -27,9 +27,9 @@ 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); +fluid_midi_parser_t *new_fluid_midi_parser(void); +void 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); /*************************************************************** @@ -40,140 +40,146 @@ fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigne #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_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 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 = 0x44, + 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 }; /* General MIDI RPN event numbers (LSB, MSB = 0) */ -enum midi_rpn_event { - RPN_PITCH_BEND_RANGE = 0x00, - RPN_CHANNEL_FINE_TUNE = 0x01, - RPN_CHANNEL_COARSE_TUNE = 0x02, - RPN_TUNING_PROGRAM_CHANGE = 0x03, - RPN_TUNING_BANK_SELECT = 0x04, - RPN_MODULATION_DEPTH_RANGE = 0x05 +enum midi_rpn_event +{ + RPN_PITCH_BEND_RANGE = 0x00, + RPN_CHANNEL_FINE_TUNE = 0x01, + RPN_CHANNEL_COARSE_TUNE = 0x02, + RPN_TUNING_PROGRAM_CHANGE = 0x03, + RPN_TUNING_BANK_SELECT = 0x04, + RPN_MODULATION_DEPTH_RANGE = 0x05 }; -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 midi_meta_event +{ + MIDI_TEXT = 0x01, + 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 }; /* MIDI SYSEX useful manufacturer values */ -enum midi_sysex_manuf { - MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ - MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ - MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ +enum midi_sysex_manuf +{ + MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ + MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ + MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ }; #define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ @@ -185,17 +191,18 @@ enum midi_sysex_manuf { /** * SYSEX tuning message IDs. */ -enum midi_sysex_tuning_msg_id { - MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ - MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ - MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ - MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ - MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ +enum midi_sysex_tuning_msg_id +{ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ }; /* General MIDI sub-ID #2 */ @@ -204,9 +211,9 @@ enum midi_sysex_tuning_msg_id { enum fluid_driver_status { - FLUID_MIDI_READY, - FLUID_MIDI_LISTENING, - FLUID_MIDI_DONE + FLUID_MIDI_READY, + FLUID_MIDI_LISTENING, + FLUID_MIDI_DONE }; /*************************************************************** @@ -214,58 +221,40 @@ enum fluid_driver_status * 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; /* Link to next event */ - void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */ - unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ - unsigned int param1; /* First parameter */ - unsigned int param2; /* Second parameter */ - unsigned char type; /* MIDI event type */ - unsigned char channel; /* MIDI channel */ +struct _fluid_midi_event_t +{ + fluid_midi_event_t *next; /* Link to next event */ + void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */ + unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ + unsigned int param1; /* First parameter */ + unsigned int param2; /* Second parameter */ + unsigned char type; /* MIDI event type */ + unsigned char channel; /* MIDI channel */ }; /* * 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; +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_playlist_item * Used as the `data' elements of the fluid_player.playlist. * Represents either a filename or a pre-loaded memory buffer. @@ -273,92 +262,73 @@ int fluid_track_send_events(fluid_track_t* track, */ typedef struct { - char* filename; /** Name of file (owned); NULL if data pre-loaded */ - void* buffer; /** The MIDI file data (owned); NULL if filename */ + char *filename; /** Name of file (owned); NULL if data pre-loaded */ + void *buffer; /** The MIDI file data (owned); NULL if filename */ size_t buffer_len; /** Number of bytes in buffer; 0 if filename */ } fluid_playlist_item; /* * fluid_player */ -struct _fluid_player_t { - int status; - int ntracks; - fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; - fluid_synth_t* synth; - fluid_timer_t* system_timer; - fluid_sample_timer_t* sample_timer; - - int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */ - fluid_list_t* playlist; /* List of fluid_playlist_item* objects */ - fluid_list_t* currentfile; /* points to an item in files, or NULL if not playing */ - - char send_program_change; /* should we ignore the program changes? */ - char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ - char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ - 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; - - handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ - void* playback_userdata; /* pointer to user-defined data passed to playback_callback function */ +struct _fluid_player_t +{ + int status; + int ntracks; + fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; + fluid_synth_t *synth; + fluid_timer_t *system_timer; + fluid_sample_timer_t *sample_timer; + + int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */ + fluid_list_t *playlist; /* List of fluid_playlist_item* objects */ + fluid_list_t *currentfile; /* points to an item in files, or NULL if not playing */ + + char send_program_change; /* should we ignore the program changes? */ + char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ + char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ + int seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ + 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; + + handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ + void *playback_userdata; /* pointer to user-defined data passed to playback_callback function */ }; -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, fluid_playlist_item *item); - -void fluid_player_settings(fluid_settings_t* settings); +void fluid_player_settings(fluid_settings_t *settings); /* * fluid_midi_file */ -typedef struct { - const char* buffer; /* Entire contents of MIDI file (borrowed) */ - int buf_len; /* Length of buffer, in bytes */ - int buf_pos; /* Current read position in contents buffer */ - int eof; /* The "end of file" condition */ - 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 +typedef struct +{ + const char *buffer; /* Entire contents of MIDI file (borrowed) */ + int buf_len; /* Length of buffer, in bytes */ + int buf_pos; /* Current read position in contents buffer */ + int eof; /* The "end of file" condition */ + 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; - int dtime; + double tempo; /* Beats per second (SI rules =) */ + int tracklen; + int trackpos; + int eot; + int varlen; + int dtime; } fluid_midi_file; -fluid_midi_file* new_fluid_midi_file(const char* buffer, size_t length); -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_eof(fluid_midi_file* mf); -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); #define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */ @@ -366,17 +336,15 @@ int fluid_midi_file_get_division(fluid_midi_file* midifile); /* * 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 char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */ - fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */ +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 char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */ + 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); - #endif /* _FLUID_MIDI_H */ diff --git a/libs/fluidsynth/src/fluid_mod.c b/libs/fluidsynth/src/fluid_mod.c index 5931aa52a6..9a48ed45d8 100644 --- a/libs/fluidsynth/src/fluid_mod.c +++ b/libs/fluidsynth/src/fluid_mod.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -22,389 +22,446 @@ #include "fluid_chan.h" #include "fluid_voice.h" -/* - * fluid_mod_clone +/** + * Clone the modulators destination, sources, flags and amount. + * @param mod the modulator to store the copy to + * @param src the source modulator to retrieve the information from + * @note The \c next member of \c mod will be left unchanged. */ void -fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src) +fluid_mod_clone(fluid_mod_t *mod, const 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; + mod->dest = src->dest; + mod->src1 = src->src1; + mod->flags1 = src->flags1; + mod->src2 = src->src2; + mod->flags2 = src->flags2; + mod->amount = src->amount; } /** * Set a modulator's primary source controller and flags. - * @param mod Modulator + * @param mod The modulator instance * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller * (#FLUID_MOD_CC), see #fluid_mod_flags. */ void -fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags) +fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags) { - mod->src1 = src; - mod->flags1 = flags; + mod->src1 = src; + mod->flags1 = flags; } /** * Set a modulator's secondary source controller and flags. - * @param mod Modulator + * @param mod The modulator instance * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller * (#FLUID_MOD_CC), see #fluid_mod_flags. */ void -fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags) +fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags) { - mod->src2 = src; - mod->flags2 = flags; + mod->src2 = src; + mod->flags2 = flags; } /** * Set the destination effect of a modulator. - * @param mod Modulator + * @param mod The modulator instance * @param dest Destination generator (#fluid_gen_type) */ void -fluid_mod_set_dest(fluid_mod_t* mod, int dest) +fluid_mod_set_dest(fluid_mod_t *mod, int dest) { - mod->dest = dest; + mod->dest = dest; } /** * Set the scale amount of a modulator. - * @param mod Modulator + * @param mod The modulator instance * @param amount Scale amount to assign */ void -fluid_mod_set_amount(fluid_mod_t* mod, double amount) +fluid_mod_set_amount(fluid_mod_t *mod, double amount) { - mod->amount = (double) amount; + mod->amount = (double) amount; } /** * Get the primary source value from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). */ int -fluid_mod_get_source1(fluid_mod_t* mod) +fluid_mod_get_source1(const fluid_mod_t *mod) { - return mod->src1; + return mod->src1; } /** * Get primary source flags from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return The primary source flags (#fluid_mod_flags). */ int -fluid_mod_get_flags1(fluid_mod_t* mod) +fluid_mod_get_flags1(const fluid_mod_t *mod) { - return mod->flags1; + return mod->flags1; } /** * Get the secondary source value from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). */ int -fluid_mod_get_source2(fluid_mod_t* mod) +fluid_mod_get_source2(const fluid_mod_t *mod) { - return mod->src2; + return mod->src2; } /** * Get secondary source flags from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return The secondary source flags (#fluid_mod_flags). */ int -fluid_mod_get_flags2(fluid_mod_t* mod) +fluid_mod_get_flags2(const fluid_mod_t *mod) { - return mod->flags2; + return mod->flags2; } /** * Get destination effect from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return Destination generator (#fluid_gen_type) */ int -fluid_mod_get_dest(fluid_mod_t* mod) +fluid_mod_get_dest(const fluid_mod_t *mod) { - return mod->dest; + return mod->dest; } /** * Get the scale amount from a modulator. - * @param mod Modulator + * @param mod The modulator instance * @return Scale amount */ double -fluid_mod_get_amount(fluid_mod_t* mod) +fluid_mod_get_amount(const fluid_mod_t *mod) +{ + return (double) mod->amount; +} + +/* + * retrieves the initial value from the given source of the modulator + */ +static fluid_real_t +fluid_mod_get_source_value(const unsigned char mod_src, + const unsigned char mod_flags, + fluid_real_t *range, + const fluid_voice_t *voice + ) { - return (fluid_real_t) mod->amount; + const fluid_channel_t *chan = voice->channel; + fluid_real_t val; + + if(mod_flags & FLUID_MOD_CC) + { + /* From MIDI Recommended Practice (RP-036) Default Pan Formula: + * "Since MIDI controller values range from 0 to 127, the exact center + * of the range, 63.5, cannot be represented. Therefore, the effective + * range for CC#10 is modified to be 1 to 127, and values 0 and 1 both + * pan hard left. The recommended method is to subtract 1 from the + * value of CC#10, and saturate the result to be non-negative." + * + * We treat the balance control in exactly the same way, as the same + * problem applies here as well. + */ + if(mod_src == PAN_MSB || mod_src == BALANCE_MSB) + { + *range = 126; + val = fluid_channel_get_cc(chan, mod_src) - 1; + + if(val < 0) + { + val = 0; + } + } + else + { + val = fluid_channel_get_cc(chan, mod_src); + } + } + else + { + switch(mod_src) + { + case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ + val = *range; + break; + + case FLUID_MOD_VELOCITY: + val = fluid_voice_get_actual_velocity(voice); + break; + + case FLUID_MOD_KEY: + val = fluid_voice_get_actual_key(voice); + break; + + case FLUID_MOD_KEYPRESSURE: + val = fluid_channel_get_key_pressure(chan, voice->key); + break; + + case FLUID_MOD_CHANNELPRESSURE: + val = fluid_channel_get_channel_pressure(chan); + break; + + case FLUID_MOD_PITCHWHEEL: + val = fluid_channel_get_pitch_bend(chan); + *range = 0x4000; + break; + + case FLUID_MOD_PITCHWHEELSENS: + val = fluid_channel_get_pitch_wheel_sensitivity(chan); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src); + val = 0.0; + } + } + + return val; } +/** + * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0] + */ +static fluid_real_t +fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range) +{ + /* normalized value, i.e. usually in the range [0;1] + * + * if val was retrieved from pitch_bend then [-0.5;0.5] + */ + const fluid_real_t val_norm = val / range; + + /* we could also only switch case the lower nibble of mod_flags, however + * this would keep us from adding further mod types in the future + * + * instead just remove the flag(s) we already took care of + */ + mod_flags &= ~FLUID_MOD_CC; + + switch(mod_flags/* & 0x0f*/) + { + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */ + val = val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */ + val = 1.0f - val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */ + val = -1.0f + 2.0f * val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */ + val = 1.0f - 2.0f * val_norm; + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */ + val = fluid_concave(127 * (val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */ + val = fluid_concave(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */ + val = (val_norm > 0.5f) ? fluid_concave(127 * 2 * (val_norm - 0.5f)) + : -fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */ + val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f)) + : fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */ + val = fluid_convex(127 * (val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */ + val = fluid_convex(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */ + val = (val_norm > 0.5f) ? fluid_convex(127 * 2 * (val_norm - 0.5f)) + : -fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */ + val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f)) + : fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */ + val = (val_norm >= 0.5f) ? 1.0f : 0.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */ + val = (val_norm >= 0.5f) ? 0.0f : 1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */ + val = (val_norm >= 0.5f) ? 1.0f : -1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */ + val = (val_norm >= 0.5f) ? -1.0f : 1.0f; + break; + + /* + * MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1, + * the less will be the resulting change of the sinus. When using this sin() + * for scaling the cutoff frequency, there will be no audible difference between + * MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87 + * (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which + * is close enough. + */ + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */ + val = sin(M_PI / 2 * val_norm * 0.87); + break; + + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = sin(M_PI / 2 * (1.0f - val_norm) * 0.87); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */ + val = (val_norm > 0.5f) ? sin(M_PI / 2 * 2 * (val_norm - 0.5f)) + : -sin(M_PI / 2 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = (val_norm > 0.5f) ? -sin(M_PI / 2 * 2 * (val_norm - 0.5f)) + : sin(M_PI / 2 * 2 * (0.5f - val_norm)); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags); + val = 0.0f; + break; + } + + return val; +} /* * fluid_mod_get_value */ fluid_real_t -fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice) +fluid_mod_get_value(fluid_mod_t *mod, 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)) { + extern fluid_mod_t default_vel2filter_mod; + + fluid_real_t v1 = 0.0, v2 = 1.0; + fluid_real_t range1 = 127.0, range2 = 127.0; + + /* '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(fluid_mod_test_identity(mod, &default_vel2filter_mod)) + { // S. Christian Collins' mod, to stop forcing velocity based filtering -/* - if (voice->vel < 64){ - return (fluid_real_t) mod->amount / 2.0; - } else { - return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; + /* + if (voice->vel < 64){ + return (fluid_real_t) mod->amount / 2.0; + } else { + return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; + } + */ + return 0; // (fluid_real_t) mod->amount / 2.0; } -*/ - return 0; // (fluid_real_t) mod->amount / 2.0; - } + // end S. Christian Collins' mod - /* 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 = fluid_channel_get_key_pressure (chan); - break; - case FLUID_MOD_CHANNELPRESSURE: - v1 = fluid_channel_get_channel_pressure (chan); - break; - case FLUID_MOD_PITCHWHEEL: - v1 = fluid_channel_get_pitch_bend (chan); - range1 = 0x4000; - break; - case FLUID_MOD_PITCHWHEELSENS: - v1 = fluid_channel_get_pitch_wheel_sensitivity (chan); - break; - default: - v1 = 0.0; - } - } + /* get the initial value of the first source */ + if(mod->src1 > 0) + { + v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, voice); - /* 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; + /* transform the input value */ + v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); } - } 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 = fluid_channel_get_key_pressure (chan); - break; - case FLUID_MOD_CHANNELPRESSURE: - v2 = fluid_channel_get_channel_pressure (chan); - break; - case FLUID_MOD_PITCHWHEEL: - v2 = fluid_channel_get_pitch_bend (chan); - break; - case FLUID_MOD_PITCHWHEELSENS: - v2 = fluid_channel_get_pitch_wheel_sensitivity (chan); - break; - default: - v1 = 0.0f; - } + else + { + return 0.0; } - /* 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; + /* no need to go further */ + if(v1 == 0.0f) + { + return 0.0f; + } + + /* get the second input source */ + if(mod->src2 > 0) + { + v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, voice); + + /* transform the second input value */ + v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); + } + else + { + v2 = 1.0f; } - } else { - v2 = 1.0f; - } - /* it's as simple as that: */ - return (fluid_real_t) mod->amount * v1 * v2; + /* it's as simple as that: */ + return (fluid_real_t) mod->amount * v1 * v2; } /** * Create a new uninitialized modulator structure. * @return New allocated modulator or NULL if out of memory */ -fluid_mod_t* -fluid_mod_new() +fluid_mod_t * +new_fluid_mod() { - 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_t *mod = FLUID_NEW(fluid_mod_t); + + if(mod == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + return mod; } /** @@ -412,9 +469,21 @@ fluid_mod_new() * @param mod Modulator to free */ void -fluid_mod_delete (fluid_mod_t *mod) +delete_fluid_mod(fluid_mod_t *mod) +{ + FLUID_FREE(mod); +} + +/** + * Returns the size of the fluid_mod_t structure. + * + * Useful in low latency scenarios e.g. to allocate a modulator on the stack. + * + * @return Size of fluid_mod_t in bytes + */ +size_t fluid_mod_sizeof() { - FLUID_FREE(mod); + return sizeof(fluid_mod_t); } /** @@ -426,63 +495,181 @@ fluid_mod_delete (fluid_mod_t *mod) * 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) +fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2) +{ + return mod1->dest == mod2->dest + && mod1->src1 == mod2->src1 + && mod1->src2 == mod2->src2 + && mod1->flags1 == mod2->flags1 + && mod1->flags2 == mod2->flags2; +} + +/** + * Check if the modulator has the given source. + * + * @param mod The modulator instance + * @param cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for) + * + * @return TRUE if the modulator has the given source, FALSE otherwise. + */ +int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl) +{ + return + ( + ( + ((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)) + ) + ); +} + +/** + * Check if the modulator has the given destination. + * @param mod The modulator instance + * @param gen The destination generator of type #fluid_gen_type to check for + * @return TRUE if the modulator has the given destination, FALSE otherwise. + */ +int fluid_mod_has_dest(const fluid_mod_t *mod, int gen) { - return mod1->dest == mod2->dest - && mod1->src1 == mod2->src1 - && mod1->src2 == mod2->src2 - && mod1->flags1 == mod2->flags1 - && mod1->flags2 == mod2->flags2; + return mod->dest == gen; } + /* 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); -}; +#ifdef DEBUG +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_CUSTOM_FILTERQ: + printf("custom-Q"); + break; + + case GEN_CUSTOM_FILTERFC: + printf("custom-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_CUSTOM_BALANCE: + printf("balance"); + 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); +}; +#endif diff --git a/libs/fluidsynth/src/fluid_mod.h b/libs/fluidsynth/src/fluid_mod.h index 81c9f76c70..e834baa513 100644 --- a/libs/fluidsynth/src/fluid_mod.h +++ b/libs/fluidsynth/src/fluid_mod.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,17 +24,30 @@ #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); +/* + * Modulator structure. See SoundFont 2.04 PDF section 8.2. + */ +struct _fluid_mod_t +{ + unsigned char dest; /**< Destination generator to control */ + unsigned char src1; /**< Source controller 1 */ + unsigned char flags1; /**< Source controller 1 flags */ + unsigned char src2; /**< Source controller 2 */ + unsigned char flags2; /**< Source controller 2 flags */ + double amount; /**< Multiplier 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; +}; -#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))))) +fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice); -#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen) +#ifdef DEBUG +void fluid_dump_modulator(fluid_mod_t *mod); +#endif #endif /* _FLUID_MOD_H */ diff --git a/libs/fluidsynth/src/fluid_phase.h b/libs/fluidsynth/src/fluid_phase.h index 15f2fa7550..08975cbb17 100644 --- a/libs/fluidsynth/src/fluid_phase.h +++ b/libs/fluidsynth/src/fluid_phase.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -22,9 +22,7 @@ #ifndef _FLUID_PHASE_H #define _FLUID_PHASE_H -#if HAVE_CONFIG_H #include "config.h" -#endif /* * phase @@ -47,7 +45,7 @@ * 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. */ -typedef unsigned long long fluid_phase_t; +typedef uint64_t fluid_phase_t; /* Purpose: * Set a to b. @@ -56,26 +54,26 @@ typedef unsigned long long fluid_phase_t; */ #define fluid_phase_set(a,b) a=b; -#define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32) +#define fluid_phase_set_int(a, b) ((a) = ((uint64_t)(b)) << 32) /* 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) = (((unsigned long long)(b)) << 32) \ - | (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) + (a) = (((uint64_t)(b)) << 32) \ + | (uint32_t) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) /* create a fluid_phase_t from an index and a fraction value */ #define fluid_phase_from_index_fract(index, fract) \ - ((((unsigned long long)(index)) << 32) + (fract)) + ((((uint64_t)(index)) << 32) + (fract)) /* Purpose: * Return the index and the fractional part, respectively. */ #define fluid_phase_index(_x) \ ((unsigned int)((_x) >> 32)) #define fluid_phase_fract(_x) \ - ((uint32)((_x) & 0xFFFFFFFF)) + ((uint32_t)((_x) & 0xFFFFFFFF)) /* Get the phase index with fractional rounding */ #define fluid_phase_index_round(_x) \ @@ -108,7 +106,7 @@ typedef unsigned long long fluid_phase_t; /* Purpose: * Subtract b samples from a. */ -#define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32) +#define fluid_phase_sub_int(a, b) ((a) -= (uint64_t)(b) << 32) /* Purpose: * Creates the expression a.index++. */ diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c index 166007da3f..51b4faa252 100644 --- a/libs/fluidsynth/src/fluid_rev.c +++ b/libs/fluidsynth/src/fluid_rev.c @@ -9,6 +9,7 @@ Translated to C by Peter Hanappe, Mai 2001 */ +#include "fluid_sys.h" #include "fluid_rev.h" /*************************************************************** @@ -18,98 +19,75 @@ /* 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!!. + * 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. On some platforms this will lead to drastic spikes in the + * CPU load. This is especially noticable on some older Pentium (especially + * Pentium 3) processors, but even more modern Intel Core processors still show + * reduced performance with denormals. While there are compile-time switches to + * treat denormals as zero for a lot of processors, those are not available or + * effective on all platforms. * - * 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. + * The fix used here: 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. */ - -# 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; +struct _fluid_allpass +{ + fluid_real_t feedback; + fluid_real_t *buffer; + int bufsize; + int bufidx; }; -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_init(fluid_allpass *allpass); +void fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val); +fluid_real_t fluid_allpass_getfeedback(fluid_allpass *allpass); static void -fluid_allpass_setbuffer(fluid_allpass* allpass, int size) +fluid_allpass_setbuffer(fluid_allpass *allpass, int size) { - allpass->bufidx = 0; - allpass->buffer = FLUID_ARRAY(fluid_real_t,size); - allpass->bufsize = size; + allpass->bufidx = 0; + allpass->buffer = FLUID_ARRAY(fluid_real_t, size); + allpass->bufsize = size; } static void -fluid_allpass_release(fluid_allpass* allpass) +fluid_allpass_release(fluid_allpass *allpass) { - FLUID_FREE(allpass->buffer); + FLUID_FREE(allpass->buffer); } void -fluid_allpass_init(fluid_allpass* allpass) +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. */ - } + 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) +fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val) { - allpass->feedback = val; + allpass->feedback = val; } fluid_real_t -fluid_allpass_getfeedback(fluid_allpass* allpass) +fluid_allpass_getfeedback(fluid_allpass *allpass) { - return allpass->feedback; + return allpass->feedback; } #define fluid_allpass_process(_allpass, _input) \ @@ -125,87 +103,76 @@ fluid_allpass_getfeedback(fluid_allpass* allpass) _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; +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, int size); -void fluid_comb_release(fluid_comb* comb); -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, int size); +void fluid_comb_release(fluid_comb *comb); +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, int size) +fluid_comb_setbuffer(fluid_comb *comb, int size) { - comb->filterstore = 0; - comb->bufidx = 0; - comb->buffer = FLUID_ARRAY(fluid_real_t,size); - comb->bufsize = size; + comb->filterstore = 0; + comb->bufidx = 0; + comb->buffer = FLUID_ARRAY(fluid_real_t, size); + comb->bufsize = size; } void -fluid_comb_release(fluid_comb* comb) +fluid_comb_release(fluid_comb *comb) { - FLUID_FREE(comb->buffer); + FLUID_FREE(comb->buffer); } void -fluid_comb_init(fluid_comb* comb) +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. */ - } + 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) +fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val) { - comb->damp1 = val; - comb->damp2 = 1 - val; + comb->damp1 = val; + comb->damp2 = 1 - val; } fluid_real_t -fluid_comb_getdamp(fluid_comb* comb) +fluid_comb_getdamp(fluid_comb *comb) { - return comb->damp1; + return comb->damp1; } void -fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val) +fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val) { - comb->feedback = val; + comb->feedback = val; } fluid_real_t -fluid_comb_getfeedback(fluid_comb* comb) +fluid_comb_getfeedback(fluid_comb *comb) { - return comb->feedback; + return comb->feedback; } #define fluid_comb_process(_comb, _input, _output) \ @@ -219,34 +186,21 @@ fluid_comb_getfeedback(fluid_comb* comb) _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 +/* scale_wet_width is a compensation weight factor to get an output + amplitude (wet) rather independent of the width setting. + 0: the output amplitude is fully dependant on the width setting. + >0: the output amplitude is less dependant on the width setting. + With a scale_wet_width of 0.2 the output amplitude is rather + independent of width setting (see fluid_revmodel_update()). + */ +#define scale_wet_width 0.2f #define scalewet 3.0f #define scaledamp 1.0f #define scaleroom 0.28f #define offsetroom 0.7f -#define initialroom 0.5f -#define initialdamp 0.2f -#define initialwet 1 -#define initialdry 0 -#define initialwidth 1 #define stereospread 23 /* @@ -280,221 +234,255 @@ fluid_comb_getfeedback(fluid_comb* comb) #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]; +struct _fluid_revmodel_t +{ + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t level, 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]; }; -static void fluid_revmodel_update(fluid_revmodel_t* rev); -static void fluid_revmodel_init(fluid_revmodel_t* rev); -void fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate); +static void fluid_revmodel_update(fluid_revmodel_t *rev); +static void fluid_revmodel_init(fluid_revmodel_t *rev); +void fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate); -fluid_revmodel_t* +fluid_revmodel_t * new_fluid_revmodel(fluid_real_t sample_rate) { - fluid_revmodel_t* rev; - rev = FLUID_NEW(fluid_revmodel_t); - if (rev == NULL) { - return NULL; - } - - fluid_set_revmodel_buffers(rev, sample_rate); - - /* 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); - - rev->gain = fixedgain; - fluid_revmodel_set(rev,FLUID_REVMODEL_SET_ALL,initialroom,initialdamp,initialwidth,initialwet); - - return rev; + fluid_revmodel_t *rev; + rev = FLUID_NEW(fluid_revmodel_t); + + if(rev == NULL) + { + return NULL; + } + + fluid_set_revmodel_buffers(rev, sample_rate); + + /* 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); + + rev->gain = fixedgain; + + return rev; } void -delete_fluid_revmodel(fluid_revmodel_t* rev) +delete_fluid_revmodel(fluid_revmodel_t *rev) { - int i; - for (i = 0; i < numcombs;i++) { - fluid_comb_release(&rev->combL[i]); - fluid_comb_release(&rev->combR[i]); - } - for (i = 0; i < numallpasses; i++) { - fluid_allpass_release(&rev->allpassL[i]); - fluid_allpass_release(&rev->allpassR[i]); - } - - FLUID_FREE(rev); + int i; + fluid_return_if_fail(rev != NULL); + + for(i = 0; i < numcombs; i++) + { + fluid_comb_release(&rev->combL[i]); + fluid_comb_release(&rev->combR[i]); + } + + for(i = 0; i < numallpasses; i++) + { + fluid_allpass_release(&rev->allpassL[i]); + fluid_allpass_release(&rev->allpassR[i]); + } + + FLUID_FREE(rev); } void -fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate) { - - float srfactor = sample_rate/44100.0f; - - fluid_comb_setbuffer(&rev->combL[0], combtuningL1*srfactor); - fluid_comb_setbuffer(&rev->combR[0], combtuningR1*srfactor); - fluid_comb_setbuffer(&rev->combL[1], combtuningL2*srfactor); - fluid_comb_setbuffer(&rev->combR[1], combtuningR2*srfactor); - fluid_comb_setbuffer(&rev->combL[2], combtuningL3*srfactor); - fluid_comb_setbuffer(&rev->combR[2], combtuningR3*srfactor); - fluid_comb_setbuffer(&rev->combL[3], combtuningL4*srfactor); - fluid_comb_setbuffer(&rev->combR[3], combtuningR4*srfactor); - fluid_comb_setbuffer(&rev->combL[4], combtuningL5*srfactor); - fluid_comb_setbuffer(&rev->combR[4], combtuningR5*srfactor); - fluid_comb_setbuffer(&rev->combL[5], combtuningL6*srfactor); - fluid_comb_setbuffer(&rev->combR[5], combtuningR6*srfactor); - fluid_comb_setbuffer(&rev->combL[6], combtuningL7*srfactor); - fluid_comb_setbuffer(&rev->combR[6], combtuningR7*srfactor); - fluid_comb_setbuffer(&rev->combL[7], combtuningL8*srfactor); - fluid_comb_setbuffer(&rev->combR[7], combtuningR8*srfactor); - fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1*srfactor); - fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1*srfactor); - fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2*srfactor); - fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2*srfactor); - fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3*srfactor); - fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3*srfactor); - fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4*srfactor); - fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4*srfactor); - - /* Clear all buffers */ - fluid_revmodel_init(rev); +fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate) +{ + + float srfactor = sample_rate / 44100.0f; + + fluid_comb_setbuffer(&rev->combL[0], combtuningL1 * srfactor); + fluid_comb_setbuffer(&rev->combR[0], combtuningR1 * srfactor); + fluid_comb_setbuffer(&rev->combL[1], combtuningL2 * srfactor); + fluid_comb_setbuffer(&rev->combR[1], combtuningR2 * srfactor); + fluid_comb_setbuffer(&rev->combL[2], combtuningL3 * srfactor); + fluid_comb_setbuffer(&rev->combR[2], combtuningR3 * srfactor); + fluid_comb_setbuffer(&rev->combL[3], combtuningL4 * srfactor); + fluid_comb_setbuffer(&rev->combR[3], combtuningR4 * srfactor); + fluid_comb_setbuffer(&rev->combL[4], combtuningL5 * srfactor); + fluid_comb_setbuffer(&rev->combR[4], combtuningR5 * srfactor); + fluid_comb_setbuffer(&rev->combL[5], combtuningL6 * srfactor); + fluid_comb_setbuffer(&rev->combR[5], combtuningR6 * srfactor); + fluid_comb_setbuffer(&rev->combL[6], combtuningL7 * srfactor); + fluid_comb_setbuffer(&rev->combR[6], combtuningR7 * srfactor); + fluid_comb_setbuffer(&rev->combL[7], combtuningL8 * srfactor); + fluid_comb_setbuffer(&rev->combR[7], combtuningR8 * srfactor); + fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1 * srfactor); + fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1 * srfactor); + fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2 * srfactor); + fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2 * srfactor); + fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3 * srfactor); + fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3 * srfactor); + fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4 * srfactor); + fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4 * srfactor); + + /* Clear all buffers */ + fluid_revmodel_init(rev); } static void -fluid_revmodel_init(fluid_revmodel_t* rev) +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]); - } + 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_reset(fluid_revmodel_t *rev) { - fluid_revmodel_init(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) +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.0f * 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); + 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.0f * 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; } - /* 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) +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.0f * 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); + 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.0f * 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; } - - /* 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; - } } static void -fluid_revmodel_update(fluid_revmodel_t* rev) +fluid_revmodel_update(fluid_revmodel_t *rev) { - /* Recalculate internal values after parameter change */ - int i; - - rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f); - rev->wet2 = rev->wet * ((1.0f - rev->width) / 2.0f); - - for (i = 0; i < numcombs; i++) { - fluid_comb_setfeedback(&rev->combL[i], rev->roomsize); - fluid_comb_setfeedback(&rev->combR[i], rev->roomsize); - } + /* Recalculate internal values after parameter change */ + int i; + + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 100). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + rev->width * scale_wet_width) + Actually, with a scale_wet_width of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + fluid_real_t wet = (rev->level * scalewet) / + (1.0f + rev->width * scale_wet_width); + + /* wet1 and wet2 are used by the stereo effect controled by the width setting + for producing a stereo ouptput from a monophonic reverb signal. + Please see the note above about a side effect tendency */ + rev->wet1 = wet * (rev->width / 2.0f + 0.5f); + rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); + + 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); - } + for(i = 0; i < numcombs; i++) + { + fluid_comb_setdamp(&rev->combL[i], rev->damp); + fluid_comb_setdamp(&rev->combR[i], rev->damp); + } } /** @@ -508,37 +496,53 @@ fluid_revmodel_update(fluid_revmodel_t* rev) * @param level Reverb level */ void -fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, - float damping, float width, float level) +fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level) { - if (set & FLUID_REVMODEL_SET_ROOMSIZE) - rev->roomsize = (roomsize * scaleroom) + offsetroom; + if(set & FLUID_REVMODEL_SET_ROOMSIZE) + { + /* With upper limit above 1.07, the output amplitude will grow + exponentially. So, keeping this upper limit to 1.0 seems sufficient + as it produces yet a long reverb time */ + fluid_clip(roomsize, 0.0f, 1.0f); + rev->roomsize = (roomsize * scaleroom) + offsetroom; + } - if (set & FLUID_REVMODEL_SET_DAMPING) - rev->damp = damping * scaledamp; + if(set & FLUID_REVMODEL_SET_DAMPING) + { + rev->damp = damping * scaledamp; + } - if (set & FLUID_REVMODEL_SET_WIDTH) - rev->width = width; + if(set & FLUID_REVMODEL_SET_WIDTH) + { + rev->width = width; + } - if (set & FLUID_REVMODEL_SET_LEVEL) - { - fluid_clip(level, 0.0f, 1.0f); - rev->wet = level * scalewet; - } + if(set & FLUID_REVMODEL_SET_LEVEL) + { + fluid_clip(level, 0.0f, 1.0f); + rev->level = level; + } - fluid_revmodel_update (rev); + fluid_revmodel_update(rev); } void -fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate) { - int i; - for (i = 0; i < numcombs;i++) { - fluid_comb_release(&rev->combL[i]); - fluid_comb_release(&rev->combR[i]); - } - for (i = 0; i < numallpasses; i++) { - fluid_allpass_release(&rev->allpassL[i]); - fluid_allpass_release(&rev->allpassR[i]); - } - fluid_set_revmodel_buffers(rev, sample_rate); +fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) +{ + int i; + + for(i = 0; i < numcombs; i++) + { + fluid_comb_release(&rev->combL[i]); + fluid_comb_release(&rev->combR[i]); + } + + for(i = 0; i < numallpasses; i++) + { + fluid_allpass_release(&rev->allpassL[i]); + fluid_allpass_release(&rev->allpassR[i]); + } + + fluid_set_revmodel_buffers(rev, sample_rate); } diff --git a/libs/fluidsynth/src/fluid_rev.h b/libs/fluidsynth/src/fluid_rev.h index f977352cf5..69c00ea71c 100644 --- a/libs/fluidsynth/src/fluid_rev.h +++ b/libs/fluidsynth/src/fluid_rev.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -30,44 +30,48 @@ typedef struct _fluid_revmodel_t fluid_revmodel_t; /** Flags for fluid_revmodel_set() */ typedef enum { - FLUID_REVMODEL_SET_ROOMSIZE = 1 << 0, - FLUID_REVMODEL_SET_DAMPING = 1 << 1, - FLUID_REVMODEL_SET_WIDTH = 1 << 2, - FLUID_REVMODEL_SET_LEVEL = 1 << 3 + FLUID_REVMODEL_SET_ROOMSIZE = 1 << 0, + FLUID_REVMODEL_SET_DAMPING = 1 << 1, + FLUID_REVMODEL_SET_WIDTH = 1 << 2, + FLUID_REVMODEL_SET_LEVEL = 1 << 3, + + /** Value for fluid_revmodel_set() which sets all reverb parameters. */ + FLUID_REVMODEL_SET_ALL = FLUID_REVMODEL_SET_LEVEL + | FLUID_REVMODEL_SET_WIDTH + | FLUID_REVMODEL_SET_DAMPING + | FLUID_REVMODEL_SET_ROOMSIZE, } fluid_revmodel_set_t; -/** Value for fluid_revmodel_set() which sets all reverb parameters. */ -#define FLUID_REVMODEL_SET_ALL 0x0F - /* * 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; +typedef struct _fluid_revmodel_presets_t +{ + const char *name; + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t width; + fluid_real_t level; } fluid_revmodel_presets_t; /* * reverb */ -fluid_revmodel_t* new_fluid_revmodel(fluid_real_t sample_rate); -void delete_fluid_revmodel(fluid_revmodel_t* rev); +fluid_revmodel_t *new_fluid_revmodel(fluid_real_t sample_rate); +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_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_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_reset(fluid_revmodel_t *rev); -void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, - float damping, float width, float level); +void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level); -void fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate); +void fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate); #endif /* _FLUID_REV_H */ diff --git a/libs/fluidsynth/src/fluid_ringbuffer.c b/libs/fluidsynth/src/fluid_ringbuffer.c index f6c06dd76d..71fd1e48a3 100644 --- a/libs/fluidsynth/src/fluid_ringbuffer.c +++ b/libs/fluidsynth/src/fluid_ringbuffer.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -39,39 +39,39 @@ * only be one producer thread and one consumer thread. */ fluid_ringbuffer_t * -new_fluid_ringbuffer (int count, int elementsize) +new_fluid_ringbuffer(int count, int elementsize) { - fluid_ringbuffer_t *queue; + fluid_ringbuffer_t *queue; - fluid_return_val_if_fail (count > 0, NULL); + fluid_return_val_if_fail(count > 0, NULL); - queue = FLUID_NEW (fluid_ringbuffer_t); + queue = FLUID_NEW(fluid_ringbuffer_t); - if (!queue) - { - FLUID_LOG (FLUID_ERR, "Out of memory"); - return NULL; - } + if(!queue) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } - queue->array = FLUID_MALLOC (elementsize * count); + queue->array = FLUID_MALLOC(elementsize * count); - if (!queue->array) - { - FLUID_FREE (queue); - FLUID_LOG (FLUID_ERR, "Out of memory"); - return NULL; - } + if(!queue->array) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_ringbuffer(queue); + return NULL; + } - /* Clear array, in case dynamic pointer reclaiming is being done */ - FLUID_MEMSET (queue->array, 0, elementsize * count); + /* Clear array, in case dynamic pointer reclaiming is being done */ + FLUID_MEMSET(queue->array, 0, elementsize * count); - queue->totalcount = count; - queue->elementsize = elementsize; - queue->count = 0; - queue->in = 0; - queue->out = 0; + queue->totalcount = count; + queue->elementsize = elementsize; + fluid_atomic_int_set(&queue->count, 0); + queue->in = 0; + queue->out = 0; - return (queue); + return (queue); } /** @@ -82,8 +82,9 @@ new_fluid_ringbuffer (int count, int elementsize) * producer threads will no longer access it. */ void -delete_fluid_ringbuffer (fluid_ringbuffer_t *queue) +delete_fluid_ringbuffer(fluid_ringbuffer_t *queue) { - FLUID_FREE (queue->array); - FLUID_FREE (queue); + fluid_return_if_fail(queue != NULL); + FLUID_FREE(queue->array); + FLUID_FREE(queue); } diff --git a/libs/fluidsynth/src/fluid_ringbuffer.h b/libs/fluidsynth/src/fluid_ringbuffer.h index bd43f8a250..e78d52291b 100644 --- a/libs/fluidsynth/src/fluid_ringbuffer.h +++ b/libs/fluidsynth/src/fluid_ringbuffer.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -23,30 +23,30 @@ #include "fluid_sys.h" -/** +/* * Lockless event queue instance. */ struct _fluid_ringbuffer_t { - char *array; /**< Queue array of arbitrary size elements */ - int totalcount; /**< Total count of elements in array */ - int count; /**< Current count of elements */ - int in; /**< Index in queue to store next pushed element */ - int out; /**< Index in queue of next popped element */ - int elementsize; /**< Size of each element */ - void* userdata; + char *array; /**< Queue array of arbitrary size elements */ + int totalcount; /**< Total count of elements in array */ + fluid_atomic_int_t count; /**< Current count of elements */ + int in; /**< Index in queue to store next pushed element */ + int out; /**< Index in queue of next popped element */ + int elementsize; /**< Size of each element */ + void *userdata; }; typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t; -fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize); -void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue); +fluid_ringbuffer_t *new_fluid_ringbuffer(int count, int elementsize); +void delete_fluid_ringbuffer(fluid_ringbuffer_t *queue); /** * Get pointer to next input array element in queue. * @param queue Lockless queue instance - * @param count Normally zero, or more if you need to push several items at once + * @param offset Normally zero, or more if you need to push several items at once * @return Pointer to array element in queue to store data to or NULL if queue is full * * This function along with fluid_ringbuffer_next_inptr() form a queue "push" @@ -55,11 +55,11 @@ void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue); * if the queue has wrapped around. This can be used to reclaim pointers to * allocated memory, etc. */ -static FLUID_INLINE void* -fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset) +static FLUID_INLINE void * +fluid_ringbuffer_get_inptr(fluid_ringbuffer_t *queue, int offset) { - return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL - : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount); + return fluid_atomic_int_get(&queue->count) + offset >= queue->totalcount ? NULL + : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount); } /** @@ -71,13 +71,16 @@ fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset) * operation and is split into 2 functions to avoid element copy. */ static FLUID_INLINE void -fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count) +fluid_ringbuffer_next_inptr(fluid_ringbuffer_t *queue, int count) { - fluid_atomic_int_add (&queue->count, count); + fluid_atomic_int_add(&queue->count, count); + + queue->in += count; - queue->in += count; - if (queue->in >= queue->totalcount) - queue->in -= queue->totalcount; + if(queue->in >= queue->totalcount) + { + queue->in -= queue->totalcount; + } } /** @@ -86,9 +89,9 @@ fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count) * @return amount of items currently in queue */ static FLUID_INLINE int -fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue) +fluid_ringbuffer_get_count(fluid_ringbuffer_t *queue) { - return fluid_atomic_int_get (&queue->count); + return fluid_atomic_int_get(&queue->count); } @@ -101,11 +104,11 @@ fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue) * This function along with fluid_ringbuffer_next_outptr() form a queue "pop" * operation and is split into 2 functions to avoid an element copy. */ -static FLUID_INLINE void* -fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue) +static FLUID_INLINE void * +fluid_ringbuffer_get_outptr(fluid_ringbuffer_t *queue) { - return fluid_ringbuffer_get_count(queue) == 0 ? NULL - : queue->array + queue->elementsize * queue->out; + return fluid_ringbuffer_get_count(queue) == 0 ? NULL + : queue->array + queue->elementsize * queue->out; } @@ -117,12 +120,14 @@ fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue) * operation and is split into 2 functions to avoid an element copy. */ static FLUID_INLINE void -fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue) +fluid_ringbuffer_next_outptr(fluid_ringbuffer_t *queue) { - fluid_atomic_int_add (&queue->count, -1); + fluid_atomic_int_add(&queue->count, -1); - if (++queue->out == queue->totalcount) - queue->out = 0; + if(++queue->out == queue->totalcount) + { + queue->out = 0; + } } #endif /* _FLUID_ringbuffer_H */ diff --git a/libs/fluidsynth/src/fluid_rvoice.c b/libs/fluidsynth/src/fluid_rvoice.c index ba8da98333..e768711637 100644 --- a/libs/fluidsynth/src/fluid_rvoice.c +++ b/libs/fluidsynth/src/fluid_rvoice.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -22,80 +22,91 @@ #include "fluid_conv.h" #include "fluid_sys.h" + +static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks); + /** * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise */ -static inline int -fluid_rvoice_calc_amp(fluid_rvoice_t* voice) +static FLUID_INLINE int +fluid_rvoice_calc_amp(fluid_rvoice_t *voice) { - fluid_real_t target_amp; /* target amplitude */ - - if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) - return -1; /* The volume amplitude is in hold phase. No sound is produced. */ - - if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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). - */ - target_amp = fluid_atten2amp (voice->dsp.attenuation) - * fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) - * fluid_adsr_env_get_val(&voice->envlfo.volenv); - } - else - { - fluid_real_t amplitude_that_reaches_noise_floor; - fluid_real_t amp_max; - - target_amp = fluid_atten2amp (voice->dsp.attenuation) - * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) - + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); - - /* We turn off a voice, if the volume has dropped low enough. */ - - /* 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. - */ + fluid_real_t target_amp; /* target amplitude */ - /* If the loop amplitude is known, we can use it if the voice loop is within - * the sample loop - */ + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) + { + return -1; /* The volume amplitude is in hold phase. No sound is produced. */ + } - /* Is the playing pointer already in the loop? */ - if (voice->dsp.has_looped) - amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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). + */ + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) + * fluid_adsr_env_get_val(&voice->envlfo.volenv); + } else - amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; - - /* 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->dsp.min_attenuation_cB) * - fluid_adsr_env_get_val(&voice->envlfo.volenv); - - /* 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) { - return 0; + fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amp_max; + + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(FLUID_PEAK_ATTENUATION * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) + + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); + + /* We turn off a voice, if the volume has dropped low enough. */ + + /* 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 + */ + + /* Is the playing pointer already in the loop? */ + if(voice->dsp.has_looped) + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; + } + else + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + } + + /* 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_cb2amp(voice->dsp.min_attenuation_cB) * + fluid_adsr_env_get_val(&voice->envlfo.volenv); + + /* 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) + { + return 0; + } } - } - /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ - voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; + /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ + voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; - fluid_check_fpe ("voice_write amplitude calculation"); + fluid_check_fpe("voice_write amplitude calculation"); - /* no volume and not changing? - No need to process */ - if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) - return -1; + /* no volume and not changing? - No need to process */ + if((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) + { + return -1; + } - return 1; + return 1; } @@ -113,140 +124,164 @@ fluid_rvoice_calc_amp(fluid_rvoice_t* voice) * TODO: Investigate whether this can be moved from rvoice to voice. */ static void -fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice) +fluid_rvoice_check_sample_sanity(fluid_rvoice_t *voice) { - int min_index_nonloop=(int) voice->dsp.sample->start; - int max_index_nonloop=(int) voice->dsp.sample->end; + int min_index_nonloop = (int) voice->dsp.sample->start; + int max_index_nonloop = (int) voice->dsp.sample->end; /* make sure we have enough samples surrounding the loop */ - int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; - int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ + int min_index_loop = (int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; + int max_index_loop = (int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ fluid_check_fpe("voice_check_sample_sanity start"); - if (!voice->dsp.check_sample_sanity_flag){ - return; - } - #if 0 - printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end); - printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend); + printf("Sample from %i to %i\n", voice->dsp.sample->start, voice->dsp.sample->end); + printf("Sample loop from %i %i\n", voice->dsp.sample->loopstart, voice->dsp.sample->loopend); printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end); - printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend); + printf("Playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); #endif /* Keep the start point within the sample data */ - if (voice->dsp.start < min_index_nonloop){ - voice->dsp.start = min_index_nonloop; - } else if (voice->dsp.start > max_index_nonloop){ - voice->dsp.start = max_index_nonloop; + if(voice->dsp.start < min_index_nonloop) + { + voice->dsp.start = min_index_nonloop; + } + else if(voice->dsp.start > max_index_nonloop) + { + voice->dsp.start = max_index_nonloop; } /* Keep the end point within the sample data */ - if (voice->dsp.end < min_index_nonloop){ - voice->dsp.end = min_index_nonloop; - } else if (voice->dsp.end > max_index_nonloop){ - voice->dsp.end = max_index_nonloop; + if(voice->dsp.end < min_index_nonloop) + { + voice->dsp.end = min_index_nonloop; + } + else if(voice->dsp.end > max_index_nonloop) + { + voice->dsp.end = max_index_nonloop; } /* Keep start and end point in the right order */ - if (voice->dsp.start > voice->dsp.end){ - int temp = voice->dsp.start; - voice->dsp.start = voice->dsp.end; - voice->dsp.end = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ + if(voice->dsp.start > voice->dsp.end) + { + int temp = voice->dsp.start; + voice->dsp.start = voice->dsp.end; + voice->dsp.end = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ } /* Zero length? */ - if (voice->dsp.start == voice->dsp.end){ - fluid_rvoice_voiceoff(voice); - return; - } - - if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) - || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) { - /* Keep the loop start point within the sample data */ - if (voice->dsp.loopstart < min_index_loop){ - voice->dsp.loopstart = min_index_loop; - } else if (voice->dsp.loopstart > max_index_loop){ - voice->dsp.loopstart = max_index_loop; - } - - /* Keep the loop end point within the sample data */ - if (voice->dsp.loopend < min_index_loop){ - voice->dsp.loopend = min_index_loop; - } else if (voice->dsp.loopend > max_index_loop){ - voice->dsp.loopend = max_index_loop; - } - - /* Keep loop start and end point in the right order */ - if (voice->dsp.loopstart > voice->dsp.loopend){ - int temp = voice->dsp.loopstart; - voice->dsp.loopstart = voice->dsp.loopend; - voice->dsp.loopend = temp; - /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ - } - - /* Loop too short? Then don't loop. */ - if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){ - voice->dsp.samplemode = 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->dsp.loopstart >= (int)voice->dsp.sample->loopstart - && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){ - /* Is there a valid peak amplitude available for the loop, and can we use it? */ - if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE){ - voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; - } else { - /* Worst case */ - voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop; - }; - }; + if(voice->dsp.start == voice->dsp.end) + { + fluid_rvoice_voiceoff(voice, NULL); + return; + } + + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + /* Keep the loop start point within the sample data */ + if(voice->dsp.loopstart < min_index_loop) + { + voice->dsp.loopstart = min_index_loop; + } + else if(voice->dsp.loopstart > max_index_loop) + { + voice->dsp.loopstart = max_index_loop; + } + + /* Keep the loop end point within the sample data */ + if(voice->dsp.loopend < min_index_loop) + { + voice->dsp.loopend = min_index_loop; + } + else if(voice->dsp.loopend > max_index_loop) + { + voice->dsp.loopend = max_index_loop; + } + + /* Keep loop start and end point in the right order */ + if(voice->dsp.loopstart > voice->dsp.loopend) + { + int temp = voice->dsp.loopstart; + voice->dsp.loopstart = voice->dsp.loopend; + voice->dsp.loopend = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ + } + + /* Loop too short? Then don't loop. */ + if(voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE) + { + voice->dsp.samplemode = 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->dsp.loopstart >= (int)voice->dsp.sample->loopstart + && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend) + { + /* Is there a valid peak amplitude available for the loop, and can we use it? */ + if(voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE) + { + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; + } + else + { + /* Worst case */ + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + }; + }; } /* if sample mode is looped */ /* Run startup specific code (only once, when the voice is started) */ - if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ - if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ - if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) - || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){ - voice->dsp.samplemode = FLUID_UNLOOPED; - } - } - - /* Set the initial phase of the voice (using the result from the - start offset modulators). */ - fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); + if(voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP) + { + if(max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE) + { + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + voice->dsp.samplemode = FLUID_UNLOOPED; + } + } + + /* Set the initial phase of the voice (using the result from the + start offset modulators). */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); } /* if startup */ /* Is this voice run in loop mode, or does it run straight to the end of the waveform data? */ - if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && - (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) - || (voice->dsp.samplemode == 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->dsp.phase); - if (index_in_sample >= voice->dsp.loopend){ - /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ - fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); - } - } -/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ + if(((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && + (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) + || (voice->dsp.samplemode == 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->dsp.phase); + + if(index_in_sample >= voice->dsp.loopend) + { + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); + } + } + + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ /* Sample sanity has been assured. Don't check again, until some sample parameter is changed by modulation. */ - voice->dsp.check_sample_sanity_flag=0; + voice->dsp.check_sample_sanity_flag = 0; #if 0 printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); #endif @@ -259,406 +294,603 @@ fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice) * * @param voice rvoice to synthesize * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) - * @return Count of samples written to dsp_buf. (-1 means voice is currently + * @return Count of samples written to dsp_buf. (-1 means voice is currently * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) * * Panning, reverb and chorus are processed separately. The dsp interpolation - * routine is in (fluid_dsp_float.c). + * routine is in (fluid_rvoice_dsp.c). */ int -fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf) +fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) { - int ticks = voice->envlfo.ticks; - int count; + int ticks = voice->envlfo.ticks; + int count, is_looping; - /******************* sample sanity check **********/ + /******************* sample sanity check **********/ - if (!voice->dsp.sample) - return 0; - if (voice->dsp.check_sample_sanity_flag) - fluid_rvoice_check_sample_sanity(voice); + if(!voice->dsp.sample) + { + return 0; + } - /******************* noteoff check ****************/ + if(voice->dsp.check_sample_sanity_flag) + { + fluid_rvoice_check_sample_sanity(voice); + } - if (voice->envlfo.noteoff_ticks != 0 && - voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) { - fluid_rvoice_noteoff(voice, 0); - } + /******************* noteoff check ****************/ - voice->envlfo.ticks += FLUID_BUFSIZE; + if(voice->envlfo.noteoff_ticks != 0 && + voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) + { + fluid_rvoice_noteoff_LOCAL(voice, 0); + } - /******************* vol env **********************/ + voice->envlfo.ticks += FLUID_BUFSIZE; - fluid_adsr_env_calc(&voice->envlfo.volenv, 1); - fluid_check_fpe ("voice_write vol env"); - if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) - return 0; + /******************* vol env **********************/ - /******************* mod env **********************/ + fluid_adsr_env_calc(&voice->envlfo.volenv, 1); + fluid_check_fpe("voice_write vol env"); - fluid_adsr_env_calc(&voice->envlfo.modenv, 0); - fluid_check_fpe ("voice_write mod env"); + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) + { + return 0; + } - /******************* lfo **********************/ + /******************* mod env **********************/ - fluid_lfo_calc(&voice->envlfo.modlfo, ticks); - fluid_check_fpe ("voice_write mod LFO"); - fluid_lfo_calc(&voice->envlfo.viblfo, ticks); - fluid_check_fpe ("voice_write vib LFO"); + fluid_adsr_env_calc(&voice->envlfo.modenv, 0); + fluid_check_fpe("voice_write mod env"); - /******************* amplitude **********************/ + /******************* lfo **********************/ - count = fluid_rvoice_calc_amp(voice); - if (count <= 0) - return count; + fluid_lfo_calc(&voice->envlfo.modlfo, ticks); + fluid_check_fpe("voice_write mod LFO"); + fluid_lfo_calc(&voice->envlfo.viblfo, ticks); + fluid_check_fpe("voice_write vib LFO"); - /******************* phase **********************/ + /******************* amplitude **********************/ - /* 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.*/ - voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + - fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch - + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch - + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) - / voice->dsp.root_pitch_hz; + count = fluid_rvoice_calc_amp(voice); - fluid_check_fpe ("voice_write phase calculation"); + if(count <= 0) + { + return count; + } - /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ - if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1; + /******************* phase **********************/ + + /* 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.*/ + voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + voice->dsp.pitchoffset + + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) + / voice->dsp.root_pitch_hz; + + /******************* portamento ****************/ + /* pitchoffset is updated if enabled. + Pitchoffset will be added to dsp pitch at next phase calculation time */ + + /* In most cases portamento will be disabled. Thus first verify that portamento is + * enabled before updating pitchoffset and before disabling portamento when necessary, + * in order to keep the performance loss at minimum. + * If the algorithm would first update pitchoffset and then verify if portamento + * needs to be disabled, there would be a significant performance drop on a x87 FPU + */ + if(voice->dsp.pitchinc > 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset > 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + else if(voice->dsp.pitchinc < 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset < 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + + fluid_check_fpe("voice_write phase calculation"); - voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE - || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE - && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); + /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ + if(voice->dsp.phase_incr == 0) + { + voice->dsp.phase_incr = 1; + } - /*********************** 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. */ - voice->dsp.dsp_buf = dsp_buf; + /* voice is currently looping? */ + is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE + || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE + && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); - switch (voice->dsp.interp_method) - { + /*********************** 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. */ + + switch(voice->dsp.interp_method) + { case FLUID_INTERP_NONE: - count = fluid_rvoice_dsp_interpolate_none (&voice->dsp); - break; + count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping); + break; + case FLUID_INTERP_LINEAR: - count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp); - break; + count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping); + break; + case FLUID_INTERP_4THORDER: default: - count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp); - break; + count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping); + break; + case FLUID_INTERP_7THORDER: - count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp); - break; - } - fluid_check_fpe ("voice_write interpolation"); - if (count == 0) - return count; + count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping); + break; + } - /*************** resonant filter ******************/ - fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, - fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + - fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); + fluid_check_fpe("voice_write interpolation"); - fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); + if(count == 0) + { + return count; + } - return count; -} + /*************** resonant filter ******************/ + fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); -static inline fluid_real_t* -get_dest_buf(fluid_rvoice_buffers_t* buffers, int index, - fluid_real_t** dest_bufs, int dest_bufcount) -{ - int j = buffers->bufs[index].mapping; - if (j >= dest_bufcount || j < 0) return NULL; - return dest_bufs[j]; -} + fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); -/** - * Mix data down to buffers - * - * @param buffers Destination buffer(s) - * @param dsp_buf Mono sample source - * @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction) - * @param dest_bufs Array of buffers to mixdown to - * @param dest_bufcount Length of dest_bufs - */ -void -fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, - fluid_real_t* dsp_buf, int samplecount, - fluid_real_t** dest_bufs, int dest_bufcount) -{ - int bufcount = buffers->count; - int i, dsp_i; - if (!samplecount || !bufcount || !dest_bufcount) - return; - - for (i=0; i < bufcount; i++) { - fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); - fluid_real_t* next_buf; - fluid_real_t amp = buffers->bufs[i].amp; - if (buf == NULL || amp == 0.0f) - continue; - - /* Optimization for centered stereo samples - we can save one - multiplication per sample */ - next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount)); - if (next_buf && buffers->bufs[i+1].amp == amp) { - for (dsp_i = 0; dsp_i < samplecount; dsp_i++) { - fluid_real_t samp = amp * dsp_buf[dsp_i]; - buf[dsp_i] += samp; - next_buf[dsp_i] += samp; - } - i++; - } - else { - for (dsp_i = 0; dsp_i < samplecount; dsp_i++) - buf[dsp_i] += amp * dsp_buf[dsp_i]; - } - } + /* additional custom filter - only uses the fixed modulator, no lfos... */ + fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0); + fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count); + + return count; } /** * Initialize buffers up to (and including) bufnum */ static int -fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum) +fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum) { - unsigned int i; + unsigned int i; - if (bufnum < buffers->count) return FLUID_OK; - if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED; + if(bufnum < buffers->count) + { + return FLUID_OK; + } + + if(bufnum >= FLUID_RVOICE_MAX_BUFS) + { + return FLUID_FAILED; + } - for (i = buffers->count; i <= bufnum; i++) { - buffers->bufs[bufnum].amp = 0.0f; - buffers->bufs[bufnum].mapping = i; - } - buffers->count = bufnum+1; - return FLUID_OK; + for(i = buffers->count; i <= bufnum; i++) + { + buffers->bufs[i].amp = 0.0f; + } + + buffers->count = bufnum + 1; + return FLUID_OK; } -void -fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers, - unsigned int bufnum, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp) { - if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) - return; - buffers->bufs[bufnum].amp = value; + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + fluid_real_t value = param[1].real; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].amp = value; } -void -fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, - unsigned int bufnum, int mapping) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping) { - if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) - return; - buffers->bufs[bufnum].mapping = mapping; + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + int mapping = param[1].i; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].mapping = mapping; } -void -fluid_rvoice_reset(fluid_rvoice_t* voice) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset) { - voice->dsp.has_looped = 0; - voice->envlfo.ticks = 0; - voice->envlfo.noteoff_ticks = 0; - voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to + fluid_rvoice_t *voice = obj; + + voice->dsp.has_looped = 0; + voice->envlfo.ticks = 0; + voice->envlfo.noteoff_ticks = 0; + voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to calculate the volume increment during processing */ - /* mod env initialization*/ - fluid_adsr_env_reset(&voice->envlfo.modenv); + /* legato initialization */ + voice->dsp.pitchoffset = 0.0; /* portamento initialization */ + voice->dsp.pitchinc = 0.0; + + /* mod env initialization*/ + fluid_adsr_env_reset(&voice->envlfo.modenv); - /* vol env initialization */ - fluid_adsr_env_reset(&voice->envlfo.volenv); + /* vol env initialization */ + fluid_adsr_env_reset(&voice->envlfo.volenv); - /* Fixme: Retrieve from any other existing - voice on this channel to keep LFOs in - unison? */ - fluid_lfo_reset(&voice->envlfo.viblfo); - fluid_lfo_reset(&voice->envlfo.modlfo); + /* Fixme: Retrieve from any other existing + voice on this channel to keep LFOs in + unison? */ + fluid_lfo_reset(&voice->envlfo.viblfo); + fluid_lfo_reset(&voice->envlfo.modlfo); - /* Clear sample history in filter */ - fluid_iir_filter_reset(&voice->resonant_filter); + /* Clear sample history in filter */ + fluid_iir_filter_reset(&voice->resonant_filter); + fluid_iir_filter_reset(&voice->resonant_custom_filter); - /* Force setting of the phase at the first DSP loop run - * This cannot be done earlier, because it depends on modulators. - [DH] Is that comment really true? */ - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; + /* Force setting of the phase at the first DSP loop run + * This cannot be done earlier, because it depends on modulators. + [DH] Is that comment really true? */ + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; } +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff) +{ + fluid_rvoice_t *rvoice = obj; + unsigned int min_ticks = param[0].i; -void -fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks) + fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks); +} + +static void +fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks) { - if (min_ticks > voice->envlfo.ticks) { - /* Delay noteoff */ - voice->envlfo.noteoff_ticks = min_ticks; - return; - } - voice->envlfo.noteoff_ticks = 0; + if(min_ticks > voice->envlfo.ticks) + { + /* Delay noteoff */ + voice->envlfo.noteoff_ticks = min_ticks; + return; + } - if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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 (fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0){ - fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; - fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * 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); - fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + voice->envlfo.noteoff_ticks = 0; + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) + { + fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; + fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo); + fluid_real_t env_value = - ((-200 * log(amp) / log(10.0) - lfo) / FLUID_PEAK_ATTENUATION - 1); + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + } + } + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); +} + +/** + * skips to Attack section + * + * Updates vol and attack data + * Correction on volume val to achieve equivalent amplitude at noteOn legato + * + * @param voice the synthesis voice to be updated +*/ +static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice) +{ + /* skips to Attack section */ + /* Once in Attack section, current count must be reset, to be sure + that the section will be not be prematurely finished. */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + { + /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ + fluid_env_data_t *env_data; + fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation); + fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation); + voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; + /* Correction on slope direction for Attack section */ + env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; + + if(voice->envlfo.volenv.val <= 1.0f) + { + /* slope attack for legato note needs to be positive from val up to 1 */ + env_data->increment = 1.0f / env_data->count; + env_data->min = -1.0f; + env_data->max = 1.0f; + } + else + { + /* slope attack for legato note needs to be negative: from val down to 1 */ + env_data->increment = -voice->envlfo.volenv.val / env_data->count; + env_data->min = 1.0f; + env_data->max = voice->envlfo.volenv.val; + } + } +} + +/** + * Used by legato Mode : multi_retrigger + * see fluid_synth_noteon_mono_legato_multi_retrigger() + * @param voice the synthesis voice to be updated +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) +{ + fluid_rvoice_t *voice = obj; + int section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + if(section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new + volenv_val to achieve equivalent amplitude during the attack phase + for seamless volume transition. */ + fluid_real_t amp_cb, env_value; + amp_cb = FLUID_PEAK_ATTENUATION * + (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); + env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + /* next, skips to Attack section */ } - } - fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); - fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); + + /* skips to Attack section from any section */ + /* Update vol and attack data */ + fluid_rvoice_local_retrigger_attack(voice); + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + /* Skips from any section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); + /* Actually (v 1.1.6) all sections are linear, so there is no need to + correct val value. However soundfont 2.01/2.4 spec. says that Attack should + be convex (see issue #153 from Christian Collins). In the case Attack + section would be changed to a non linear shape it will be necessary to do + a correction for seamless val transition. Here is the place to do this */ } +/** + * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc + * @param voice rvoice to set portamento. + * @param countinc increment count number. + * @param pitchoffset pitch offset to apply to voice dsp.pitch. + * + * Notes: + * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), + * pitchoffset is accumulated in current dsp pitchoffset. + * 2) And to get constant portamento duration, dsp pitch increment is updated. +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento) +{ + fluid_rvoice_t *voice = obj; + unsigned int countinc = param[0].i; + fluid_real_t pitchoffset = param[1].real; + + if(countinc) + { + voice->dsp.pitchoffset += pitchoffset; + voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc; + } -void -fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value) + /* Then during the voice processing (in fluid_rvoice_write()), + dsp.pitchoffset will be incremented by dsp pitchinc. */ +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate) { - voice->dsp.output_rate = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.output_rate = value; } -void -fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method) { - voice->dsp.interp_method = value; + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.interp_method = value; } -void -fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz) { - voice->dsp.root_pitch_hz = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.root_pitch_hz = value; } -void -fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch) { - voice->dsp.pitch = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.pitch = value; } -void -fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation) { - voice->dsp.attenuation = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.prev_attenuation = voice->dsp.attenuation; + voice->dsp.attenuation = value; } -void -fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB) { - voice->dsp.min_attenuation_cB = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.min_attenuation_cB = value; } -void -fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch) { - voice->envlfo.viblfo_to_pitch = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.viblfo_to_pitch = value; } -void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch) { - voice->envlfo.modlfo_to_pitch = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_pitch = value; } -void -fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol) { - voice->envlfo.modlfo_to_vol = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_vol = value; } -void -fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc) { - voice->envlfo.modlfo_to_fc = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_fc = value; } -void -fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc) { - voice->envlfo.modenv_to_fc = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_fc = value; } -void -fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch) { - voice->envlfo.modenv_to_pitch = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_pitch = value; } -void -fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain) { - voice->dsp.synth_gain = value; + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; - /* 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->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; - voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + voice->dsp.synth_gain = value; + + /* 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->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; + voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void -fluid_rvoice_set_start(fluid_rvoice_t* voice, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start) { - voice->dsp.start = value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.start = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void -fluid_rvoice_set_end(fluid_rvoice_t* voice, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end) { - voice->dsp.end = value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.end = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void -fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart) { - voice->dsp.loopstart = value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopstart = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend) { - voice->dsp.loopend = value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopend = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode) { - voice->dsp.samplemode = value; - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; + fluid_rvoice_t *voice = obj; + enum fluid_loop value = param[0].i; + + voice->dsp.samplemode = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } -void -fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample) { - voice->dsp.sample = value; - if (value) { - voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; - } + fluid_rvoice_t *voice = obj; + fluid_sample_t *value = param[0].ptr; + + voice->dsp.sample = value; + + if(value) + { + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; + } } -void -fluid_rvoice_voiceoff(fluid_rvoice_t* voice) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff) { - fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); - fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); + fluid_rvoice_t *voice = obj; + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); } diff --git a/libs/fluidsynth/src/fluid_rvoice.h b/libs/fluidsynth/src/fluid_rvoice.h index 4566cb1de3..bae3ac9390 100644 --- a/libs/fluidsynth/src/fluid_rvoice.h +++ b/libs/fluidsynth/src/fluid_rvoice.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -36,165 +36,190 @@ typedef struct _fluid_rvoice_t fluid_rvoice_t; /* 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 :) + * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7 + * 1.e-7 * 2 == 2.e-7 :) */ -#define FLUID_NOISE_FLOOR 0.00003 +#define FLUID_NOISE_FLOOR 2.e-7 - -enum fluid_loop { - FLUID_UNLOOPED = 0, - FLUID_LOOP_DURING_RELEASE = 1, - FLUID_NOTUSED = 2, - FLUID_LOOP_UNTIL_RELEASE = 3 +enum fluid_loop +{ + FLUID_UNLOOPED = 0, + FLUID_LOOP_DURING_RELEASE = 1, + FLUID_NOTUSED = 2, + FLUID_LOOP_UNTIL_RELEASE = 3 }; -/** +/* * rvoice ticks-based parameters * These parameters must be updated even if the voice is currently quiet. */ struct _fluid_rvoice_envlfo_t { - /* Note-off minimum length */ - unsigned int ticks; - unsigned int noteoff_ticks; - - /* vol env */ - fluid_adsr_env_t volenv; - - /* mod env */ - fluid_adsr_env_t modenv; - fluid_real_t modenv_to_fc; - fluid_real_t modenv_to_pitch; - - /* mod lfo */ - fluid_lfo_t modlfo; - fluid_real_t modlfo_to_fc; - fluid_real_t modlfo_to_pitch; - fluid_real_t modlfo_to_vol; - - /* vib lfo */ - fluid_lfo_t viblfo; - fluid_real_t viblfo_to_pitch; + /* Note-off minimum length */ + unsigned int ticks; + unsigned int noteoff_ticks; + + /* vol env */ + fluid_adsr_env_t volenv; + + /* mod env */ + fluid_adsr_env_t modenv; + fluid_real_t modenv_to_fc; + fluid_real_t modenv_to_pitch; + + /* mod lfo */ + fluid_lfo_t modlfo; + fluid_real_t modlfo_to_fc; + fluid_real_t modlfo_to_pitch; + fluid_real_t modlfo_to_vol; + + /* vib lfo */ + fluid_lfo_t viblfo; + fluid_real_t viblfo_to_pitch; }; -/** +/* * rvoice parameters needed for dsp interpolation */ struct _fluid_rvoice_dsp_t { - /* interpolation method, as in fluid_interp in fluidsynth.h */ - int interp_method; - fluid_sample_t* sample; - int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters - have to be checked. */ - - /* sample and loop start and end points (offset in sample memory). */ - int start; - int end; - int loopstart; - int loopend; /* Note: first point following the loop (superimposed on loopstart) */ - enum fluid_loop samplemode; - - /* Stuff needed for phase calculations */ - - fluid_real_t pitch; /* the pitch in midicents */ - fluid_real_t root_pitch_hz; - fluid_real_t output_rate; - - /* Stuff needed for amplitude calculations */ - - int has_looped; /* Flag that is set as soon as the first loop is completed. */ - 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 amplitude_that_reaches_noise_floor_nonloop; - fluid_real_t amplitude_that_reaches_noise_floor_loop; - fluid_real_t synth_gain; /* master gain */ + /* interpolation method, as in fluid_interp in fluidsynth.h */ + enum fluid_interp interp_method; + enum fluid_loop samplemode; + + /* Flag that is set as soon as the first loop is completed. */ + char has_looped; + /* Flag that initiates, that sample-related parameters have to be checked. */ + char check_sample_sanity_flag; - /* Dynamic input to the interpolator below */ + fluid_sample_t *sample; - fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ + /* sample and loop start and end points (offset in sample memory). */ + int start; + int end; + int loopstart; + int loopend; /* Note: first point following the loop (superimposed on loopstart) */ - fluid_real_t amp; /* current linear amplitude */ - fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ + /* Stuff needed for portamento calculations */ + fluid_real_t pitchoffset; /* the portamento range in midicents */ + fluid_real_t pitchinc; /* the portamento increment in midicents */ - fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ - fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ - int is_looping; + /* Stuff needed for phase calculations */ + fluid_real_t pitch; /* the pitch in midicents */ + fluid_real_t root_pitch_hz; + fluid_real_t output_rate; + + /* Stuff needed for amplitude calculations */ + + fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t prev_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_multi_retrigger_attack() */ + fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation + * during the lifetime of the voice */ + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; + fluid_real_t synth_gain; /* master gain */ + + /* Dynamic input to the interpolator below */ + + fluid_real_t amp; /* current linear amplitude */ + fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ + + fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ + fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ }; /* Currently left, right, reverb, chorus. To be changed if we ever add surround positioning, or stereo reverb/chorus */ #define FLUID_RVOICE_MAX_BUFS (4) -/** +/* * rvoice mixer-related parameters */ struct _fluid_rvoice_buffers_t { - unsigned int count; /* Number of records in "bufs" */ - struct { - fluid_real_t amp; - int mapping; /* Mapping to mixdown buffer index */ - } bufs[FLUID_RVOICE_MAX_BUFS]; + unsigned int count; /* Number of records in "bufs" */ + struct + { + fluid_real_t amp; + int mapping; /* Mapping to mixdown buffer index */ + } bufs[FLUID_RVOICE_MAX_BUFS]; }; -/** - * Parameters needed to synthesize a voice +/* + * Hard real-time parameters needed to synthesize a voice */ struct _fluid_rvoice_t { - fluid_rvoice_envlfo_t envlfo; - fluid_rvoice_dsp_t dsp; - fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ - fluid_rvoice_buffers_t buffers; + fluid_rvoice_envlfo_t envlfo; + fluid_rvoice_dsp_t dsp; + fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ + fluid_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant filter */ + fluid_rvoice_buffers_t buffers; }; -int fluid_rvoice_write(fluid_rvoice_t* voice, fluid_real_t *dsp_buf); - -void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, - fluid_real_t* dsp_buf, int samplecount, - fluid_real_t** dest_bufs, int dest_bufcount); -void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers, - unsigned int bufnum, fluid_real_t value); -void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, - unsigned int bufnum, int mapping); - -/* Dynamic update functions */ - -void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks); -void fluid_rvoice_voiceoff(fluid_rvoice_t* voice); -void fluid_rvoice_reset(fluid_rvoice_t* voice); -void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t output_rate); -void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int interp_method); -void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t root_pitch_hz); -void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); -void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value); -void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value); -void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value); -void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value); -void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value); -void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value); +int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample); /* defined in fluid_rvoice_dsp.c */ +void fluid_rvoice_dsp_config(void); +int fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); -void fluid_rvoice_dsp_config (void); -int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice); -int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice); -int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice); -int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice); + +/* + * Combines the most significant 16 bit part of a sample with a potentially present + * least sig. 8 bit part in order to create a 24 bit sample. + */ +static FLUID_INLINE int32_t +fluid_rvoice_get_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + /* cast sample to unsigned type, so we can safely shift and bitwise or + * without relying on undefined behaviour (should never happen anyway ofc...) */ + uint32_t msb = (uint32_t)dsp_msb[idx]; + uint8_t lsb = 0U; + + /* most soundfonts have 16 bit samples, assume that it's unlikely we + * experience 24 bit samples here */ + if(FLUID_UNLIKELY(dsp_lsb != NULL)) + { + lsb = (uint8_t)dsp_lsb[idx]; + } + + return (int32_t)((msb << 8) | lsb); +} #endif diff --git a/libs/fluidsynth/src/fluid_rvoice_dsp.c b/libs/fluidsynth/src/fluid_rvoice_dsp.c index df7da5022d..cc162829f6 100644 --- a/libs/fluidsynth/src/fluid_rvoice_dsp.c +++ b/libs/fluidsynth/src/fluid_rvoice_dsp.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -28,7 +28,7 @@ * Interpolates audio data (obtains values between the samples of the original * waveform data). * - * Variables loaded from the voice structure (assigned in fluid_voice_write()): + * Variables loaded from the voice structure (assigned in fluid_rvoice_write()): * - dsp_data: Pointer to the original waveform data * - dsp_phase: The position in the original waveform data. * This has an integer and a fractional part (between samples). @@ -61,122 +61,138 @@ static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7]; /* Initializes interpolation tables */ -void fluid_rvoice_dsp_config (void) +void fluid_rvoice_dsp_config(void) { - int i, i2; - double x, v; - double i_shifted; - - /* 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][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x))); - interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5)); - interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x))); - interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0)); - - interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x); - interp_coeff_linear[i][1] = (fluid_real_t)x; - } - - /* i: Offset in terms of whole samples */ - for (i = 0; i < SINC_INTERP_ORDER; i++) - { /* i2: Offset in terms of fractional samples ('subsamples') */ - for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++) + int i, i2; + double x, v; + double i_shifted; + + /* 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][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5)); + interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0)); + + interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x); + interp_coeff_linear[i][1] = (fluid_real_t)x; + } + + /* i: Offset in terms of whole samples */ + for(i = 0; i < SINC_INTERP_ORDER; i++) { - /* center on middle of table */ - i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) - + (double)i2 / (double)FLUID_INTERP_MAX; - - /* 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)); - } - else v = 1.0; - - sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; + /* i2: Offset in terms of fractional samples ('subsamples') */ + for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++) + { + /* center on middle of table */ + i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) + + (double)i2 / (double)FLUID_INTERP_MAX; + + /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ + if(fabs(i_shifted) > 0.000001) + { + double arg = M_PI * i_shifted; + v = (fluid_real_t)sin(arg) / (arg); + /* Hanning window */ + v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * arg / (fluid_real_t)SINC_INTERP_ORDER)); + } + else + { + v = 1.0; + } + + sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; + } } - } #if 0 - for (i = 0; i < FLUID_INTERP_MAX; i++) - { - printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n", - i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i], - sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]); - } + + for(i = 0; i < FLUID_INTERP_MAX; i++) + { + printf("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n", + i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i], + sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]); + } + #endif - fluid_check_fpe("interpolation table calculation"); + fluid_check_fpe("interpolation table calculation"); +} + +static FLUID_INLINE fluid_real_t +fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx); + return (fluid_real_t)sample; } /* No interpolation. Just take the sample, which is closest to * the playback pointer. Questionable quality, but very * efficient. */ int -fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice) +fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) { - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int end_index; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = voice->is_looping; - - end_index = looping ? voice->loopend - 1 : voice->end; - - while (1) - { - dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ - - /* interpolate sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + end_index = looping ? voice->loopend - 1 : voice->end; + + while(1) { - dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ - dsp_amp += dsp_amp_incr; + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + + /* interpolate sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + dsp_buf[dsp_i] = dsp_amp * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + dsp_amp += dsp_amp_incr; + } + + /* break out if not looping (buffer may not be full) */ + if(!looping) + { + break; + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } } - /* break out if not looping (buffer may not be full) */ - if (!looping) break; - - /* go back to loop start */ - if (dsp_phase_index > end_index) - { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - voice->has_looped = 1; - } + voice->phase = dsp_phase; + voice->amp = dsp_amp; - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - } - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); + return (dsp_i); } /* Straight line interpolation. @@ -184,88 +200,99 @@ fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice) * smaller if end of sample occurs). */ int -fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice) +fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) { - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int end_index; - short int point; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = voice->is_looping; - - /* last index before 2nd interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 1; - - /* 2nd interpolation point to use at end of loop or sample */ - if (looping) point = dsp_data[voice->loopstart]; /* loop start */ - else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */ - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + fluid_real_t point; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 2nd interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 1; + + /* 2nd interpolation point to use at end of loop or sample */ + if(looping) { - coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] - + coeffs[1] * dsp_data[dsp_phase_index+1]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */ } - - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + else { - coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] - + coeffs[1] * point); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; /* increment amplitude */ + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); /* duplicate end for samples no longer looping */ } - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start (if past */ - if (dsp_phase_index > end_index) + while(1) { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - voice->has_looped = 1; + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * point); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; /* increment amplitude */ + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start (if past */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index--; /* set end back to second to last sample point */ } - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index--; /* set end back to second to last sample point */ - } + voice->phase = dsp_phase; + voice->amp = dsp_amp; - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); + return (dsp_i); } /* 4th order (cubic) interpolation. @@ -273,149 +300,158 @@ fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice) * smaller if end of sample occurs). */ int -fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice) +fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) { - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int start_index, end_index; - short int start_point, end_point1, end_point2; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* voice is currently looping? */ - looping = voice->is_looping; - - /* last index before 4th interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 2; - - if (voice->has_looped) /* set start_index and start point if looped or not */ - { - start_index = voice->loopstart; - start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */ - } - else - { - start_index = voice->start; - start_point = dsp_data[voice->start]; /* just duplicate the point */ - } - - /* get points off the end (loop start if looping, duplicate point if end) */ - if (looping) - { - end_point1 = dsp_data[voice->loopstart]; - end_point2 = dsp_data[voice->loopstart + 1]; - } - else - { - end_point1 = dsp_data[voice->end]; - end_point2 = end_point1; - } - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_point, end_point1, end_point2; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 4th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 2; + + if(voice->has_looped) /* set start_index and start point if looped or not */ { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * dsp_data[dsp_phase_index+2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */ } - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + else { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * dsp_data[dsp_phase_index+2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + start_index = voice->start; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */ } - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the 2nd to last point */ - - /* interpolate within 2nd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + /* get points off the end (loop start if looping, duplicate point if end) */ + if(looping) { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * dsp_data[dsp_phase_index+1] - + coeffs[3] * end_point1); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); } - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within the last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + else { - coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; - dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] - + coeffs[1] * dsp_data[dsp_phase_index] - + coeffs[2] * end_point1 - + coeffs[3] * end_point2); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_point2 = end_point1; } - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start */ - if (dsp_phase_index > end_index) + while(1) { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - - if (!voice->has_looped) - { - voice->has_looped = 1; - start_index = voice->loopstart; - start_point = dsp_data[voice->loopend - 1]; - } + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * start_point + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * end_point1); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within the last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * end_point1 + + coeffs[3] * end_point2); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 2; /* set end back to third to last sample point */ } - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; + voice->phase = dsp_phase; + voice->amp = dsp_amp; - end_index -= 2; /* set end back to third to last sample point */ - } - - voice->phase = dsp_phase; - voice->amp = dsp_amp; - - return (dsp_i); + return (dsp_i); } /* 7th order interpolation. @@ -423,253 +459,257 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice) * smaller if end of sample occurs). */ int -fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice) +fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) { - fluid_phase_t dsp_phase = voice->phase; - fluid_phase_t dsp_phase_incr; - short int *dsp_data = voice->sample->data; - fluid_real_t *dsp_buf = voice->dsp_buf; - fluid_real_t dsp_amp = voice->amp; - fluid_real_t dsp_amp_incr = voice->amp_incr; - unsigned int dsp_i = 0; - unsigned int dsp_phase_index; - unsigned int start_index, end_index; - short int start_points[3]; - short int end_points[3]; - fluid_real_t *coeffs; - int looping; - - /* Convert playback "speed" floating point value to phase index/fract */ - fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); - - /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on - * the 4th sample point */ - fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000); - - /* voice is currently looping? */ - looping = voice->is_looping; - - /* last index before 7th interpolation point must be specially handled */ - end_index = (looping ? voice->loopend - 1 : voice->end) - 3; - - if (voice->has_looped) /* set start_index and start point if looped or not */ - { - start_index = voice->loopstart; - start_points[0] = dsp_data[voice->loopend - 1]; - start_points[1] = dsp_data[voice->loopend - 2]; - start_points[2] = dsp_data[voice->loopend - 3]; - } - else - { - start_index = voice->start; - start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */ - start_points[1] = start_points[0]; - start_points[2] = start_points[0]; - } - - /* get the 3 points off the end (loop start if looping, duplicate point if end) */ - if (looping) - { - end_points[0] = dsp_data[voice->loopstart]; - end_points[1] = dsp_data[voice->loopstart + 1]; - end_points[2] = dsp_data[voice->loopstart + 2]; - } - else - { - end_points[0] = dsp_data[voice->end]; - end_points[1] = end_points[0]; - end_points[2] = end_points[0]; - } - - while (1) - { - dsp_phase_index = fluid_phase_index (dsp_phase); - - /* interpolate first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[2] - + coeffs[1] * (fluid_real_t)start_points[1] - + coeffs[2] * (fluid_real_t)start_points[0] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - start_index++; - - /* interpolate 2nd to first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[1] - + coeffs[1] * (fluid_real_t)start_points[0] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - start_index++; - - /* interpolate 3rd to first sample point (start or loop start) if needed */ - for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_points[3], end_points[3]; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on + * the 4th sample point */ + fluid_phase_incr(dsp_phase, (fluid_phase_t)0x80000000); + + /* last index before 7th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 3; + + if(voice->has_looped) /* set start_index and start point if looped or not */ { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)start_points[0] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); } - - start_index -= 2; /* set back to original start index */ - - - /* interpolate the sequence of sample points */ - for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) - { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; - } - - /* break out if buffer filled */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index++; /* we're now interpolating the 3rd to last point */ - - /* interpolate within 3rd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + else { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] - + coeffs[6] * (fluid_real_t)end_points[0]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + start_index = voice->start; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the start point */ + start_points[1] = start_points[0]; + start_points[2] = start_points[0]; } - end_index++; /* we're now interpolating the 2nd to last point */ - - /* interpolate within 2nd to last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + /* get the 3 points off the end (loop start if looping, duplicate point if end) */ + if(looping) { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] - + coeffs[5] * (fluid_real_t)end_points[0] - + coeffs[6] * (fluid_real_t)end_points[1]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); + end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2); } - - end_index++; /* we're now interpolating the last point */ - - /* interpolate within last point */ - for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + else { - coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; - - dsp_buf[dsp_i] = dsp_amp - * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] - + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] - + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] - + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] - + coeffs[4] * (fluid_real_t)end_points[0] - + coeffs[5] * (fluid_real_t)end_points[1] - + coeffs[6] * (fluid_real_t)end_points[2]); - - /* increment phase and amplitude */ - fluid_phase_incr (dsp_phase, dsp_phase_incr); - dsp_phase_index = fluid_phase_index (dsp_phase); - dsp_amp += dsp_amp_incr; + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_points[1] = end_points[0]; + end_points[2] = end_points[0]; } - if (!looping) break; /* break out if not looping (end of sample) */ - - /* go back to loop start */ - if (dsp_phase_index > end_index) + while(1) { - fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); - - if (!voice->has_looped) - { - voice->has_looped = 1; - start_index = voice->loopstart; - start_points[0] = dsp_data[voice->loopend - 1]; - start_points[1] = dsp_data[voice->loopend - 2]; - start_points[2] = dsp_data[voice->loopend - 3]; - } + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[2] + + coeffs[1] * start_points[1] + + coeffs[2] * start_points[0] + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 2nd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[1] + + coeffs[1] * start_points[0] + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 3rd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[0] + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index -= 2; /* set back to original start index */ + + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 3rd to last point */ + + /* interpolate within 3rd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * end_points[0]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * end_points[0] + + coeffs[6] * end_points[1]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * end_points[0] + + coeffs[5] * end_points[1] + + coeffs[6] * end_points[2]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 3; /* set end back to 4th to last sample point */ } - /* break out if filled buffer */ - if (dsp_i >= FLUID_BUFSIZE) break; - - end_index -= 3; /* set end back to 4th to last sample point */ - } - - /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on - * the 4th sample point (correct back to real value) */ - fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000); + /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on + * the 4th sample point (correct back to real value) */ + fluid_phase_decr(dsp_phase, (fluid_phase_t)0x80000000); - voice->phase = dsp_phase; - voice->amp = dsp_amp; + voice->phase = dsp_phase; + voice->amp = dsp_amp; - return (dsp_i); + return (dsp_i); } diff --git a/libs/fluidsynth/src/fluid_rvoice_event.c b/libs/fluidsynth/src/fluid_rvoice_event.c index 65edb9dacb..4a513778bd 100644 --- a/libs/fluidsynth/src/fluid_rvoice_event.c +++ b/libs/fluidsynth/src/fluid_rvoice_event.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -25,242 +25,144 @@ #include "fluid_lfo.h" #include "fluid_adsr_env.h" -#define EVENTFUNC_0(proc, type) \ - if (event->method == proc) { \ - proc((type) event->object); \ - return; } - -#define EVENTFUNC_R1(proc, type) \ - if (event->method == proc) { \ - if(event->intparam != 0) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \ - proc((type) event->object, event->realparams[0]); \ - return; } - -#define EVENTFUNC_PTR(proc, type, type2) \ - if (event->method == proc) { \ - proc((type) event->object, (type2) event->ptr); \ - return; } - -#define EVENTFUNC_I1(proc, type) \ - if (event->method == proc) { \ - if(event->realparams[0] != 0.0f) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \ - proc((type) event->object, event->intparam); \ - return; } - -#define EVENTFUNC_IR(proc, type) \ - if (event->method == proc) { \ - proc((type) event->object, event->intparam, event->realparams[0]); \ - return; } - -#define EVENTFUNC_ALL(proc, type) \ - if (event->method == proc) { \ - proc((type) event->object, event->intparam, event->realparams[0], \ - event->realparams[1], event->realparams[2], event->realparams[3], \ - event->realparams[4]); \ - return; } - -#define EVENTFUNC_R4(proc, type) \ - if (event->method == proc) { \ - proc((type) event->object, event->intparam, event->realparams[0], \ - event->realparams[1], event->realparams[2], event->realparams[3]); \ - return; } +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event); -void -fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event) +static FLUID_INLINE void +fluid_rvoice_event_dispatch(fluid_rvoice_event_t *event) { - EVENTFUNC_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*); - EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*); - EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*); - - EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*); - - EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*); - EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*); - - EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*); - EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*); - - EVENTFUNC_IR(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*); - EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*); - - EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_output_rate, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_root_pitch_hz, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_synth_gain, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_pitch, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_attenuation, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_min_attenuation_cB, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_viblfo_to_pitch, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_pitch, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_vol, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_fc, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_modenv_to_fc, fluid_rvoice_t*); - EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_interp_method, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_start, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_end, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_loopstart, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_loopend, fluid_rvoice_t*); - EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*); - EVENTFUNC_PTR(fluid_rvoice_set_sample, fluid_rvoice_t*, fluid_sample_t*); - - EVENTFUNC_R1(fluid_rvoice_mixer_set_samplerate, fluid_rvoice_mixer_t*); - EVENTFUNC_I1(fluid_rvoice_mixer_set_polyphony, fluid_rvoice_mixer_t*); - EVENTFUNC_I1(fluid_rvoice_mixer_set_reverb_enabled, fluid_rvoice_mixer_t*); - EVENTFUNC_I1(fluid_rvoice_mixer_set_chorus_enabled, fluid_rvoice_mixer_t*); - EVENTFUNC_I1(fluid_rvoice_mixer_set_mix_fx, fluid_rvoice_mixer_t*); - EVENTFUNC_0(fluid_rvoice_mixer_reset_fx, fluid_rvoice_mixer_t*); - EVENTFUNC_0(fluid_rvoice_mixer_reset_reverb, fluid_rvoice_mixer_t*); - EVENTFUNC_0(fluid_rvoice_mixer_reset_chorus, fluid_rvoice_mixer_t*); - EVENTFUNC_IR(fluid_rvoice_mixer_set_threads, fluid_rvoice_mixer_t*); - - EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*); - EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*); - - FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method); + event->method(event->object, event->param); } /** * In order to be able to push more than one event atomically, - * use push for all events, then use flush to commit them to the + * use push for all events, then use flush to commit them to the * queue. If threadsafe is false, all events are processed immediately. */ int -fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, int intparam, - fluid_real_t realparam) +fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam) { - fluid_rvoice_event_t* event; - fluid_rvoice_event_t local_event; - event = handler->is_threadsafe ? - fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event; - - if (event == NULL) { - FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); - return FLUID_FAILED; // Buffer full... - } - - event->method = method; - event->object = object; - event->intparam = intparam; - event->realparams[0] = realparam; - if (handler->is_threadsafe) - handler->queue_stored++; - else - fluid_rvoice_event_dispatch(event); - return FLUID_OK; -} + fluid_rvoice_event_t local_event; + local_event.method = method; + local_event.object = object; + local_event.param[0].i = intparam; + local_event.param[1].real = realparam; -int -fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, void* ptr) + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +int +fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) { - fluid_rvoice_event_t* event; - fluid_rvoice_event_t local_event; - event = handler->is_threadsafe ? - fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event; - - if (event == NULL) { - FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); - return FLUID_FAILED; // Buffer full... - } - - event->method = method; - event->object = object; - event->ptr = ptr; - if (handler->is_threadsafe) - handler->queue_stored++; - else - fluid_rvoice_event_dispatch(event); - return FLUID_OK; + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + FLUID_MEMCPY(&local_event.param, param, sizeof(*param) * MAX_EVENT_PARAMS); + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } +int +fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + local_event.param[0].ptr = ptr; -int -fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, int intparam, - fluid_real_t r1, fluid_real_t r2, - fluid_real_t r3, fluid_real_t r4, fluid_real_t r5) + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event) { - fluid_rvoice_event_t* event; - fluid_rvoice_event_t local_event; - event = handler->is_threadsafe ? - fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event; - - if (event == NULL) { - FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); - return FLUID_FAILED; // Buffer full... - } - - event->method = method; - event->object = object; - event->intparam = intparam; - event->realparams[0] = r1; - event->realparams[1] = r2; - event->realparams[2] = r3; - event->realparams[3] = r4; - event->realparams[4] = r5; - if (handler->is_threadsafe) - handler->queue_stored++; - else - fluid_rvoice_event_dispatch(event); - return FLUID_OK; + fluid_rvoice_event_t *event; + int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1); + + event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored); + + if(event == NULL) + { + fluid_atomic_int_add(&handler->queue_stored, -1); + FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); + return FLUID_FAILED; // Buffer full... + } + + FLUID_MEMCPY(event, src_event, sizeof(*event)); + + return FLUID_OK; } -static void -finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice) +void +fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, fluid_rvoice_t *rvoice) { - fluid_rvoice_eventhandler_t* eventhandler = userdata; - fluid_rvoice_t** vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0); - if (vptr == NULL) - return; // Buffer full - *vptr = rvoice; - fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1); + fluid_rvoice_t **vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0); + + if(vptr == NULL) + { + return; // Buffer full + } + + *vptr = rvoice; + fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1); } -fluid_rvoice_eventhandler_t* -new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize, - int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate) +fluid_rvoice_eventhandler_t * +new_fluid_rvoice_eventhandler(int queuesize, + int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate, int extra_threads, int prio) { - fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); - if (eventhandler == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - eventhandler->mixer = NULL; - eventhandler->queue = NULL; - eventhandler->finished_voices = NULL; - eventhandler->is_threadsafe = is_threadsafe; - eventhandler->queue_stored = 0; - - eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size, - sizeof(fluid_rvoice_t*)); - if (eventhandler->finished_voices == NULL) - goto error_recovery; - - eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t)); - if (eventhandler->queue == NULL) - goto error_recovery; - - eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, sample_rate); - if (eventhandler->mixer == NULL) - goto error_recovery; - fluid_rvoice_mixer_set_finished_voices_callback(eventhandler->mixer, - finished_voice_callback, eventhandler); - return eventhandler; - + fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); + + if(eventhandler == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + eventhandler->mixer = NULL; + eventhandler->queue = NULL; + eventhandler->finished_voices = NULL; + + fluid_atomic_int_set(&eventhandler->queue_stored, 0); + + eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size, + sizeof(fluid_rvoice_t *)); + + if(eventhandler->finished_voices == NULL) + { + goto error_recovery; + } + + eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t)); + + if(eventhandler->queue == NULL) + { + goto error_recovery; + } + + eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, sample_rate, eventhandler, extra_threads, prio); + + if(eventhandler->mixer == NULL) + { + goto error_recovery; + } + + return eventhandler; + error_recovery: - delete_fluid_rvoice_eventhandler(eventhandler); - return NULL; + delete_fluid_rvoice_eventhandler(eventhandler); + return NULL; } -int -fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler) +int +fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *handler) { - return fluid_ringbuffer_get_count(handler->queue); + return fluid_ringbuffer_get_count(handler->queue); } @@ -268,26 +170,30 @@ fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler) * Call fluid_rvoice_event_dispatch for all events in queue * @return number of events dispatched */ -int -fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler) +int +fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *handler) { - fluid_rvoice_event_t* event; - int result = 0; - while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) { - fluid_rvoice_event_dispatch(event); - result++; - fluid_ringbuffer_next_outptr(handler->queue); - } - return result; + fluid_rvoice_event_t *event; + int result = 0; + + while(NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) + { + fluid_rvoice_event_dispatch(event); + result++; + fluid_ringbuffer_next_outptr(handler->queue); + } + + return result; } -void -delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler) +void +delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *handler) { - if (handler == NULL) return; - delete_fluid_rvoice_mixer(handler->mixer); - delete_fluid_ringbuffer(handler->queue); - delete_fluid_ringbuffer(handler->finished_voices); - FLUID_FREE(handler); + fluid_return_if_fail(handler != NULL); + + delete_fluid_rvoice_mixer(handler->mixer); + delete_fluid_ringbuffer(handler->queue); + delete_fluid_ringbuffer(handler->finished_voices); + FLUID_FREE(handler); } diff --git a/libs/fluidsynth/src/fluid_rvoice_event.h b/libs/fluidsynth/src/fluid_rvoice_event.h index e8693bcd21..d1fd8d62cb 100644 --- a/libs/fluidsynth/src/fluid_rvoice_event.h +++ b/libs/fluidsynth/src/fluid_rvoice_event.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -26,88 +26,87 @@ #include "fluid_rvoice_mixer.h" #include "fluid_ringbuffer.h" -#define EVENT_REAL_PARAMS (5) - typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t; -typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; - -struct _fluid_rvoice_event_t { - void* method; - void* object; - void* ptr; - int intparam; - fluid_real_t realparams[EVENT_REAL_PARAMS]; -}; - -void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event); +struct _fluid_rvoice_event_t +{ + fluid_rvoice_function_t method; + void *object; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; +}; -/** - * Bridge between the renderer thread and the midi state thread. - * If is_threadsafe is true, that means fluid_rvoice_eventhandler_fetch_all - * can be called in parallell with fluid_rvoice_eventhandler_push/flush +/* + * Bridge between the renderer thread and the midi state thread. + * fluid_rvoice_eventhandler_fetch_all() can be called in parallell + * with fluid_rvoice_eventhandler_push/flush() */ -struct _fluid_rvoice_eventhandler_t { - int is_threadsafe; /* False for optimal performance, true for atomic operations */ - fluid_ringbuffer_t* queue; /**< List of fluid_rvoice_event_t */ - int queue_stored; /**< Extras pushed but not flushed */ - fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */ - fluid_rvoice_mixer_t* mixer; +struct _fluid_rvoice_eventhandler_t +{ + fluid_ringbuffer_t *queue; /**< List of fluid_rvoice_event_t */ + fluid_atomic_int_t queue_stored; /**< Extras pushed but not flushed */ + fluid_ringbuffer_t *finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */ + fluid_rvoice_mixer_t *mixer; }; -fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler( - int is_threadsafe, int queuesize, int finished_voices_size, int bufs, - int fx_bufs, fluid_real_t sample_rate); +fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler( + int queuesize, int finished_voices_size, int bufs, + int fx_bufs, int fx_units, fluid_real_t sample_rate, int, int); -void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*); +void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *); -int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t*); -int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t*); +int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *); +int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *); +void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, + fluid_rvoice_t *rvoice); -static FLUID_INLINE void -fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t* handler) +static FLUID_INLINE void +fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t *handler) { - if (handler->queue_stored > 0) { - fluid_ringbuffer_next_inptr(handler->queue, handler->queue_stored); - handler->queue_stored = 0; - } + int queue_stored = fluid_atomic_int_get(&handler->queue_stored); + + if(queue_stored > 0) + { + fluid_atomic_int_set(&handler->queue_stored, 0); + fluid_ringbuffer_next_inptr(handler->queue, queue_stored); + } } /** * @return next finished voice, or NULL if nothing in queue */ -static FLUID_INLINE fluid_rvoice_t* -fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t* handler) +static FLUID_INLINE fluid_rvoice_t * +fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t *handler) { - void* result = fluid_ringbuffer_get_outptr(handler->finished_voices); - if (result == NULL) return NULL; - result = * (fluid_rvoice_t**) result; - fluid_ringbuffer_next_outptr(handler->finished_voices); - return result; + void *result = fluid_ringbuffer_get_outptr(handler->finished_voices); + + if(result == NULL) + { + return NULL; + } + + result = * (fluid_rvoice_t **) result; + fluid_ringbuffer_next_outptr(handler->finished_voices); + return result; } -int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, int intparam, - fluid_real_t realparam); +int fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam); -int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, void* ptr); +int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr); -int fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler, - void* method, void* object, int intparam, - fluid_real_t r1, fluid_real_t r2, - fluid_real_t r3, fluid_real_t r4, fluid_real_t r5); +int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); static FLUID_INLINE void -fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler, - fluid_rvoice_t* rvoice) +fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_t *rvoice) { - if (handler->is_threadsafe) fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice, handler->mixer, rvoice); - else - fluid_rvoice_mixer_add_voice(handler->mixer, rvoice); } diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c index d5369aacce..8c5254f269 100644 --- a/libs/fluidsynth/src/fluid_rvoice_mixer.c +++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,10 +24,7 @@ #include "fluid_rev.h" #include "fluid_chorus.h" #include "fluidsynth_priv.h" -//#include "fluid_ladspa.h" - -#define SYNTH_REVERB_CHANNEL 0 -#define SYNTH_CHORUS_CHANNEL 1 +#include "fluid_synth.h" #undef ENABLE_MIXER_THREADS // Ardour does the multithreading -- synth.cpu-cores defaults to 1 @@ -37,492 +34,651 @@ typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t; -struct _fluid_mixer_buffers_t { - fluid_rvoice_mixer_t* mixer; /**< Owner of object */ -#ifdef ENABLE_MIXER_THREADS - fluid_thread_t* thread; /**< Thread object */ +struct _fluid_mixer_buffers_t +{ + fluid_rvoice_mixer_t *mixer; /**< Owner of object */ +#if ENABLE_MIXER_THREADS + fluid_thread_t *thread; /**< Thread object */ #endif - fluid_rvoice_t** finished_voices; /* List of voices who have finished */ - int finished_voice_count; - - int ready; /**< Atomic: buffers are ready for mixing */ - - int buf_blocks; /**< Number of blocks allocated in the buffers */ - - int buf_count; - fluid_real_t** left_buf; - fluid_real_t** right_buf; - - int fx_buf_count; - fluid_real_t** fx_left_buf; - fluid_real_t** fx_right_buf; + fluid_rvoice_t **finished_voices; /* List of voices who have finished */ + int finished_voice_count; + + fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ + + fluid_real_t *local_buf; + + int buf_count; + int fx_buf_count; + + /** buffer to store the left part of a stereo channel to. + * Specifically a two dimensional array, containing \c buf_count sample buffers + * (i.e. for each synth.audio-channels), of which each contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT + * boundary provided that this pointer points to an aligned buffer. + * So make sure to access the sample buffer by first aligning this + * pointer using fluid_align_ptr() + */ + fluid_real_t *left_buf; + + /** dito, but for right part of a stereo channel */ + fluid_real_t *right_buf; + + /** buffer to store the left part of a stereo effects channel to. + * Specifically a two dimensional array, containing \c fx_buf_count buffers + * (i.e. for each synth.effects-channels), of which each buffer contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + */ + fluid_real_t *fx_left_buf; + fluid_real_t *fx_right_buf; }; typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t; -struct _fluid_mixer_fx_t { - fluid_revmodel_t* reverb; /**< Reverb unit */ - fluid_chorus_t* chorus; /**< Chorus unit */ - int with_reverb; /**< Should the synth use the built-in reverb unit? */ - int with_chorus; /**< Should the synth use the built-in chorus unit? */ - int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */ +struct _fluid_mixer_fx_t +{ + fluid_revmodel_t *reverb; /**< Reverb unit */ + fluid_chorus_t *chorus; /**< Chorus unit */ }; -struct _fluid_rvoice_mixer_t { - fluid_mixer_fx_t fx; +struct _fluid_rvoice_mixer_t +{ + fluid_mixer_fx_t *fx; - fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ - void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Used by mixer only: Receive this callback every time a voice is removed */ - void* remove_voice_callback_userdata; + fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ + fluid_rvoice_eventhandler_t *eventhandler; - fluid_rvoice_t** rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */ - int polyphony; /**< Read-only: Length of voices array */ - int active_voices; /**< Read-only: Number of non-null voices */ - int current_blockcount; /**< Read-only: how many blocks to process this time */ + fluid_rvoice_t **rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */ + int polyphony; /**< Read-only: Length of voices array */ + int active_voices; /**< Read-only: Number of non-null voices */ + int current_blockcount; /**< Read-only: how many blocks to process this time */ + int fx_units; + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ + fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ #endif -#ifdef ENABLE_MIXER_THREADS +#if ENABLE_MIXER_THREADS // int sleeping_threads; /**< Atomic: number of threads currently asleep */ // int active_threads; /**< Atomic: number of threads in the thread loop */ - int threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ - int current_rvoice; /**< Atomic: for the threads to know next voice to */ - fluid_cond_t* wakeup_threads; /**< Signalled when the threads should wake up */ - fluid_cond_mutex_t* wakeup_threads_m; /**< wakeup_threads mutex companion */ - fluid_cond_t* thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */ - fluid_cond_mutex_t* thread_ready_m; /**< thread_ready mutex companion */ - - int thread_count; /**< Number of extra mixer threads for multi-core rendering */ - fluid_mixer_buffers_t* threads; /**< Array of mixer threads (thread_count in length) */ + fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ + fluid_atomic_int_t current_rvoice; /**< Atomic: for the threads to know next voice to */ + fluid_cond_t *wakeup_threads; /**< Signalled when the threads should wake up */ + fluid_cond_mutex_t *wakeup_threads_m; /**< wakeup_threads mutex companion */ + fluid_cond_t *thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */ + fluid_cond_mutex_t *thread_ready_m; /**< thread_ready mutex companion */ + + int thread_count; /**< Number of extra mixer threads for multi-core rendering */ + fluid_mixer_buffers_t *threads; /**< Array of mixer threads (thread_count in length) */ #endif }; -static FLUID_INLINE void -fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer) -{ - int i; - fluid_profile_ref_var(prof_ref); - if (mixer->fx.with_reverb) { - if (mixer->fx.mix_fx_to_out) { - for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) - fluid_revmodel_processmix(mixer->fx.reverb, - &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], - &mixer->buffers.left_buf[0][i], - &mixer->buffers.right_buf[0][i]); - } - else { - for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) - fluid_revmodel_processreplace(mixer->fx.reverb, - &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], - &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], - &mixer->buffers.fx_right_buf[SYNTH_REVERB_CHANNEL][i]); - } - fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref); - } - - if (mixer->fx.with_chorus) { - if (mixer->fx.mix_fx_to_out) { - for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) - fluid_chorus_processmix(mixer->fx.chorus, - &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], - &mixer->buffers.left_buf[0][i], - &mixer->buffers.right_buf[0][i]); - } - else { - for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) - fluid_chorus_processreplace(mixer->fx.chorus, - &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], - &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], - &mixer->buffers.fx_right_buf[SYNTH_CHORUS_CHANNEL][i]); - } - fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref); - } - -#ifdef LADSPA - /* Run the signal through the LADSPA Fx unit */ - if (mixer->LADSPA_FxUnit) { - int j; - FLUID_DECLARE_VLA(fluid_real_t*, left_buf, mixer->buffers.buf_count); - FLUID_DECLARE_VLA(fluid_real_t*, right_buf, mixer->buffers.buf_count); - FLUID_DECLARE_VLA(fluid_real_t*, fx_left_buf, mixer->buffers.fx_buf_count); - FLUID_DECLARE_VLA(fluid_real_t*, fx_right_buf, mixer->buffers.fx_buf_count); - for (j=0; j < mixer->buffers.buf_count; j++) { - left_buf[j] = mixer->buffers.left_buf[j]; - right_buf[j] = mixer->buffers.right_buf[j]; - } - for (j=0; j < mixer->buffers.fx_buf_count; j++) { - fx_left_buf[j] = mixer->buffers.fx_left_buf[j]; - fx_right_buf[j] = mixer->buffers.fx_right_buf[j]; - } - for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) { - fluid_LADSPA_run(mixer->LADSPA_FxUnit, left_buf, right_buf, fx_left_buf, - fx_right_buf); - for (j=0; j < mixer->buffers.buf_count; j++) { - left_buf[j] += FLUID_BUFSIZE; - right_buf[j] += FLUID_BUFSIZE; - } - for (j=0; j < mixer->buffers.fx_buf_count; j++) { - fx_left_buf[j] += FLUID_BUFSIZE; - fx_right_buf[j] += FLUID_BUFSIZE; - } - } - fluid_check_fpe("LADSPA"); - } +#if ENABLE_MIXER_THREADS +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer); +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level); #endif -} -/** - * During rendering, rvoices might be finished. Set this callback - * for getting a callback any time the rvoice is finished. - */ -void fluid_rvoice_mixer_set_finished_voices_callback( - fluid_rvoice_mixer_t* mixer, - void (*func)(void*, fluid_rvoice_t*), - void* userdata) +static FLUID_INLINE void +fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount) { - mixer->remove_voice_callback_userdata = userdata; - mixer->remove_voice_callback = func; -} + const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; + int i, f; + void (*reverb_process_func)(fluid_revmodel_t *rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + void (*chorus_process_func)(fluid_chorus_t *chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r; -/** - * Synthesize one voice and add to buffer. - * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means - * voice has been finished, removed and possibly replaced with another voice. - * @return Number of samples written - */ -static int -fluid_mix_one(fluid_rvoice_t* rvoice, fluid_real_t** bufs, unsigned int bufcount, int blockcount) -{ - int i, result = 0; + // all dry unprocessed mono input is stored in the left channel + fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *in_ch = in_rev; + + fluid_profile_ref_var(prof_ref); - FLUID_DECLARE_VLA(fluid_real_t, local_buf, FLUID_BUFSIZE*blockcount); - for (i=0; i < blockcount; i++) { - int s = fluid_rvoice_write(rvoice, &local_buf[FLUID_BUFSIZE*i]); - if (s == -1) { - s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */ - FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t)); - } - result += s; - if (s < FLUID_BUFSIZE) { - break; + if(mixer->mix_fx_to_out) + { + // mix effects to first stereo channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processmix; + chorus_process_func = fluid_chorus_processmix; + + } + else + { + // replace effects into respective stereo effects channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processreplace; + chorus_process_func = fluid_chorus_processreplace; + } + + + if(mixer->with_reverb) + { + for(f = 0; f < mixer->fx_units; f++) + { + int buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; + + for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) + { + int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i; + + reverb_process_func(mixer->fx[f].reverb, + &in_rev[samp_idx], + mixer->mix_fx_to_out ? &out_rev_l[i] : &out_rev_l[samp_idx], + mixer->mix_fx_to_out ? &out_rev_r[i] : &out_rev_r[samp_idx]); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + + if(mixer->with_chorus) + { + for(f = 0; f < mixer->fx_units; f++) + { + int buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; + + for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) + { + int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i; + + chorus_process_func(mixer->fx[f].chorus, + &in_ch [samp_idx], + mixer->mix_fx_to_out ? &out_ch_l[i] : &out_ch_l[samp_idx], + mixer->mix_fx_to_out ? &out_ch_r[i] : &out_ch_r[samp_idx]); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + +#ifdef LADSPA + + /* Run the signal through the LADSPA Fx unit. The buffers have already been + * set up in fluid_rvoice_mixer_set_ladspa. */ + if(mixer->ladspa_fx) + { + fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE); + fluid_check_fpe("LADSPA"); } - } - fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount); - return result; +#endif } /** * Glue to get fluid_rvoice_buffers_mix what it wants * Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling */ -static FLUID_INLINE int -fluid_mixer_buffers_prepare(fluid_mixer_buffers_t* buffers, fluid_real_t** outbufs) -{ - fluid_real_t* reverb_buf, *chorus_buf; - int i; - - /* 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. */ - reverb_buf = buffers->mixer->fx.with_reverb ? buffers->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL; - chorus_buf = buffers->mixer->fx.with_chorus ? buffers->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL; - outbufs[buffers->buf_count*2 + SYNTH_REVERB_CHANNEL] = reverb_buf; - outbufs[buffers->buf_count*2 + SYNTH_CHORUS_CHANNEL] = chorus_buf; - - /* 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. - */ - - for (i = 0; i < buffers->buf_count; i++) { - outbufs[i*2] = buffers->left_buf[i]; - outbufs[i*2+1] = buffers->right_buf[i]; - } - return buffers->buf_count*2 + 2; +static FLUID_INLINE int +fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs) +{ + fluid_real_t *base_ptr; + int i; + const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units; + const int offset = buffers->buf_count * 2; + int with_reverb = buffers->mixer->with_reverb; + int with_chorus = buffers->mixer->with_chorus; + + /* Set up the reverb and chorus buffers only when the effect is enabled or + * when LADSPA is active. Nonexisting buffers are detected in the DSP loop. + * Not sending the effect signals saves some time in that case. */ +#ifdef LADSPA + int with_ladspa = (buffers->mixer->ladspa_fx != NULL); + with_reverb = (with_reverb | with_ladspa); + with_chorus = (with_chorus | with_ladspa); +#endif + + // all the dry, non-processed mono audio for effects is to be stored in the left buffers + base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->mixer->fx_units; i++) + { + int fx_idx = i * fx_channels_per_unit; + + outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] = + (with_reverb) + ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + + outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] = + (with_chorus) + ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + } + + /* 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. + */ + base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + return offset + buffers->fx_buf_count; } static FLUID_INLINE void -fluid_finish_rvoice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* rvoice) -{ - if (buffers->finished_voice_count < buffers->mixer->polyphony) - buffers->finished_voices[buffers->finished_voice_count++] = rvoice; - else - FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony"); -} - -static void -fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t* buffers) -{ - int i,j; - for (i=0; i < buffers->finished_voice_count; i++) { - fluid_rvoice_t* v = buffers->finished_voices[i]; - int* av = &buffers->mixer->active_voices; - for (j=0; j < *av; j++) { - if (v == buffers->mixer->rvoices[j]) { - (*av)--; - /* Pack the array */ - if (j < *av) - buffers->mixer->rvoices[j] = buffers->mixer->rvoices[*av]; - } - } - if (buffers->mixer->remove_voice_callback) - buffers->mixer->remove_voice_callback( - buffers->mixer->remove_voice_callback_userdata, v); - } - buffers->finished_voice_count = 0; -} - -static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t* mixer) -{ -#ifdef ENABLE_MIXER_THREADS - int i; - for (i=0; i < mixer->thread_count; i++) - fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]); +fluid_finish_rvoice(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice) +{ + if(buffers->finished_voice_count < buffers->mixer->polyphony) + { + buffers->finished_voices[buffers->finished_voice_count++] = rvoice; + } + else + { + FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony"); + } +} + +static void +fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t *buffers) +{ + int i, j; + + for(i = 0; i < buffers->finished_voice_count; i++) + { + fluid_rvoice_t *v = buffers->finished_voices[i]; + int av = buffers->mixer->active_voices; + + for(j = 0; j < av; j++) + { + if(v == buffers->mixer->rvoices[j]) + { + av--; + + /* Pack the array */ + if(j < av) + { + buffers->mixer->rvoices[j] = buffers->mixer->rvoices[av]; + } + } + } + + buffers->mixer->active_voices = av; + + fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v); + } + + buffers->finished_voice_count = 0; +} + +static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer) +{ +#if ENABLE_MIXER_THREADS + int i; + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]); + } + #endif - fluid_mixer_buffer_process_finished_voices(&mixer->buffers); + fluid_mixer_buffer_process_finished_voices(&mixer->buffers); } -static FLUID_INLINE void -fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers, - fluid_rvoice_t* voice, fluid_real_t** bufs, - unsigned int bufcount) + +static FLUID_INLINE fluid_real_t * +get_dest_buf(fluid_rvoice_buffers_t *buffers, int index, + fluid_real_t **dest_bufs, int dest_bufcount) { - int s = fluid_mix_one(voice, bufs, bufcount, buffers->mixer->current_blockcount); - if (s < buffers->mixer->current_blockcount * FLUID_BUFSIZE) { - fluid_finish_rvoice(buffers, voice); - } + int j = buffers->bufs[index].mapping; + + if(j >= dest_bufcount || j < 0) + { + return NULL; + } + + return dest_bufs[j]; } -/* -static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers, - fluid_rvoice_t* voice) + +/** + * Mix data down to buffers + * + * @param buffers Destination buffer(s) + * @param dsp_buf Mono sample source + * @param start_block Block to start mixing at + * @param sample_count number of samples to mix following \c start_block + * @param dest_bufs Array of buffers to mixdown to + * @param dest_bufcount Length of dest_bufs + */ +static void +fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, + fluid_real_t *FLUID_RESTRICT dsp_buf, + int start_block, int sample_count, + fluid_real_t **dest_bufs, int dest_bufcount) { - int i, retval=0; - int fvc = buffers->finished_voice_count; - for (i=0; i < fvc; i++) - if (buffers->finished_voices[i] == voice) { - fvc--; - if (i < fvc) - buffers->finished_voices[i] = buffers->finished_voices[fvc]; - retval++; + int bufcount = buffers->count; + int i, dsp_i; + + if(sample_count <= 0 || dest_bufcount <= 0) + { + return; + } + + FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0); + FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0); + + for(i = 0; i < bufcount; i++) + { + fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); + fluid_real_t amp = buffers->bufs[i].amp; + + if(buf == NULL || amp == 0.0f) + { + continue; + } + + FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0); + + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + + for(dsp_i = (start_block * FLUID_BUFSIZE); dsp_i < sample_count; dsp_i++) + { + buf[dsp_i] += amp * dsp_buf[dsp_i]; + } } - fvc = buffers->finished_voice_count; - return retval; } -*/ -int -fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice) +/** + * Synthesize one voice and add to buffer. + * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means + * voice has been finished, removed and possibly replaced with another voice. + */ +static FLUID_INLINE void +fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, + fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs, + unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount) { - int i; + int i, total_samples = 0, start_block = 0; - if (mixer->active_voices < mixer->polyphony) { - mixer->rvoices[mixer->active_voices++] = voice; - return FLUID_OK; - } - - /* See if any voices just finished, if so, take its place. - This can happen in voice overflow conditions. */ - for (i=0; i < mixer->active_voices; i++) { - if (mixer->rvoices[i] == voice) { - FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); - return FLUID_FAILED; + for(i = 0; i < blockcount; i++) + { + int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); + + if(s == -1) + { + start_block += s; + s = FLUID_BUFSIZE; + } + + total_samples += s; + + if(s < FLUID_BUFSIZE) + { + break; + } } - if (mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) { - fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); - mixer->rvoices[i] = voice; - return FLUID_OK; + + fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, -start_block, total_samples - ((-start_block)*FLUID_BUFSIZE), dest_bufs, dest_bufcount); + + if(total_samples < blockcount * FLUID_BUFSIZE) + { + fluid_finish_rvoice(buffers, rvoice); } - } +} - /* This should never happen */ - FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); - return FLUID_FAILED; +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice) +{ + int i; + fluid_rvoice_mixer_t *mixer = obj; + fluid_rvoice_t *voice = param[0].ptr; + + if(mixer->active_voices < mixer->polyphony) + { + mixer->rvoices[mixer->active_voices++] = voice; + return; // success + } + + /* See if any voices just finished, if so, take its place. + This can happen in voice overflow conditions. */ + for(i = 0; i < mixer->active_voices; i++) + { + if(mixer->rvoices[i] == voice) + { + FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); + return; + } + + if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) + { + fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); + mixer->rvoices[i] = voice; + return; // success + } + } + + /* This should never happen */ + FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); + return; } -static int -fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t* buffers, int value) +static int +fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value) { - void* newptr; + void *newptr; - if (buffers->finished_voice_count > value) - return FLUID_FAILED; - - newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t*)); - if (newptr == NULL && value > 0) - return FLUID_FAILED; - buffers->finished_voices = newptr; - return FLUID_OK; + if(buffers->finished_voice_count > value) + { + return FLUID_FAILED; + } + + newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t *)); + + if(newptr == NULL && value > 0) + { + return FLUID_FAILED; + } + + buffers->finished_voices = newptr; + return FLUID_OK; } /** * Update polyphony - max number of voices (NOTE: not hard real-time capable) * @return FLUID_OK or FLUID_FAILED */ -int -fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony) { - void* newptr; - if (handler->active_voices > value) - return FLUID_FAILED; + void *newptr; + fluid_rvoice_mixer_t *handler = obj; + int value = param[0].i; - newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*)); - if (newptr == NULL) - return FLUID_FAILED; - handler->rvoices = newptr; + if(handler->active_voices > value) + { + return /*FLUID_FAILED*/; + } - if (fluid_mixer_buffers_update_polyphony(&handler->buffers, value) - == FLUID_FAILED) - return FLUID_FAILED; + newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *)); -#ifdef ENABLE_MIXER_THREADS - { - int i; - for (i=0; i < handler->thread_count; i++) - if (fluid_mixer_buffers_update_polyphony(&handler->threads[i], value) - == FLUID_FAILED) - return FLUID_FAILED; - } + if(newptr == NULL) + { + return /*FLUID_FAILED*/; + } + + handler->rvoices = newptr; + + if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + +#if ENABLE_MIXER_THREADS + { + int i; + + for(i = 0; i < handler->thread_count; i++) + { + if(fluid_mixer_buffers_update_polyphony(&handler->threads[i], value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + } + } #endif - handler->polyphony = value; - return FLUID_OK; + handler->polyphony = value; + return /*FLUID_OK*/; } -static void -fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer) +static void +fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount) { - int i; - FLUID_DECLARE_VLA(fluid_real_t*, bufs, - mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); - int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); - fluid_profile_ref_var(prof_ref); - for (i=0; i < mixer->active_voices; i++) { - fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs, - bufcount); - fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref); - } -} + int i; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_profile_ref_var(prof_ref); + + for(i = 0; i < mixer->active_voices; i++) + { + fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs, + bufcount, local_buf, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + blockcount * FLUID_BUFSIZE); + } +} static FLUID_INLINE void -fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers) +fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount) { - int i; - int size = buffers->mixer->current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); - /* TODO: Optimize by only zero out the buffers we actually use later on. */ - for (i=0; i < buffers->buf_count; i++) { - FLUID_MEMSET(buffers->left_buf[i], 0, size); - FLUID_MEMSET(buffers->right_buf[i], 0, size); - } - for (i=0; i < buffers->fx_buf_count; i++) { - FLUID_MEMSET(buffers->fx_left_buf[i], 0, size); - FLUID_MEMSET(buffers->fx_right_buf[i], 0, size); - } -} + int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); + /* TODO: Optimize by only zero out the buffers we actually use later on. */ + int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count; + fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); -static int -fluid_mixer_buffers_init(fluid_mixer_buffers_t* buffers, fluid_rvoice_mixer_t* mixer) -{ - int i, samplecount; - - buffers->mixer = mixer; - buffers->buf_count = buffers->mixer->buffers.buf_count; - buffers->fx_buf_count = buffers->mixer->buffers.fx_buf_count; - buffers->buf_blocks = buffers->mixer->buffers.buf_blocks; - samplecount = FLUID_BUFSIZE * buffers->buf_blocks; - - - /* Left and right audio buffers */ + for(i = 0; i < buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } + + buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < fx_buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } +} - buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count); - buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count); +static int +fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer) +{ + const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT; - if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; - } + buffers->mixer = mixer; + buffers->buf_count = mixer->buffers.buf_count; + buffers->fx_buf_count = mixer->buffers.fx_buf_count; - FLUID_MEMSET(buffers->left_buf, 0, buffers->buf_count * sizeof(fluid_real_t*)); - FLUID_MEMSET(buffers->right_buf, 0, buffers->buf_count * sizeof(fluid_real_t*)); + /* Local mono voice buf */ + buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT); - for (i = 0; i < buffers->buf_count; i++) { + /* Left and right audio buffers */ - buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); - buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); + buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); - if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; + if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; } - } - - /* Effects audio buffers */ - buffers->fx_left_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count); - buffers->fx_right_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count); + /* Effects audio buffers */ - if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; - } + buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); - FLUID_MEMSET(buffers->fx_left_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*)); - FLUID_MEMSET(buffers->fx_right_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*)); + if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } - for (i = 0; i < buffers->fx_buf_count; i++) { - buffers->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); - buffers->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); + buffers->finished_voices = NULL; - if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; + if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony) + == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; } - } - - buffers->finished_voices = NULL; - if (fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony) - == FLUID_FAILED) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; - } - - return 1; + + return 1; } /** * Note: Not hard real-time capable (calls malloc) */ -void -fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) { - int i; - if (mixer->fx.chorus) - delete_fluid_chorus(mixer->fx.chorus); - mixer->fx.chorus = new_fluid_chorus(samplerate); - if (mixer->fx.reverb) - fluid_revmodel_samplerate_change(mixer->fx.reverb, samplerate); - for (i=0; i < mixer->active_voices; i++) - fluid_rvoice_set_output_rate(mixer->rvoices[i], samplerate); + fluid_rvoice_mixer_t *mixer = obj; + fluid_real_t samplerate = param[1].real; // becausee fluid_synth_update_mixer() puts real into arg2 + + int i; + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].chorus) + { + delete_fluid_chorus(mixer->fx[i].chorus); + } + + mixer->fx[i].chorus = new_fluid_chorus(samplerate); + + if(mixer->fx[i].reverb) + { + fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate); + } + } + +#if LADSPA + + if(mixer->ladspa_fx != NULL) + { + fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate); + } + +#endif } @@ -530,188 +686,299 @@ fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samp * @param buf_count number of primary stereo buffers * @param fx_buf_count number of stereo effect buffers */ -fluid_rvoice_mixer_t* -new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate) +fluid_rvoice_mixer_t * +new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio) { - fluid_rvoice_mixer_t* mixer = FLUID_NEW(fluid_rvoice_mixer_t); - if (mixer == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t)); - mixer->buffers.buf_count = buf_count; - mixer->buffers.fx_buf_count = fx_buf_count; - mixer->buffers.buf_blocks = FLUID_MIXER_MAX_BUFFERS_DEFAULT; - - /* allocate the reverb module */ - mixer->fx.reverb = new_fluid_revmodel(sample_rate); - mixer->fx.chorus = new_fluid_chorus(sample_rate); - if (mixer->fx.reverb == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - delete_fluid_rvoice_mixer(mixer); - return NULL; - } - - if (!fluid_mixer_buffers_init(&mixer->buffers, mixer)) { - delete_fluid_rvoice_mixer(mixer); - return NULL; - } - -#ifdef ENABLE_MIXER_THREADS - mixer->thread_ready = new_fluid_cond(); - mixer->wakeup_threads = new_fluid_cond(); - mixer->thread_ready_m = new_fluid_cond_mutex(); - mixer->wakeup_threads_m = new_fluid_cond_mutex(); - if (!mixer->thread_ready || !mixer->wakeup_threads || - !mixer->thread_ready_m || !mixer->wakeup_threads_m) { + int i; + fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t); + + if(mixer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t)); + mixer->eventhandler = evthandler; + mixer->fx_units = fx_units; + mixer->buffers.buf_count = buf_count; + mixer->buffers.fx_buf_count = fx_buf_count * fx_units; + + /* allocate the reverb module */ + mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units); + if(mixer->fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx)); + + for(i = 0; i < fx_units; i++) + { + mixer->fx[i].reverb = new_fluid_revmodel(sample_rate); + mixer->fx[i].chorus = new_fluid_chorus(sample_rate); + + if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + if(!fluid_mixer_buffers_init(&mixer->buffers, mixer)) + { + goto error_recovery; + } + +#if ENABLE_MIXER_THREADS + mixer->thread_ready = new_fluid_cond(); + mixer->wakeup_threads = new_fluid_cond(); + mixer->thread_ready_m = new_fluid_cond_mutex(); + mixer->wakeup_threads_m = new_fluid_cond_mutex(); + + if(!mixer->thread_ready || !mixer->wakeup_threads || + !mixer->thread_ready_m || !mixer->wakeup_threads_m) + { + goto error_recovery; + } + + if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK) + { + goto error_recovery; + } + +#endif + + return mixer; + +error_recovery: delete_fluid_rvoice_mixer(mixer); return NULL; - } -#endif - - return mixer; } static void -fluid_mixer_buffers_free(fluid_mixer_buffers_t* buffers) -{ - int i; - - FLUID_FREE(buffers->finished_voices); - - /* free all the sample buffers */ - if (buffers->left_buf != NULL) { - for (i = 0; i < buffers->buf_count; i++) { - if (buffers->left_buf[i] != NULL) { - FLUID_FREE(buffers->left_buf[i]); - } - } +fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers) +{ + FLUID_FREE(buffers->finished_voices); + + /* free all the sample buffers */ + FLUID_FREE(buffers->local_buf); FLUID_FREE(buffers->left_buf); - } + FLUID_FREE(buffers->right_buf); + FLUID_FREE(buffers->fx_left_buf); + FLUID_FREE(buffers->fx_right_buf); +} + +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) +{ + int i; + + fluid_return_if_fail(mixer != NULL); - if (buffers->right_buf != NULL) { - for (i = 0; i < buffers->buf_count; i++) { - if (buffers->right_buf[i] != NULL) { - FLUID_FREE(buffers->right_buf[i]); - } +#if ENABLE_MIXER_THREADS + delete_rvoice_mixer_threads(mixer); + + if(mixer->thread_ready) + { + delete_fluid_cond(mixer->thread_ready); } - FLUID_FREE(buffers->right_buf); - } - if (buffers->fx_left_buf != NULL) { - for (i = 0; i < buffers->fx_buf_count; i++) { - if (buffers->fx_left_buf[i] != NULL) { - FLUID_FREE(buffers->fx_left_buf[i]); - } + if(mixer->wakeup_threads) + { + delete_fluid_cond(mixer->wakeup_threads); } - FLUID_FREE(buffers->fx_left_buf); - } - if (buffers->fx_right_buf != NULL) { - for (i = 0; i < buffers->fx_buf_count; i++) { - if (buffers->fx_right_buf[i] != NULL) { - FLUID_FREE(buffers->fx_right_buf[i]); - } + if(mixer->thread_ready_m) + { + delete_fluid_cond_mutex(mixer->thread_ready_m); + } + + if(mixer->wakeup_threads_m) + { + delete_fluid_cond_mutex(mixer->wakeup_threads_m); } - FLUID_FREE(buffers->fx_right_buf); - } -} -void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer) -{ - if (!mixer) - return; - fluid_rvoice_mixer_set_threads(mixer, 0, 0); -#ifdef ENABLE_MIXER_THREADS - if (mixer->thread_ready) - delete_fluid_cond(mixer->thread_ready); - if (mixer->wakeup_threads) - delete_fluid_cond(mixer->wakeup_threads); - if (mixer->thread_ready_m) - delete_fluid_cond_mutex(mixer->thread_ready_m); - if (mixer->wakeup_threads_m) - delete_fluid_cond_mutex(mixer->wakeup_threads_m); #endif - fluid_mixer_buffers_free(&mixer->buffers); - if (mixer->fx.reverb) - delete_fluid_revmodel(mixer->fx.reverb); - if (mixer->fx.chorus) - delete_fluid_chorus(mixer->fx.chorus); - FLUID_FREE(mixer->rvoices); - FLUID_FREE(mixer); + fluid_mixer_buffers_free(&mixer->buffers); + + + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].reverb) + { + delete_fluid_revmodel(mixer->fx[i].reverb); + } + + if(mixer->fx[i].chorus) + { + delete_fluid_chorus(mixer->fx[i].chorus); + } + } + + FLUID_FREE(mixer->fx); + FLUID_FREE(mixer->rvoices); + FLUID_FREE(mixer); } -#ifdef LADSPA -void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, - fluid_LADSPA_FxUnit_t* ladspa) +#ifdef LADSPA +/** + * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers + * as LADSPA host buffers with sensible names */ +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups) { - mixer->LADSPA_FxUnit = ladspa; + mixer->ladspa_fx = ladspa_fx; + + if(ladspa_fx == NULL) + { + return; + } + else + { + fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *chor = rev; + + rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups, + main_l, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups, + main_r, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1, + rev, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1, + chor, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + } } #endif -void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) { - mixer->fx.with_reverb = on; + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + + mixer->with_reverb = on; } -void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) { - mixer->fx.with_chorus = on; + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + mixer->with_chorus = on; } -void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on) +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) { - mixer->fx.mix_fx_to_out = on; + mixer->mix_fx_to_out = on; } -void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set, - int nr, double level, double speed, - double depth_ms, int type) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params) { - fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type); + fluid_rvoice_mixer_t *mixer = obj; + int set = param[0].i; + int nr = param[1].i; + fluid_real_t level = param[2].real; + fluid_real_t speed = param[3].real; + fluid_real_t depth_ms = param[4].real; + int type = param[5].i; + + int i; + for(i = 0; i < mixer->fx_units; i++) + { + fluid_chorus_set(mixer->fx[i].chorus, set, nr, level, speed, depth_ms, type); + } } -void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set, - double roomsize, double damping, - double width, double level) + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params) { - fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level); + fluid_rvoice_mixer_t *mixer = obj; + int set = param[0].i; + fluid_real_t roomsize = param[1].real; + fluid_real_t damping = param[2].real; + fluid_real_t width = param[3].real; + fluid_real_t level = param[4].real; + + int i; + for(i = 0; i < mixer->fx_units; i++) + { + fluid_revmodel_set(mixer->fx[i].reverb, set, roomsize, damping, width, level); + } } -void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer) +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb) { - fluid_revmodel_reset(mixer->fx.reverb); - fluid_chorus_reset(mixer->fx.chorus); + fluid_rvoice_mixer_t *mixer = obj; + int i; + for(i = 0; i < mixer->fx_units; i++) + { + fluid_revmodel_reset(mixer->fx[i].reverb); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i; + for(i = 0; i < mixer->fx_units; i++) + { + fluid_chorus_reset(mixer->fx[i].chorus); + } } -void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer) +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right) { - fluid_revmodel_reset(mixer->fx.reverb); + *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.buf_count; } -void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer) +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right) { - fluid_chorus_reset(mixer->fx.chorus); + *fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.fx_buf_count; } -int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer, - fluid_real_t*** left, fluid_real_t*** right) +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer) { - *left = mixer->buffers.left_buf; - *right = mixer->buffers.right_buf; - return mixer->buffers.buf_count; + return FLUID_MIXER_MAX_BUFFERS_DEFAULT; } +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer) +{ + return mixer->active_voices; +} +#endif -#ifdef ENABLE_MIXER_THREADS +#if ENABLE_MIXER_THREADS -static FLUID_INLINE fluid_rvoice_t* -fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer) +static FLUID_INLINE fluid_rvoice_t * +fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t *mixer) { - int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1); - if (i >= mixer->active_voices) - return NULL; - return mixer->rvoices[i]; + int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1); + + if(i >= mixer->active_voices) + { + return NULL; + } + + return mixer->rvoices[i]; } #define THREAD_BUF_PROCESSING 0 @@ -720,255 +987,391 @@ fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer) #define THREAD_BUF_TERMINATE 3 /* Core thread function (processes voices in parallel to primary synthesis thread) */ -static void -fluid_mixer_thread_func (void* data) -{ - fluid_mixer_buffers_t* buffers = data; - fluid_rvoice_mixer_t* mixer = buffers->mixer; - int hasValidData = 0; - FLUID_DECLARE_VLA(fluid_real_t*, bufs, buffers->buf_count*2 + buffers->fx_buf_count*2); - int bufcount = 0; - - while (!fluid_atomic_int_get(&mixer->threads_should_terminate)) { - fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer); - if (rvoice == NULL) { - // if no voices: signal rendered buffers, sleep - fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA); - fluid_cond_mutex_lock(mixer->thread_ready_m); - fluid_cond_signal(mixer->thread_ready); - fluid_cond_mutex_unlock(mixer->thread_ready_m); - - fluid_cond_mutex_lock(mixer->wakeup_threads_m); - while (1) { - int j = fluid_atomic_int_get(&buffers->ready); - if (j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE) - break; - fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m); - } - fluid_cond_mutex_unlock(mixer->wakeup_threads_m); - - hasValidData = 0; - } - else { - // else: if buffer is not zeroed, zero buffers - if (!hasValidData) { - fluid_mixer_buffers_zero(buffers); - bufcount = fluid_mixer_buffers_prepare(buffers, bufs); - hasValidData = 1; - } - // then render voice to buffers - fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount); - } - } +static fluid_thread_return_t +fluid_mixer_thread_func(void *data) +{ + fluid_mixer_buffers_t *buffers = data; + fluid_rvoice_mixer_t *mixer = buffers->mixer; + int hasValidData = 0; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, buffers->buf_count * 2 + buffers->fx_buf_count * 2); + int bufcount = 0; + int current_blockcount = 0; + fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT); + + while(!fluid_atomic_int_get(&mixer->threads_should_terminate)) + { + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice == NULL) + { + // if no voices: signal rendered buffers, sleep + fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA); + fluid_cond_mutex_lock(mixer->thread_ready_m); + fluid_cond_signal(mixer->thread_ready); + fluid_cond_mutex_unlock(mixer->thread_ready_m); + + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + + while(1) + { + int j = fluid_atomic_int_get(&buffers->ready); + + if(j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE) + { + break; + } + + fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m); + } + + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + hasValidData = 0; + } + else + { + // else: if buffer is not zeroed, zero buffers + if(!hasValidData) + { + // blockcount may have changed, since thread was put to sleep + current_blockcount = mixer->current_blockcount; + fluid_mixer_buffers_zero(buffers, current_blockcount); + bufcount = fluid_mixer_buffers_prepare(buffers, bufs); + hasValidData = 1; + } + + // then render voice to buffers + fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + } + } + return FLUID_THREAD_RETURN_VALUE; } static void -fluid_mixer_buffers_mix(fluid_mixer_buffers_t* dest, fluid_mixer_buffers_t* src) +fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount) { - int i,j; - int scount = dest->mixer->current_blockcount * FLUID_BUFSIZE; - int minbuf; - - minbuf = dest->buf_count; - if (minbuf > src->buf_count) - minbuf = src->buf_count; - for (i=0; i < minbuf; i++) { - for (j=0; j < scount; j++) { - dest->left_buf[i][j] += src->left_buf[i][j]; - dest->right_buf[i][j] += src->right_buf[i][j]; + int i, j; + int scount = current_blockcount * FLUID_BUFSIZE; + int minbuf; + fluid_real_t *FLUID_RESTRICT base_src; + fluid_real_t *FLUID_RESTRICT base_dst; + + minbuf = dst->buf_count; + + if(minbuf > src->buf_count) + { + minbuf = src->buf_count; + } + + base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } } - } - minbuf = dest->fx_buf_count; - if (minbuf > src->fx_buf_count) - minbuf = src->fx_buf_count; - for (i=0; i < minbuf; i++) { - for (j=0; j < scount; j++) { - dest->fx_left_buf[i][j] += src->fx_left_buf[i][j]; - dest->fx_right_buf[i][j] += src->fx_right_buf[i][j]; + minbuf = dst->fx_buf_count; + + if(minbuf > src->fx_buf_count) + { + minbuf = src->fx_buf_count; + } + + base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } } - } } /** - * Go through all threads and see if someone is finished for mixing + * Go through all threads and see if someone is finished for mixing */ -static FLUID_INLINE int -fluid_mixer_mix_in(fluid_rvoice_mixer_t* mixer, int extra_threads) -{ - int i, result, hasmixed; - do { - hasmixed = 0; - result = 0; - for (i=0; i < extra_threads; i++) { - int j = fluid_atomic_int_get(&mixer->threads[i].ready); - switch (j) { - case THREAD_BUF_PROCESSING: - result = 1; - break; - case THREAD_BUF_VALID: - fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA); - fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i]); - hasmixed = 1; - break; - } - } - } while (hasmixed); - return result; -} - -static void -fluid_render_loop_multithread(fluid_rvoice_mixer_t* mixer) -{ - int i, bufcount; - //int scount = mixer->current_blockcount * FLUID_BUFSIZE; - FLUID_DECLARE_VLA(fluid_real_t*, bufs, - mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); - // How many threads should we start this time? - int extra_threads = mixer->active_voices / VOICES_PER_THREAD; - if (extra_threads > mixer->thread_count) - extra_threads = mixer->thread_count; - if (extra_threads == 0) { - // No extra threads? No thread overhead! - fluid_render_loop_singlethread(mixer); - return; - } - - bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); - - // Prepare voice list - fluid_cond_mutex_lock(mixer->wakeup_threads_m); - fluid_atomic_int_set(&mixer->current_rvoice, 0); - for (i=0; i < extra_threads; i++) - fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING); - // Signal threads to wake up - fluid_cond_broadcast(mixer->wakeup_threads); - fluid_cond_mutex_unlock(mixer->wakeup_threads_m); - - // If thread is finished, mix it in - while (fluid_mixer_mix_in(mixer, extra_threads)) { - // Otherwise get a voice and render it - fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer); - if (rvoice != NULL) { - fluid_profile_ref_var(prof_ref); - fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount); - fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref); - //test++; - } - else { - // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock - int is_processing = 0; - //waits++; - fluid_cond_mutex_lock(mixer->thread_ready_m); - for (i=0; i < extra_threads; i++) - if (fluid_atomic_int_get(&mixer->threads[i].ready) == - THREAD_BUF_PROCESSING) - is_processing = 1; - if (is_processing) - fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m); - fluid_cond_mutex_unlock(mixer->thread_ready_m); - } - } - //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d", - // mixer->current_blockcount, test, mixer->active_voices, waits); +static int +fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount) +{ + int i, result, hasmixed; + + do + { + hasmixed = 0; + result = 0; + + for(i = 0; i < extra_threads; i++) + { + int j = fluid_atomic_int_get(&mixer->threads[i].ready); + + switch(j) + { + case THREAD_BUF_PROCESSING: + result = 1; + break; + + case THREAD_BUF_VALID: + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA); + fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i], current_blockcount); + hasmixed = 1; + break; + } + } + } + while(hasmixed); + + return result; } -#endif +static void +fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount) +{ + int i, bufcount; + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); -/** - * Update amount of extra mixer threads. - * @param thread_count Number of extra mixer threads for multi-core rendering - * @param prio_level real-time prio level for the extra mixer threads - */ -void -fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count, - int prio_level) -{ -#ifdef ENABLE_MIXER_THREADS - char name[16]; - int i; - - // Kill all existing threads first - if (mixer->thread_count) { + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + // How many threads should we start this time? + int extra_threads = mixer->active_voices / VOICES_PER_THREAD; + + if(extra_threads > mixer->thread_count) + { + extra_threads = mixer->thread_count; + } + + if(extra_threads == 0) + { + // No extra threads? No thread overhead! + fluid_render_loop_singlethread(mixer, current_blockcount); + return; + } + + bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + // Prepare voice list + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + fluid_atomic_int_set(&mixer->current_rvoice, 0); + + for(i = 0; i < extra_threads; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING); + } + + // Signal threads to wake up + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + // If thread is finished, mix it in + while(fluid_mixer_mix_in(mixer, extra_threads, current_blockcount)) + { + // Otherwise get a voice and render it + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice != NULL) + { + fluid_profile_ref_var(prof_ref); + fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + current_blockcount * FLUID_BUFSIZE); + //test++; + } + else + { + // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock + int is_processing = 0; + //waits++; + fluid_cond_mutex_lock(mixer->thread_ready_m); + + for(i = 0; i < extra_threads; i++) + { + if(fluid_atomic_int_get(&mixer->threads[i].ready) == + THREAD_BUF_PROCESSING) + { + is_processing = 1; + } + } + + if(is_processing) + { + fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m); + } + + fluid_cond_mutex_unlock(mixer->thread_ready_m); + } + } + + //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d", + // current_blockcount, test, mixer->active_voices, waits); +} + +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer) +{ + int i; fluid_atomic_int_set(&mixer->threads_should_terminate, 1); // Signal threads to wake up fluid_cond_mutex_lock(mixer->wakeup_threads_m); - for (i=0; i < mixer->thread_count; i++) - fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); + } + fluid_cond_broadcast(mixer->wakeup_threads); fluid_cond_mutex_unlock(mixer->wakeup_threads_m); - - for (i=0; i < mixer->thread_count; i++) { - if (mixer->threads[i].thread) { - fluid_thread_join(mixer->threads[i].thread); - delete_fluid_thread(mixer->threads[i].thread); - } - fluid_mixer_buffers_free(&mixer->threads[i]); + + for(i = 0; i < mixer->thread_count; i++) + { + if(mixer->threads[i].thread) + { + fluid_thread_join(mixer->threads[i].thread); + delete_fluid_thread(mixer->threads[i].thread); + } + + fluid_mixer_buffers_free(&mixer->threads[i]); } + FLUID_FREE(mixer->threads); mixer->thread_count = 0; mixer->threads = NULL; - } - - if (thread_count == 0) - return; - - // Now prepare the new threads - fluid_atomic_int_set(&mixer->threads_should_terminate, 0); - mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count); - if (mixer->threads == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return; - } - FLUID_MEMSET(mixer->threads, 0, thread_count*sizeof(fluid_mixer_buffers_t)); - mixer->thread_count = thread_count; - for (i=0; i < thread_count; i++) { - fluid_mixer_buffers_t* b = &mixer->threads[i]; - if (!fluid_mixer_buffers_init(b, mixer)) - return; - fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); - g_snprintf (name, sizeof (name), "mixer%d", i); - b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0); - if (!b->thread) - return; - } +} -#endif +/** + * Update amount of extra mixer threads. + * @param thread_count Number of extra mixer threads for multi-core rendering + * @param prio_level real-time prio level for the extra mixer threads + */ +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level) +{ + char name[16]; + int i; + + // Kill all existing threads first + if(mixer->thread_count) + { + delete_rvoice_mixer_threads(mixer); + } + + if(thread_count == 0) + { + return FLUID_OK; + } + + // Now prepare the new threads + fluid_atomic_int_set(&mixer->threads_should_terminate, 0); + mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count); + + if(mixer->threads == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(mixer->threads, 0, thread_count * sizeof(fluid_mixer_buffers_t)); + mixer->thread_count = thread_count; + + for(i = 0; i < thread_count; i++) + { + fluid_mixer_buffers_t *b = &mixer->threads[i]; + + if(!fluid_mixer_buffers_init(b, mixer)) + { + return FLUID_FAILED; + } + + fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); + FLUID_SNPRINTF(name, sizeof(name), "mixer%d", i); + b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0); + + if(!b->thread) + { + return FLUID_FAILED; + } + } + + return FLUID_OK; } +#endif /** * Synthesize audio into buffers - * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples + * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples * @return number of blocks rendered */ -int -fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount) -{ - fluid_profile_ref_var(prof_ref); - - mixer->current_blockcount = blockcount > mixer->buffers.buf_blocks ? - mixer->buffers.buf_blocks : blockcount; - - // Zero buffers - fluid_mixer_buffers_zero(&mixer->buffers); - fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref); - -#ifdef ENABLE_MIXER_THREADS - if (mixer->thread_count > 0) - fluid_render_loop_multithread(mixer); - else +int +fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount) +{ + fluid_profile_ref_var(prof_ref); + + mixer->current_blockcount = blockcount; + + // Zero buffers + fluid_mixer_buffers_zero(&mixer->buffers, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + +#if ENABLE_MIXER_THREADS + + if(mixer->thread_count > 0) + { + fluid_render_loop_multithread(mixer, blockcount); + } + else #endif - fluid_render_loop_singlethread(mixer); - fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref); - + { + fluid_render_loop_singlethread(mixer, blockcount); + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + - // Process reverb & chorus - fluid_rvoice_mixer_process_fx(mixer); + // Process reverb & chorus + fluid_rvoice_mixer_process_fx(mixer, blockcount); - // Call the callback and pack active voice array - fluid_rvoice_mixer_process_finished_voices(mixer); + // Call the callback and pack active voice array + fluid_rvoice_mixer_process_finished_voices(mixer); - return mixer->current_blockcount; + return blockcount; } diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.h b/libs/fluidsynth/src/fluid_rvoice_mixer.h index d4e41ca0a8..1b3fceb342 100644 --- a/libs/fluidsynth/src/fluid_rvoice_mixer.h +++ b/libs/fluidsynth/src/fluid_rvoice_mixer.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,50 +24,41 @@ #include "fluidsynth_priv.h" #include "fluid_rvoice.h" -//#include "fluid_ladspa.h" typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t; -#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) - +int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount); +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right); +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right); +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer); +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer); +#endif +fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *, int, int); -void fluid_rvoice_mixer_set_finished_voices_callback( - fluid_rvoice_mixer_t* mixer, - void (*func)(void*, fluid_rvoice_t*), - void* userdata); +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *); -int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount); -int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer, - fluid_real_t*** left, fluid_real_t*** right); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params); -fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, - fluid_real_t sample_rate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus); -void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t*); -void fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate); -void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on); -void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on); -void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on); -int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value); -int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice); -void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set, - int nr, double level, double speed, - double depth_ms, int type); -void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set, - double roomsize, double damping, - double width, double level); -void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer); -void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer); -void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer); -void fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count, - int prio_level); - -#ifdef LADSPA -void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, - fluid_LADSPA_FxUnit_t* ladspa); +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on); +#ifdef LADSPA +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups); #endif #endif diff --git a/libs/fluidsynth/src/fluid_samplecache.c b/libs/fluidsynth/src/fluid_samplecache.c new file mode 100644 index 0000000000..773e19feea --- /dev/null +++ b/libs/fluidsynth/src/fluid_samplecache.c @@ -0,0 +1,295 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* CACHED SAMPLE DATA LOADER + * + * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read + * data across all FluidSynth instances in a global (process-wide) list. + */ + +#include "fluid_samplecache.h" +#include "fluid_sys.h" +#include "fluidsynth.h" +#include "fluid_list.h" + + +typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; + +struct _fluid_samplecache_entry_t +{ + /* The follwing members all form the cache key */ + char *filename; + time_t modification_time; + unsigned int sf_samplepos; + unsigned int sf_samplesize; + unsigned int sf_sample24pos; + unsigned int sf_sample24size; + unsigned int sample_start; + unsigned int sample_end; + int sample_type; + /* End of cache key members */ + + short *sample_data; + char *sample_data24; + int sample_count; + + int num_references; + int mlocked; +}; + +static fluid_list_t *samplecache_list = NULL; +static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT; + +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type); +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type); +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry); + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time); + + +/* PUBLIC INTERFACE */ + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **sample_data, char **sample_data24) +{ + fluid_samplecache_entry_t *entry; + int ret; + + fluid_mutex_lock(samplecache_mutex); + + entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type); + + if(entry == NULL) + { + entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type); + + if(entry == NULL) + { + ret = -1; + goto unlock_exit; + } + + samplecache_list = fluid_list_prepend(samplecache_list, entry); + } + + if(try_mlock && !entry->mlocked) + { + /* Lock the memory to disable paging. It's okay if this fails. It + * probably means that the user doesn't have the required permission. */ + if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) + { + if(entry->sample_data24 != NULL) + { + entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); + } + else + { + entry->mlocked = TRUE; + } + + if(!entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); + } + } + } + + entry->num_references++; + *sample_data = entry->sample_data; + *sample_data24 = entry->sample_data24; + ret = entry->sample_count; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + +int fluid_samplecache_unload(const short *sample_data) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + int ret; + + fluid_mutex_lock(samplecache_mutex); + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if(sample_data == entry->sample_data) + { + entry->num_references--; + + if(entry->num_references == 0) + { + if(entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + + if(entry->sample_data24 != NULL) + { + fluid_munlock(entry->sample_data24, entry->sample_count); + } + } + + samplecache_list = fluid_list_remove(samplecache_list, entry); + delete_samplecache_entry(entry); + } + + ret = FLUID_OK; + goto unlock_exit; + } + + entry_list = fluid_list_next(entry_list); + } + + FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); + ret = FLUID_FAILED; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + + +/* Private functions */ +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type) +{ + fluid_samplecache_entry_t *entry; + + entry = FLUID_NEW(fluid_samplecache_entry_t); + + if(entry == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(entry, 0, sizeof(*entry)); + + entry->filename = FLUID_STRDUP(sf->fname); + + if(entry->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED) + { + FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file."); + entry->modification_time = 0; + } + + entry->sf_samplepos = sf->samplepos; + entry->sf_samplesize = sf->samplesize; + entry->sf_sample24pos = sf->sample24pos; + entry->sf_sample24size = sf->sample24size; + entry->sample_start = sample_start; + entry->sample_end = sample_end; + entry->sample_type = sample_type; + + entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type, + &entry->sample_data, &entry->sample_data24); + + if(entry->sample_count < 0) + { + goto error_exit; + } + + return entry; + +error_exit: + delete_samplecache_entry(entry); + return NULL; +} + +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry) +{ + fluid_return_if_fail(entry != NULL); + + FLUID_FREE(entry->filename); + FLUID_FREE(entry->sample_data); + FLUID_FREE(entry->sample_data24); + FLUID_FREE(entry); +} + +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type) +{ + time_t mtime; + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + + if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED) + { + FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file."); + mtime = 0; + } + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if((FLUID_STRCMP(sf->fname, entry->filename) == 0) && + (mtime == entry->modification_time) && + (sf->samplepos == entry->sf_samplepos) && + (sf->samplesize == entry->sf_samplesize) && + (sf->sample24pos == entry->sf_sample24pos) && + (sf->sample24size == entry->sf_sample24size) && + (sample_start == entry->sample_start) && + (sample_end == entry->sample_end) && + (sample_type == entry->sample_type)) + { + return entry; + } + + entry_list = fluid_list_next(entry_list); + } + + return NULL; +} + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time) +{ + fluid_stat_buf_t buf; + + if(fluid_stat(filename, &buf)) + { + return FLUID_FAILED; + } + + *modification_time = buf.st_mtime; + return FLUID_OK; +} diff --git a/libs/fluidsynth/src/fluid_samplecache.h b/libs/fluidsynth/src/fluid_samplecache.h new file mode 100644 index 0000000000..49b802ba61 --- /dev/null +++ b/libs/fluidsynth/src/fluid_samplecache.h @@ -0,0 +1,34 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SAMPLECACHE_H +#define _FLUID_SAMPLECACHE_H + +#include "fluid_sfont.h" +#include "fluid_sffile.h" + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **data, char **data24); + +int fluid_samplecache_unload(const short *sample_data); + +#endif /* _FLUID_SAMPLECACHE_H */ diff --git a/libs/fluidsynth/src/fluid_settings.c b/libs/fluidsynth/src/fluid_settings.c index 181484ecc3..05423384ee 100644 --- a/libs/fluidsynth/src/fluid_settings.c +++ b/libs/fluidsynth/src/fluid_settings.c @@ -3,228 +3,251 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ -#include "fluidsynth_priv.h" #include "fluid_sys.h" #include "fluid_hash.h" #include "fluid_synth.h" -//#include "fluid_cmd.h" -//#include "fluid_adriver.h" -//#include "fluid_mdriver.h" #include "fluid_settings.h" #include "fluid_midi.h" -/* Defined in fluid_filerenderer.c */ -extern void fluid_file_renderer_settings (fluid_settings_t* settings); - /* maximum allowed components of a settings variable (separated by '.') */ #define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */ #define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ -static void fluid_settings_init(fluid_settings_t* settings); -static void fluid_settings_key_destroy_func(void* value); -static void fluid_settings_value_destroy_func(void* value); +static void fluid_settings_init(fluid_settings_t *settings); +static void fluid_settings_key_destroy_func(void *value); +static void fluid_settings_value_destroy_func(void *value); static int fluid_settings_tokenize(const char *s, char *buf, char **ptr); /* Common structure to all settings nodes */ -typedef struct { - int type; /**< fluid_types_enum */ -} fluid_setting_node_t; - -typedef struct { - fluid_setting_node_t node; - char* value; - char* def; - int hints; - fluid_list_t* options; - fluid_str_update_t update; - void* data; +typedef struct +{ + char *value; + char *def; + int hints; + fluid_list_t *options; + fluid_str_update_t update; + void *data; } fluid_str_setting_t; -typedef struct { - fluid_setting_node_t node; - double value; - double def; - double min; - double max; - int hints; - fluid_num_update_t update; - void* data; +typedef struct +{ + double value; + double def; + double min; + double max; + int hints; + fluid_num_update_t update; + void *data; } fluid_num_setting_t; -typedef struct { - fluid_setting_node_t node; - int value; - int def; - int min; - int max; - int hints; - fluid_int_update_t update; - void* data; +typedef struct +{ + int value; + int def; + int min; + int max; + int hints; + fluid_int_update_t update; + void *data; } fluid_int_setting_t; -typedef struct { - fluid_setting_node_t node; - fluid_hashtable_t *hashtable; +typedef struct +{ + fluid_hashtable_t *hashtable; } fluid_set_setting_t; +typedef struct +{ + int type; /**< fluid_types_enum */ -static fluid_str_setting_t* -new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data) + union + { + fluid_str_setting_t str; + fluid_num_setting_t num; + fluid_int_setting_t i; + fluid_set_setting_t set; + }; +} fluid_setting_node_t; + +static fluid_setting_node_t * +new_fluid_str_setting(const char *value, const char *def, int hints) { - fluid_str_setting_t* str; - - str = FLUID_NEW(fluid_str_setting_t); - - if (!str) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - str->node.type = FLUID_STR_TYPE; - 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; + fluid_setting_node_t *node; + fluid_str_setting_t *str; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_STR_TYPE; + + str = &node->str; + str->value = value ? FLUID_STRDUP(value) : NULL; + str->def = def ? FLUID_STRDUP(def) : NULL; + str->hints = hints; + str->options = NULL; + str->update = NULL; + str->data = NULL; + return node; } static void -delete_fluid_str_setting(fluid_str_setting_t* str) +delete_fluid_str_setting(fluid_setting_node_t *node) { - if (!str) return; + fluid_return_if_fail(node != NULL); - if (str->value) FLUID_FREE(str->value); - if (str->def) FLUID_FREE(str->def); + FLUID_ASSERT(node->type == FLUID_STR_TYPE); - if (str->options) { - fluid_list_t* list = str->options; + FLUID_FREE(node->str.value); + FLUID_FREE(node->str.def); - while (list) { - FLUID_FREE (list->data); - list = fluid_list_next(list); - } + if(node->str.options) + { + fluid_list_t *list = node->str.options; + + while(list) + { + FLUID_FREE(list->data); + list = fluid_list_next(list); + } - delete_fluid_list(str->options); - } + delete_fluid_list(node->str.options); + } - FLUID_FREE(str); + FLUID_FREE(node); } -static fluid_num_setting_t* -new_fluid_num_setting(double min, double max, double def, - int hints, fluid_num_update_t fun, void* data) +static fluid_setting_node_t * +new_fluid_num_setting(double min, double max, double def, int hints) { - fluid_num_setting_t* setting; - - setting = FLUID_NEW(fluid_num_setting_t); - - if (!setting) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - setting->node.type = FLUID_NUM_TYPE; - setting->value = def; - setting->def = def; - setting->min = min; - setting->max = max; - setting->hints = hints; - setting->update = fun; - setting->data = data; - return setting; + fluid_setting_node_t *node; + fluid_num_setting_t *num; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_NUM_TYPE; + + num = &node->num; + num->value = def; + num->def = def; + num->min = min; + num->max = max; + num->hints = hints; + num->update = NULL; + num->data = NULL; + + return node; } static void -delete_fluid_num_setting(fluid_num_setting_t* setting) +delete_fluid_num_setting(fluid_setting_node_t *node) { - if (setting) FLUID_FREE(setting); + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_NUM_TYPE); + FLUID_FREE(node); } -static fluid_int_setting_t* -new_fluid_int_setting(int min, int max, int def, - int hints, fluid_int_update_t fun, void* data) +static fluid_setting_node_t * +new_fluid_int_setting(int min, int max, int def, int hints) { - fluid_int_setting_t* setting; - - setting = FLUID_NEW(fluid_int_setting_t); - - if (!setting) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - setting->node.type = FLUID_INT_TYPE; - setting->value = def; - setting->def = def; - setting->min = min; - setting->max = max; - setting->hints = hints; - setting->update = fun; - setting->data = data; - return setting; + fluid_setting_node_t *node; + fluid_int_setting_t *i; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_INT_TYPE; + + i = &node->i; + i->value = def; + i->def = def; + i->min = min; + i->max = max; + i->hints = hints; + i->update = NULL; + i->data = NULL; + return node; } static void -delete_fluid_int_setting(fluid_int_setting_t* setting) +delete_fluid_int_setting(fluid_setting_node_t *node) { - if (setting) FLUID_FREE(setting); + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_INT_TYPE); + FLUID_FREE(node); } -static fluid_set_setting_t* +static fluid_setting_node_t * new_fluid_set_setting(void) { - fluid_set_setting_t* setting; - - setting = FLUID_NEW(fluid_set_setting_t); - - if (!setting) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - setting->node.type = FLUID_SET_TYPE; - setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, - fluid_settings_key_destroy_func, - fluid_settings_value_destroy_func); - if (!setting->hashtable) - { - FLUID_FREE (setting); - return NULL; - } - - return setting; + fluid_setting_node_t *node; + fluid_set_setting_t *set; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_SET_TYPE; + set = &node->set; + + set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + + if(!set->hashtable) + { + FLUID_FREE(node); + return NULL; + } + + return node; } static void -delete_fluid_set_setting(fluid_set_setting_t* setting) +delete_fluid_set_setting(fluid_setting_node_t *node) { - if (setting) - { - delete_fluid_hashtable(setting->hashtable); - FLUID_FREE(setting); - } + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_SET_TYPE); + delete_fluid_hashtable(node->set.hashtable); + FLUID_FREE(node); } /** @@ -234,16 +257,20 @@ delete_fluid_set_setting(fluid_set_setting_t* setting) fluid_settings_t * new_fluid_settings(void) { - fluid_settings_t* settings; + fluid_settings_t *settings; + + settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); - settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, - fluid_settings_key_destroy_func, - fluid_settings_value_destroy_func); - if (settings == NULL) return NULL; + if(settings == NULL) + { + return NULL; + } - fluid_rec_mutex_init (settings->mutex); - fluid_settings_init(settings); - return settings; + fluid_rec_mutex_init(settings->mutex); + fluid_settings_init(settings); + return settings; } /** @@ -251,86 +278,93 @@ new_fluid_settings(void) * @param settings a settings object */ void -delete_fluid_settings(fluid_settings_t* settings) +delete_fluid_settings(fluid_settings_t *settings) { - fluid_return_if_fail (settings != NULL); + fluid_return_if_fail(settings != NULL); - fluid_rec_mutex_destroy (settings->mutex); - delete_fluid_hashtable(settings); + fluid_rec_mutex_destroy(settings->mutex); + delete_fluid_hashtable(settings); } /* Settings hash key destroy function */ static void -fluid_settings_key_destroy_func(void* value) +fluid_settings_key_destroy_func(void *value) { - FLUID_FREE (value); /* Free the string key value */ + FLUID_FREE(value); /* Free the string key value */ } /* Settings hash value destroy function */ static void -fluid_settings_value_destroy_func(void* value) +fluid_settings_value_destroy_func(void *value) { - fluid_setting_node_t *node = value; - - switch (node->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_set_setting((fluid_set_setting_t*) value); - break; - } + fluid_setting_node_t *node = value; + + switch(node->type) + { + case FLUID_NUM_TYPE: + delete_fluid_num_setting(node); + break; + + case FLUID_INT_TYPE: + delete_fluid_int_setting(node); + break; + + case FLUID_STR_TYPE: + delete_fluid_str_setting(node); + break; + + case FLUID_SET_TYPE: + delete_fluid_set_setting(node); + break; + } } void -fluid_settings_init(fluid_settings_t* settings) +fluid_settings_init(fluid_settings_t *settings) { - fluid_return_if_fail (settings != NULL); + fluid_return_if_fail(settings != NULL); - fluid_synth_settings(settings); + fluid_synth_settings(settings); #if 0 - fluid_shell_settings(settings); - fluid_player_settings(settings); - fluid_file_renderer_settings(settings); - fluid_audio_driver_settings(settings); - fluid_midi_driver_settings(settings); + fluid_shell_settings(settings); + fluid_player_settings(settings); + fluid_file_renderer_settings(settings); + fluid_audio_driver_settings(settings); + fluid_midi_driver_settings(settings); #endif } static int fluid_settings_tokenize(const char *s, char *buf, char **ptr) { - char *tokstr, *tok; - int n = 0; + char *tokstr, *tok; + int n = 0; - if (strlen (s) > MAX_SETTINGS_LABEL) - { - FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", - MAX_SETTINGS_LABEL); - return 0; - } + if(FLUID_STRLEN(s) > MAX_SETTINGS_LABEL) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", + MAX_SETTINGS_LABEL); + return 0; + } - FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ - tokstr = buf; + FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ + tokstr = buf; - while ((tok = fluid_strtok (&tokstr, "."))) - { - if (n >= MAX_SETTINGS_TOKENS) + while((tok = fluid_strtok(&tokstr, "."))) { - FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", - MAX_SETTINGS_TOKENS); - return 0; - } else - ptr[n++] = tok; - } + if(n >= MAX_SETTINGS_TOKENS) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", + MAX_SETTINGS_TOKENS); + return 0; + } + else + { + ptr[n++] = tok; + } + } - return n; + return n; } /** @@ -339,34 +373,45 @@ fluid_settings_tokenize(const char *s, char *buf, char **ptr) * @param settings a settings object * @param name Settings name * @param value Location to store setting node if found - * @return 1 if the node exists, 0 otherwise + * @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise */ static int -fluid_settings_get(fluid_settings_t* settings, const char *name, +fluid_settings_get(fluid_settings_t *settings, const char *name, fluid_setting_node_t **value) { - fluid_hashtable_t* table = settings; - fluid_setting_node_t *node = NULL; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int ntokens; - int n; + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node = NULL; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int ntokens; + int n; + + ntokens = fluid_settings_tokenize(name, buf, tokens); - ntokens = fluid_settings_tokenize (name, buf, tokens); + if(table == NULL || ntokens <= 0) + { + return FLUID_FAILED; + } - if (table == NULL || ntokens <= 0) return 0; + for(n = 0; n < ntokens; n++) + { - for (n = 0; n < ntokens; n++) { + node = fluid_hashtable_lookup(table, tokens[n]); - node = fluid_hashtable_lookup(table, tokens[n]); - if (!node) return 0; + if(!node) + { + return FLUID_FAILED; + } - table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL; - } + table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL; + } - if (value) *value = node; + if(value) + { + *value = node; + } - return 1; + return FLUID_OK; } /** @@ -375,583 +420,741 @@ fluid_settings_get(fluid_settings_t* settings, const char *name, * @param settings a settings object * @param name Settings name * @param value Node instance to assign (used directly) - * @return 1 if the value has been set, zero otherwise + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ static int -fluid_settings_set(fluid_settings_t* settings, const char *name, void* value) +fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *value) { - fluid_hashtable_t* table = settings; - fluid_setting_node_t *node; - char* tokens[MAX_SETTINGS_TOKENS]; - char buf[MAX_SETTINGS_LABEL+1]; - int n, num; - char *dupname; - - num = fluid_settings_tokenize (name, buf, tokens) - 1; - if (num == 0) - return 0; + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int n, num; + char *dupname; - for (n = 0; n < num; n++) { + num = fluid_settings_tokenize(name, buf, tokens); - node = fluid_hashtable_lookup(table, tokens[n]); + if(num == 0) + { + return FLUID_FAILED; + } - if (node) { + num--; - if (node->type == FLUID_SET_TYPE) { - table = ((fluid_set_setting_t *)node)->hashtable; - } else { - /* path ends prematurely */ - FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); - return 0; - } + for(n = 0; n < num; n++) + { - } else { - /* create a new node */ - fluid_set_setting_t* setnode; + node = fluid_hashtable_lookup(table, tokens[n]); - dupname = FLUID_STRDUP (tokens[n]); - setnode = new_fluid_set_setting (); + if(node) + { - if (!dupname || !setnode) - { - if (dupname) FLUID_FREE (dupname); - else FLUID_LOG(FLUID_ERR, "Out of memory"); + if(node->type == FLUID_SET_TYPE) + { + table = node->set.hashtable; + } + else + { + /* path ends prematurely */ + FLUID_LOG(FLUID_WARN, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); + return FLUID_FAILED; + } - if (setnode) delete_fluid_set_setting (setnode); + } + else + { + /* create a new node */ + fluid_setting_node_t *setnode; + + dupname = FLUID_STRDUP(tokens[n]); + setnode = new_fluid_set_setting(); + + if(!dupname || !setnode) + { + if(dupname) + { + FLUID_FREE(dupname); + } + else + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(setnode) + { + delete_fluid_set_setting(setnode); + } + + return FLUID_FAILED; + } + + fluid_hashtable_insert(table, dupname, setnode); + table = setnode->set.hashtable; + } + } - return 0; - } + dupname = FLUID_STRDUP(tokens[num]); - fluid_hashtable_insert(table, dupname, setnode); - table = setnode->hashtable; + if(!dupname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; } - } - dupname = FLUID_STRDUP (tokens[num]); + fluid_hashtable_insert(table, dupname, value); - if (!dupname) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return 0; - } + return FLUID_OK; +} + +/** + * Registers a new string value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_hashtable_insert(table, dupname, value); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); - return 1; + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + node = new_fluid_str_setting(def, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_str_setting(node); + } + } + else + { + /* if variable already exists, don't change its value. */ + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + setting->def = def ? FLUID_STRDUP(def) : NULL; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; } -/** returns 1 if the value has been registered correctly, 0 - otherwise */ +/** + * Registers a new float value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ int -fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints, - fluid_str_update_t fun, void* data) +fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints) { - fluid_setting_node_t *node; - fluid_str_setting_t* setting; - int retval; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - - fluid_rec_mutex_lock (settings->mutex); - - if (!fluid_settings_get(settings, name, &node)) { - setting = new_fluid_str_setting(def, def, hints, fun, data); - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_str_setting (setting); - } else { - /* if variable already exists, don't change its value. */ - if (node->type == FLUID_STR_TYPE) { - setting = (fluid_str_setting_t*) node; - setting->update = fun; - setting->data = data; - setting->def = def? FLUID_STRDUP(def) : NULL; - setting->hints = hints; - retval = 1; - } else { - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - retval = 0; - } - } - - fluid_rec_mutex_unlock (settings->mutex); - - return retval; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all floating point settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_num_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_num_setting(node); + } + } + else + { + if(node->type == FLUID_NUM_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_num_setting_t *setting = &node->num; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; } -/** returns 1 if the value has been register correctly, zero - otherwise */ +/** + * Registers a new integer value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED 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) +fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints) { - fluid_setting_node_t *node; - int retval; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - - /* For now, all floating point settings are bounded below and above */ - hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; - - fluid_rec_mutex_lock (settings->mutex); - - if (!fluid_settings_get(settings, name, &node)) { - /* insert a new setting */ - fluid_num_setting_t* setting; - setting = new_fluid_num_setting(min, max, def, hints, fun, data); - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_num_setting (setting); - } else { - if (node->type == FLUID_NUM_TYPE) { - /* update the existing setting but don't change its value */ - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - setting->update = fun; - setting->data = data; - setting->min = min; - setting->max = max; - setting->def = def; - setting->hints = hints; - retval = 1; - } else { - /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - retval = 0; - } - } - - fluid_rec_mutex_unlock (settings->mutex); - - return retval; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all integer settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_int_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_int_setting(node); + } + } + else + { + if(node->type == FLUID_INT_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_int_setting_t *setting = &node->i; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; } -/** 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) +/** + * Registers a callback for the specified string setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t callback, void *data) { - fluid_setting_node_t *node; - int retval; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - - /* For now, all integer settings are bounded below and above */ - hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; - - fluid_rec_mutex_lock (settings->mutex); - - if (!fluid_settings_get(settings, name, &node)) { - /* insert a new setting */ - fluid_int_setting_t* setting; - setting = new_fluid_int_setting(min, max, def, hints, fun, data); - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_int_setting (setting); - } else { - if (node->type == FLUID_INT_TYPE) { - /* update the existing setting but don't change its value */ - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - setting->update = fun; - setting->data = data; - setting->min = min; - setting->max = max; - setting->def = def; - setting->hints = hints; - retval = 1; - } else { - /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - retval = 0; - } - } - - fluid_rec_mutex_unlock (settings->mutex); - - return retval; + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->str; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; } /** - * Get the type of the setting with the given name + * Registers a callback for the specified numeric setting. * * @param settings a settings object - * @param name a setting's name - * @return the type for the named setting, or #FLUID_NO_TYPE when it does not exist + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise */ -int -fluid_settings_get_type(fluid_settings_t* settings, const char *name) +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t callback, void *data) { - fluid_setting_node_t *node; - int type; + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE); - fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE); - fluid_return_val_if_fail (name[0] != '\0', FLUID_NO_TYPE); + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_NUM_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } - fluid_rec_mutex_lock (settings->mutex); - type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE; - fluid_rec_mutex_unlock (settings->mutex); + setting = &node->num; + setting->update = callback; + setting->data = data; - return (type); + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; } /** - * Get the hints for the named setting as an integer bitmap + * Registers a callback for the specified int setting. * * @param settings a settings object - * @param name a setting's name - * @return the hints associated to the named setting if it exists, zero otherwise + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise */ -int -fluid_settings_get_hints(fluid_settings_t* settings, const char *name) +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t callback, void *data) { - fluid_setting_node_t *node; - int hints = 0; + fluid_setting_node_t *node; + fluid_int_setting_t *setting; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node)) { - if (node->type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - hints = setting->hints; - } else if (node->type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) node; - hints = setting->hints; - } else if (node->type == FLUID_INT_TYPE) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - hints = setting->hints; + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_INT_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; } - } - fluid_rec_mutex_unlock (settings->mutex); + setting = &node->i; + setting->update = callback; + setting->data = data; - return hints; + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; } /** - * Ask whether the setting is changeable in real-time. + * Get the type of the setting with the given name * * @param settings a settings object * @param name a setting's name - * @return non zero if the setting is changeable in real-time + * @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist */ int -fluid_settings_is_realtime(fluid_settings_t* settings, const char *name) +fluid_settings_get_type(fluid_settings_t *settings, const char *name) { - fluid_setting_node_t *node; - int isrealtime = FALSE; + fluid_setting_node_t *node; + int type = FLUID_NO_TYPE; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_return_val_if_fail(settings != NULL, type); + fluid_return_val_if_fail(name != NULL, type); + fluid_return_val_if_fail(name[0] != '\0', type); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node)) { - if (node->type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - isrealtime = setting->update != NULL; - } else if (node->type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) node; - isrealtime = setting->update != NULL; - } else if (node->type == FLUID_INT_TYPE) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - isrealtime = setting->update != NULL; + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + type = node->type; } - } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return isrealtime; + return type; } /** - * Set a string value for a named setting + * Get the hints for the named setting as an integer bitmap * * @param settings a settings object * @param name a setting's name - * @param str new string value - * @return 1 if the value has been set, 0 otherwise + * @param hints set to the hints associated to the setting if it exists + * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise */ int -fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str) +fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_rec_mutex_lock(settings->mutex); - fluid_rec_mutex_lock (settings->mutex); + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + *hints = setting->hints; + retval = FLUID_OK; + } + } - if (fluid_settings_get (settings, name, &node)) { - if (node->type == FLUID_STR_TYPE) { - fluid_str_setting_t *setting = (fluid_str_setting_t *)node; + fluid_rec_mutex_unlock(settings->mutex); - if (setting->value) FLUID_FREE (setting->value); - setting->value = str ? FLUID_STRDUP (str) : NULL; + return retval; +} - /* Call under lock to keep update() synchronized with the current value */ - if (setting->update) (*setting->update)(setting->data, name, str); - retval = 1; - } - else if (node->type == FLUID_INT_TYPE) /* Handle yes/no for boolean values for backwards compatibility */ - { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; +/** + * Ask whether the setting is changeable in real-time. + * + * @param settings a settings object + * @param name a setting's name + * @return TRUE if the setting is changeable in real-time, FALSE otherwise + */ +int +fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int isrealtime = FALSE; + + fluid_return_val_if_fail(settings != NULL, 0); + fluid_return_val_if_fail(name != NULL, 0); + fluid_return_val_if_fail(name[0] != '\0', 0); - if (setting->hints & FLUID_HINT_TOGGLED) - { - if (FLUID_STRCMP (str, "yes") == 0) + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + isrealtime = setting->update != NULL; + } + else if(node->type == FLUID_STR_TYPE) { - setting->value = TRUE; - if (setting->update) (*setting->update)(setting->data, name, TRUE); + fluid_str_setting_t *setting = &node->str; + isrealtime = setting->update != NULL; } - else if (FLUID_STRCMP (str, "no") == 0) + else if(node->type == FLUID_INT_TYPE) { - setting->value = FALSE; - if (setting->update) (*setting->update)(setting->data, name, FALSE); + fluid_int_setting_t *setting = &node->i; + isrealtime = setting->update != NULL; } - } } - } else { - /* insert a new setting */ - fluid_str_setting_t* setting; - setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_str_setting (setting); - } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return isrealtime; } /** - * Copy the value of a string setting + * Set a string value for a named setting + * * @param settings a settings object * @param name a setting's name - * @param str Caller supplied buffer to copy string value to - * @param len Size of 'str' buffer (no more than len bytes will be written, which - * will always include a zero terminator) - * @return 1 if the value exists, 0 otherwise - * @since 1.1.0 - * - * Like fluid_settings_getstr() but is thread safe. A size of 256 should be - * more than sufficient for the string buffer. + * @param str new string value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int -fluid_settings_copystr(fluid_settings_t* settings, const char *name, - char *str, int len) +fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str) { - fluid_setting_node_t *node; - int retval = 0; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (str != NULL, 0); - fluid_return_val_if_fail (len > 0, 0); + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + char *new_value = NULL; + fluid_str_update_t callback = NULL; + void *data = NULL; - str[0] = 0; + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get (settings, name, &node)) - { - if (node->type == FLUID_STR_TYPE) + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_STR_TYPE)) { - fluid_str_setting_t *setting = (fluid_str_setting_t *)node; + goto error_recovery; + } - if (setting->value) - { - FLUID_STRNCPY (str, setting->value, len); - str[len - 1] = 0; /* Force terminate, in case of truncation */ - } + setting = &node->str; - retval = 1; + if(setting->value) + { + FLUID_FREE(setting->value); } - else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + + if(str) { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; + new_value = FLUID_STRDUP(str); + + if(new_value == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + setting->value = new_value; - if (setting->hints & FLUID_HINT_TOGGLED) - { - FLUID_STRNCPY (str, setting->value ? "yes" : "no", len); - str[len - 1] = 0; /* Force terminate, in case of truncation */ + callback = setting->update; + data = setting->data; - retval = 1; - } + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, new_value); } - } - fluid_rec_mutex_unlock (settings->mutex); + return FLUID_OK; - return retval; +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; } /** - * Duplicate the value of a string setting + * Copy the value of a string setting into the provided buffer (thread safe) * @param settings a settings object * @param name a setting's name - * @param str Location to store pointer to allocated duplicate string - * @return 1 if the value exists and was successfully duplicated, 0 otherwise + * @param str Caller supplied buffer to copy string value to + * @param len Size of 'str' buffer (no more than len bytes will be written, which + * will always include a zero terminator) + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise * @since 1.1.0 * - * Like fluid_settings_copystr() but allocates a new copy of the string. Caller - * owns the string and should free it with free() when done using it. + * @note A size of 256 should be more than sufficient for the string buffer. */ int -fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str) +fluid_settings_copystr(fluid_settings_t *settings, const char *name, + char *str, int len) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); + fluid_return_val_if_fail(len > 0, retval); - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (str != NULL, 0); + str[0] = 0; - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node)) - { - if (node->type == FLUID_STR_TYPE) + if(fluid_settings_get(settings, name, &node) == FLUID_OK) { - fluid_str_setting_t *setting = (fluid_str_setting_t *)node; + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; - if (setting->value) - { - *str = FLUID_STRDUP (setting->value); - if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory"); - } + if(setting->value) + { + FLUID_STRNCPY(str, setting->value, len); + } - if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ - } - else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ - { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; - if (setting->hints & FLUID_HINT_TOGGLED) - { - *str = FLUID_STRDUP (setting->value ? "yes" : "no"); - if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory"); + if(setting->hints & FLUID_HINT_TOGGLED) + { + FLUID_STRNCPY(str, setting->value ? "yes" : "no", len); - if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ - } + retval = FLUID_OK; + } + } } - } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** - * Get the value of a string setting + * Duplicate the value of a string setting * @param settings a settings object * @param name a setting's name - * @param str Location to store pointer to the settings string value - * @return 1 if the value exists, 0 otherwise - * @deprecated - * - * 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 and it - * is valid only until a new value is assigned to the setting of the given name. + * @param str Location to store pointer to allocated duplicate string + * @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise + * @since 1.1.0 * - * NOTE: In a multi-threaded environment, caller must ensure that the setting - * being read by fluid_settings_getstr() is not assigned during the - * duration of callers use of the setting's value. Use fluid_settings_copystr() - * or fluid_settings_dupstr() which does not have this restriction. + * Like fluid_settings_copystr() but allocates a new copy of the string. Caller + * owns the string and should free it with free() when done using it. */ int -fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str) +fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (str != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node)) - { - if (node->type == FLUID_STR_TYPE) + if(fluid_settings_get(settings, name, &node) == FLUID_OK) { - fluid_str_setting_t *setting = (fluid_str_setting_t *)node; - *str = setting->value; - retval = 1; - } - else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ - { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; - - if (setting->hints & FLUID_HINT_TOGGLED) - { - *str = setting->value ? "yes" : "no"; - retval = 1; - } + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + *str = FLUID_STRDUP(setting->value); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + *str = FLUID_STRDUP(setting->value ? "yes" : "no"); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + } } - } - else *str = NULL; - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } + /** * Test a string setting for some value. * * @param settings a settings object * @param name a setting's name * @param s a string to be tested - * @return 1 if the value exists and is equal to 's', 0 otherwise + * @return TRUE if the value exists and is equal to 's', FALSE otherwise */ int -fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s) +fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FALSE; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (s != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get (settings, name, &node)) - { - if (node->type == FLUID_STR_TYPE) - { - fluid_str_setting_t *setting = (fluid_str_setting_t *)node; - if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0; - } - else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + if(fluid_settings_get(settings, name, &node) == FLUID_OK) { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + retval = FLUID_STRCMP(setting->value, s) == 0; + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; - if (setting->hints & FLUID_HINT_TOGGLED) - retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0; + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0; + } + } } - } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -960,39 +1163,43 @@ fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const ch * * @param settings a settings object * @param name a setting's name - * @return the default string value of the setting if it exists, NULL otherwise + * @param def the default string value of the setting if it exists + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -char* -fluid_settings_getstr_default(fluid_settings_t* settings, const char *name) +int +fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) { - fluid_setting_node_t *node; - char *retval = NULL; + fluid_setting_node_t *node; + char *retval = NULL; - fluid_return_val_if_fail (settings != NULL, NULL); - fluid_return_val_if_fail (name != NULL, NULL); - fluid_return_val_if_fail (name[0] != '\0', NULL); + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get (settings, name, &node)) - { - if (node->type == FLUID_STR_TYPE) + if(fluid_settings_get(settings, name, &node) == FLUID_OK) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) node; - retval = setting->def; - } - else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ - { - fluid_int_setting_t *setting = (fluid_int_setting_t *)node; + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->def; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; - if (setting->hints & FLUID_HINT_TOGGLED) - retval = setting->def ? "yes" : "no"; + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = setting->def ? "yes" : "no"; + } + } } - } - fluid_rec_mutex_unlock (settings->mutex); + *def = retval; + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval != NULL ? FLUID_OK : FLUID_FAILED; } /** @@ -1000,35 +1207,36 @@ fluid_settings_getstr_default(fluid_settings_t* settings, const char *name) * @param settings a settings object * @param name a setting's name * @param s option string to add - * @return 1 if the setting exists and option was added, 0 otherwise + * @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise * * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set. */ int -fluid_settings_add_option(fluid_settings_t* settings, const char *name, const char *s) +fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (s != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) node; - char* copy = FLUID_STRDUP(s); - setting->options = fluid_list_append(setting->options, copy); - setting->hints |= FLUID_HINT_OPTIONLIST; - retval = 1; - } + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { + fluid_str_setting_t *setting = &node->str; + char *copy = FLUID_STRDUP(s); + setting->options = fluid_list_append(setting->options, copy); + setting->hints |= FLUID_HINT_OPTIONLIST; + retval = FLUID_OK; + } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -1036,42 +1244,47 @@ fluid_settings_add_option(fluid_settings_t* settings, const char *name, const ch * @param settings a settings object * @param name a setting's name * @param s option string to remove - * @return 1 if the setting exists and option was removed, 0 otherwise + * @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise */ int -fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s) +fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (s != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_STR_TYPE)) { + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { - fluid_str_setting_t* setting = (fluid_str_setting_t*) node; - fluid_list_t* list = setting->options; + fluid_str_setting_t *setting = &node->str; + fluid_list_t *list = setting->options; - while (list) { - char* option = (char*) fluid_list_get(list); - if (FLUID_STRCMP(s, option) == 0) { - FLUID_FREE (option); - setting->options = fluid_list_remove_link(setting->options, list); - retval = 1; - break; - } - list = fluid_list_next(list); + while(list) + { + char *option = (char *) fluid_list_get(list); + + if(FLUID_STRCMP(s, option) == 0) + { + FLUID_FREE(option); + setting->options = fluid_list_remove_link(setting->options, list); + retval = FLUID_OK; + break; + } + + list = fluid_list_next(list); + } } - } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -1080,46 +1293,55 @@ fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const * @param settings a settings object * @param name a setting's name * @param val new setting's value - * @return 1 if the value has been set, 0 otherwise + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int -fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val) +fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) { - fluid_setting_node_t *node; - fluid_num_setting_t* setting; - int retval = 0; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + fluid_num_update_t callback = NULL; + void *data = NULL; - fluid_rec_mutex_lock (settings->mutex); + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - if (fluid_settings_get(settings, name, &node)) { - if (node->type == FLUID_NUM_TYPE) { - setting = (fluid_num_setting_t*) node; + fluid_rec_mutex_lock(settings->mutex); - if (val < setting->min) val = setting->min; - else if (val > setting->max) val = setting->max; + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_NUM_TYPE)) + { + goto error_recovery; + } - setting->value = val; + setting = &node->num; - /* Call under lock to keep update() synchronized with the current value */ - if (setting->update) (*setting->update)(setting->data, name, val); - retval = 1; + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name); + goto error_recovery; } - } 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; - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_num_setting (setting); - } - fluid_rec_mutex_unlock (settings->mutex); + callback = setting->update; + data = setting->data; - return retval; + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; } /** @@ -1128,31 +1350,53 @@ fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val) * @param settings a settings object * @param name a setting's name * @param val variable pointer to receive the setting's numeric value - * @return 1 if the value exists, 0 otherwise + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise */ int -fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val) +fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (val != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - *val = setting->value; - retval = 1; - } + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->value; + retval = FLUID_OK; + } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; +} + +/** + * float-typed wrapper for fluid_settings_getnum + * + * @param settings a settings object + * @param name a setting's name + * @param val variable pointer to receive the setting's float value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val) +{ + double tmp; + + if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK) + { + *val = tmp; + return FLUID_OK; + } + + return FLUID_FAILED; } /** @@ -1162,29 +1406,35 @@ fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val) * @param name a setting's name * @param min setting's range lower limit * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise */ -void -fluid_settings_getnum_range(fluid_settings_t* settings, const char *name, - double* min, double* max) +int +fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max) { - fluid_setting_node_t *node; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); - fluid_return_if_fail (settings != NULL); - fluid_return_if_fail (name != NULL); - fluid_return_if_fail (name[0] != '\0'); - fluid_return_if_fail (min != NULL); - fluid_return_if_fail (max != NULL); + fluid_rec_mutex_lock(settings->mutex); - fluid_rec_mutex_lock (settings->mutex); + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - *min = setting->min; - *max = setting->max; - } + fluid_rec_mutex_unlock(settings->mutex); - fluid_rec_mutex_unlock (settings->mutex); + return retval; } /** @@ -1192,29 +1442,33 @@ fluid_settings_getnum_range(fluid_settings_t* settings, const char *name, * * @param settings a settings object * @param name a setting's name - * @return the default value if the named setting exists, 0.0f otherwise + * @param val set to the default value if the named setting exists + * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise */ -double -fluid_settings_getnum_default(fluid_settings_t* settings, const char *name) +int +fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val) { - fluid_setting_node_t *node; - double retval = 0.0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0.0); - fluid_return_val_if_fail (name != NULL, 0.0); - fluid_return_val_if_fail (name[0] != '\0', 0.0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) node; - retval = setting->def; - } + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->def; + retval = FLUID_OK; + } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -1223,46 +1477,55 @@ fluid_settings_getnum_default(fluid_settings_t* settings, const char *name) * @param settings a settings object * @param name a setting's name * @param val new setting's integer value - * @return 1 if the value has been set, 0 otherwise + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise */ int -fluid_settings_setint(fluid_settings_t* settings, const char *name, int val) +fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) { - fluid_setting_node_t *node; - fluid_int_setting_t* setting; - int retval = 0; - - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_setting_node_t *node; + fluid_int_setting_t *setting; + fluid_int_update_t callback = NULL; + void *data = NULL; - fluid_rec_mutex_lock (settings->mutex); + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); - if (fluid_settings_get(settings, name, &node)) { - if (node->type == FLUID_INT_TYPE) { - setting = (fluid_int_setting_t*) node; + fluid_rec_mutex_lock(settings->mutex); - if (val < setting->min) val = setting->min; - else if (val > setting->max) val = setting->max; + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_INT_TYPE)) + { + goto error_recovery; + } - setting->value = val; + setting = &node->i; - /* Call under lock to keep update() synchronized with the current value */ - if (setting->update) (*setting->update)(setting->data, name, val); - retval = 1; + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name); + goto error_recovery; } - } 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; - retval = fluid_settings_set(settings, name, setting); - if (retval != 1) delete_fluid_int_setting (setting); - } - fluid_rec_mutex_unlock (settings->mutex); + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; - return retval; +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; } /** @@ -1271,31 +1534,32 @@ fluid_settings_setint(fluid_settings_t* settings, const char *name, int val) * @param settings a settings object * @param name a setting's name * @param val pointer to a variable to receive the setting's integer value - * @return 1 if the value exists, 0 otherwise + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise */ int -fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val) +fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); - fluid_return_val_if_fail (val != NULL, 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - *val = setting->value; - retval = 1; - } + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->value; + retval = FLUID_OK; + } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -1304,29 +1568,35 @@ fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val) * @param name a setting's name * @param min setting's range lower limit * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise */ -void -fluid_settings_getint_range(fluid_settings_t* settings, const char *name, - int* min, int* max) +int +fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max) { - fluid_setting_node_t *node; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_if_fail (settings != NULL); - fluid_return_if_fail (name != NULL); - fluid_return_if_fail (name[0] != '\0'); - fluid_return_if_fail (min != NULL); - fluid_return_if_fail (max != NULL); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - *min = setting->min; - *max = setting->max; - } + fluid_rec_mutex_unlock(settings->mutex); - fluid_rec_mutex_unlock (settings->mutex); + return retval; } /** @@ -1334,29 +1604,32 @@ fluid_settings_getint_range(fluid_settings_t* settings, const char *name, * * @param settings a settings object * @param name a setting's name - * @return the setting's default integer value it it exists, zero otherwise + * @param val set to the setting's default integer value if it exists + * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise */ -int -fluid_settings_getint_default(fluid_settings_t* settings, const char *name) +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val) { - fluid_setting_node_t *node; - int retval = 0; + fluid_setting_node_t *node; + int retval = FLUID_FAILED; - fluid_return_val_if_fail (settings != NULL, 0); - fluid_return_val_if_fail (name != NULL, 0); - fluid_return_val_if_fail (name[0] != '\0', 0); + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); - fluid_rec_mutex_lock (settings->mutex); + fluid_rec_mutex_lock(settings->mutex); - if (fluid_settings_get(settings, name, &node) - && (node->type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) node; - retval = setting->def; - } + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->def; + retval = FLUID_OK; + } - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return retval; + return retval; } /** @@ -1368,45 +1641,50 @@ fluid_settings_getint_default(fluid_settings_t* settings, const char *name) * @param data any user provided pointer * @param func callback function to be called on each iteration * - * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each + * @note Starting with FluidSynth 1.1.0 the \a func callback is called for each * option in alphabetical order. Sort order was undefined in previous versions. */ void -fluid_settings_foreach_option (fluid_settings_t* settings, const char *name, - void* data, fluid_settings_foreach_option_t func) +fluid_settings_foreach_option(fluid_settings_t *settings, const char *name, + void *data, fluid_settings_foreach_option_t func) { - fluid_setting_node_t *node; - fluid_str_setting_t *setting; - fluid_list_t *p, *newlist = NULL; + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + fluid_list_t *p, *newlist = NULL; - fluid_return_if_fail (settings != NULL); - fluid_return_if_fail (name != NULL); - fluid_return_if_fail (name[0] != '\0'); - fluid_return_if_fail (func != NULL); + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(name != NULL); + fluid_return_if_fail(name[0] != '\0'); + fluid_return_if_fail(func != NULL); - fluid_rec_mutex_lock (settings->mutex); /* ++ lock */ + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ - if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE) - { - fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ - return; - } + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return; + } - setting = (fluid_str_setting_t*)node; + setting = &node->str; - /* Duplicate option list */ - for (p = setting->options; p; p = p->next) - newlist = fluid_list_append (newlist, fluid_list_get (p)); + /* Duplicate option list */ + for(p = setting->options; p; p = p->next) + { + newlist = fluid_list_append(newlist, fluid_list_get(p)); + } - /* Sort by name */ - newlist = fluid_list_sort (newlist, fluid_list_str_compare_func); + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); - for (p = newlist; p; p = p->next) - (*func)(data, (char *)name, (char *)fluid_list_get (p)); + for(p = newlist; p; p = p->next) + { + (*func)(data, name, (const char *)fluid_list_get(p)); + } - fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ - delete_fluid_list (newlist); + delete_fluid_list(newlist); } /** @@ -1418,21 +1696,26 @@ fluid_settings_foreach_option (fluid_settings_t* settings, const char *name, * @since 1.1.0 */ int -fluid_settings_option_count (fluid_settings_t *settings, const char *name) +fluid_settings_option_count(fluid_settings_t *settings, const char *name) { - fluid_setting_node_t *node; - int count = -1; + fluid_setting_node_t *node; + int count = -1; - fluid_return_val_if_fail (settings != NULL, -1); - fluid_return_val_if_fail (name != NULL, -1); - fluid_return_val_if_fail (name[0] != '\0', -1); + fluid_return_val_if_fail(settings != NULL, -1); + fluid_return_val_if_fail(name != NULL, -1); + fluid_return_val_if_fail(name[0] != '\0', -1); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && node->type == FLUID_STR_TYPE) + { + count = fluid_list_size(node->str.options); + } - fluid_rec_mutex_lock (settings->mutex); - if (fluid_settings_get(settings, name, &node) && node->type == FLUID_STR_TYPE) - count = fluid_list_size (((fluid_str_setting_t *)node)->options); - fluid_rec_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock(settings->mutex); - return (count); + return (count); } /** @@ -1445,113 +1728,136 @@ fluid_settings_option_count (fluid_settings_t *settings, const char *name) * @since 1.1.0 */ char * -fluid_settings_option_concat (fluid_settings_t *settings, const char *name, - const char *separator) +fluid_settings_option_concat(fluid_settings_t *settings, const char *name, + const char *separator) { - fluid_setting_node_t *node; - fluid_str_setting_t *setting; - fluid_list_t *p, *newlist = NULL; - int count, len; - char *str, *option; + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + fluid_list_t *p, *newlist = NULL; + size_t count, len; + char *str, *option; - fluid_return_val_if_fail (settings != NULL, NULL); - fluid_return_val_if_fail (name != NULL, NULL); - fluid_return_val_if_fail (name[0] != '\0', NULL); + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); - if (!separator) separator = ", "; + if(!separator) + { + separator = ", "; + } - fluid_rec_mutex_lock (settings->mutex); /* ++ lock */ + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ - if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE) - { - fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ - return (NULL); - } + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return (NULL); + } - setting = (fluid_str_setting_t*)node; + setting = &node->str; - /* Duplicate option list, count options and get total string length */ - for (p = setting->options, count = 0, len = 0; p; p = p->next, count++) - { - option = fluid_list_get (p); + /* Duplicate option list, count options and get total string length */ + for(p = setting->options, count = 0, len = 0; p; p = p->next, count++) + { + option = fluid_list_get(p); - if (option) + if(option) + { + newlist = fluid_list_append(newlist, option); + len += FLUID_STRLEN(option); + } + } + + if(count > 1) { - newlist = fluid_list_append (newlist, option); - len += strlen (option); + len += (count - 1) * FLUID_STRLEN(separator); } - } - if (count > 1) len += (count - 1) * strlen (separator); - len++; /* For terminator */ + len++; /* For terminator */ - /* Sort by name */ - newlist = fluid_list_sort (newlist, fluid_list_str_compare_func); + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); - str = FLUID_MALLOC (len); + str = FLUID_MALLOC(len); - if (str) - { - str[0] = 0; - for (p = newlist; p; p = p->next) + if(str) { - option = fluid_list_get (p); - strcat (str, option); - if (p->next) strcat (str, separator); + str[0] = 0; + + for(p = newlist; p; p = p->next) + { + option = fluid_list_get(p); + strcat(str, option); + + if(p->next) + { + strcat(str, separator); + } + } } - } - fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ - delete_fluid_list (newlist); + delete_fluid_list(newlist); - if (!str) FLUID_LOG (FLUID_ERR, "Out of memory"); + if(!str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } - return (str); + return (str); } /* Structure passed to fluid_settings_foreach_iter recursive function */ typedef struct { - char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */ - fluid_list_t *names; /* For fluid_settings_foreach() */ + char path[MAX_SETTINGS_LABEL + 1]; /* Maximum settings label length */ + fluid_list_t *names; /* For fluid_settings_foreach() */ } fluid_settings_foreach_bag_t; static int -fluid_settings_foreach_iter (void* key, void* value, void* data) +fluid_settings_foreach_iter(void *key, void *value, void *data) { - fluid_settings_foreach_bag_t *bag = data; - char *keystr = key; - fluid_setting_node_t *node = value; - int pathlen; - char *s; - - pathlen = strlen (bag->path); - - if (pathlen > 0) - { - bag->path[pathlen] = '.'; - bag->path[pathlen + 1] = 0; - } - - strcat (bag->path, keystr); - - switch (node->type) { - case FLUID_NUM_TYPE: - case FLUID_INT_TYPE: - case FLUID_STR_TYPE: - s = FLUID_STRDUP (bag->path); - if (s) bag->names = fluid_list_append (bag->names, s); - break; - case FLUID_SET_TYPE: - fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable, - fluid_settings_foreach_iter, bag); - break; - } - - bag->path[pathlen] = 0; - - return 0; + fluid_settings_foreach_bag_t *bag = data; + char *keystr = key; + fluid_setting_node_t *node = value; + size_t pathlen; + char *s; + + pathlen = FLUID_STRLEN(bag->path); + + if(pathlen > 0) + { + bag->path[pathlen] = '.'; + bag->path[pathlen + 1] = 0; + } + + strcat(bag->path, keystr); + + switch(node->type) + { + case FLUID_NUM_TYPE: + case FLUID_INT_TYPE: + case FLUID_STR_TYPE: + s = FLUID_STRDUP(bag->path); + + if(s) + { + bag->names = fluid_list_append(bag->names, s); + } + + break; + + case FLUID_SET_TYPE: + fluid_hashtable_foreach(node->set.hashtable, + fluid_settings_foreach_iter, bag); + break; + } + + bag->path[pathlen] = 0; + + return 0; } /** @@ -1562,41 +1868,78 @@ fluid_settings_foreach_iter (void* key, void* value, void* data) * @param data any user provided pointer * @param func callback function to be called on each iteration * - * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each + * @note Starting with FluidSynth 1.1.0 the \a func callback is called for each * setting in alphabetical order. Sort order was undefined in previous versions. */ void -fluid_settings_foreach (fluid_settings_t* settings, void* data, - fluid_settings_foreach_t func) +fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func) { - fluid_settings_foreach_bag_t bag; - fluid_setting_node_t *node; - fluid_list_t *p; - int r; + fluid_settings_foreach_bag_t bag; + fluid_setting_node_t *node; + fluid_list_t *p; + + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(func != NULL); + + bag.path[0] = 0; + bag.names = NULL; + + fluid_rec_mutex_lock(settings->mutex); + + /* Add all node names to the bag.names list */ + fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag); + + /* Sort names */ + bag.names = fluid_list_sort(bag.names, fluid_list_str_compare_func); + + /* Loop over names and call the callback */ + for(p = bag.names; p; p = p->next) + { + if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK + && node) + { + (*func)(data, (const char *)(p->data), node->type); + } + + FLUID_FREE(p->data); /* -- Free name */ + } - fluid_return_if_fail (settings != NULL); - fluid_return_if_fail (func != NULL); + fluid_rec_mutex_unlock(settings->mutex); - bag.path[0] = 0; - bag.names = NULL; + delete_fluid_list(bag.names); /* -- Free names list */ +} - fluid_rec_mutex_lock (settings->mutex); +/** + * Split a comma-separated list of integers and fill the passed + * in buffer with the parsed values. + * + * @param str the comma-separated string to split + * @param buf user-supplied buffer to hold the parsed numbers + * @param buf_len length of user-supplied buffer + * @return number of parsed values or -1 on failure + */ +int fluid_settings_split_csv(const char *str, int *buf, int buf_len) +{ + char *s; + char *tok; + char *tokstr; + int n = 0; - /* Add all node names to the bag.names list */ - fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag); + s = tokstr = FLUID_STRDUP(str); - /* Sort names */ - bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func); + if(s == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return -1; + } - /* Loop over names and call the callback */ - for (p = bag.names; p; p = p->next) - { - r = fluid_settings_get (settings, (char *)(p->data), &node); - if (r && node) (*func) (data, (char *)(p->data), node->type); - FLUID_FREE (p->data); /* -- Free name */ - } + while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len) + { + buf[n++] = atoi(tok); + } - fluid_rec_mutex_unlock (settings->mutex); + FLUID_FREE(s); - delete_fluid_list (bag.names); /* -- Free names list */ + return n; } diff --git a/libs/fluidsynth/src/fluid_settings.h b/libs/fluidsynth/src/fluid_settings.h index 244f0b457b..4a952f1bad 100644 --- a/libs/fluidsynth/src/fluid_settings.h +++ b/libs/fluidsynth/src/fluid_settings.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -22,35 +22,34 @@ #ifndef _FLUID_SETTINGS_H #define _FLUID_SETTINGS_H +int fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s); +int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s); -/** returns 1 if the option was added, 0 otherwise */ -int fluid_settings_add_option(fluid_settings_t* settings, const char* name, const char* s); +typedef void (*fluid_str_update_t)(void *data, const char *name, const char *value); -/** returns 1 if the option was added, 0 otherwise */ -int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, const char* s); +int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints); +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t fun, void *data); -typedef int (*fluid_num_update_t)(void* data, const char* name, double value); -typedef int (*fluid_str_update_t)(void* data, const char* name, const char* value); -typedef int (*fluid_int_update_t)(void* data, const char* name, int value); +typedef void (*fluid_num_update_t)(void *data, const char *name, double value); -/** returns 0 if the value has been registered correctly, non-zero - otherwise */ -int fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints, - fluid_str_update_t fun, void* data); +int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints); +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t fun, void *data); -/** returns 0 if the value has been registered correctly, non-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); +/* Type specific wrapper for fluid_settings_getnum */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val); -/** returns 0 if the value has been registered correctly, non-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); +typedef void (*fluid_int_update_t)(void *data, const char *name, int value); +int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints); +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t fun, void *data); + +int fluid_settings_split_csv(const char *str, int *buf, int buf_len); #endif /* _FLUID_SETTINGS_H */ diff --git a/libs/fluidsynth/src/fluid_sffile.c b/libs/fluidsynth/src/fluid_sffile.c new file mode 100644 index 0000000000..b3e64cc3f4 --- /dev/null +++ b/libs/fluidsynth/src/fluid_sffile.c @@ -0,0 +1,2566 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_sffile.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" + +#if LIBSNDFILE_SUPPORT +#include +#endif + +/*=================================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 +*/ + +/* 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 */ + SM24_ID +}; + +static const char idlist[] = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" + "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24" + }; + + +/* 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 */ + + +static const unsigned short invalid_inst_gen[] = +{ + Gen_Unused1, + Gen_Unused2, + Gen_Unused3, + Gen_Unused4, + Gen_Reserved1, + Gen_Reserved2, + Gen_Reserved3, + 0 +}; + +static const unsigned short invalid_preset_gen[] = +{ + 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 +}; + + +#define CHNKIDSTR(id) &idlist[(id - 1) * 4] + +/* sfont file chunk sizes */ +#define SF_PHDR_SIZE (38) +#define SF_BAG_SIZE (4) +#define SF_MOD_SIZE (10) +#define SF_GEN_SIZE (4) +#define SF_IHDR_SIZE (22) +#define SF_SHDR_SIZE (46) + + +#define READCHUNK(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 8, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + ((SFChunk *)(var))->size = FLUID_LE32TOH(((SFChunk *)(var))->size); \ + } while (0) + +#define READD(sf, var) \ + do \ + { \ + uint32_t _temp; \ + if (sf->fcbs->fread(&_temp, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE32TOH(_temp); \ + } while (0) + +#define READW(sf, var) \ + do \ + { \ + uint16_t _temp; \ + if (sf->fcbs->fread(&_temp, 2, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE16TOH(_temp); \ + } while (0) + +#define READID(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define READSTR(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 20, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + (*var)[20] = '\0'; \ + } while (0) + +#define READB(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(&var, 1, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIP(sf, size) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, size, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIPW(sf) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, 2, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +/* removes and advances a fluid_list_t pointer */ +#define SLADVREM(list, item) \ + do \ + { \ + fluid_list_t *_temp = item; \ + item = fluid_list_next(item); \ + list = fluid_list_remove_link(list, _temp); \ + delete1_fluid_list(_temp); \ + } while (0) + + +static int load_header(SFData *sf); +static int load_body(SFData *sf); +static int process_info(SFData *sf, int size); +static int process_sdta(SFData *sf, unsigned int size); +static int process_pdta(SFData *sf, int size); +static int load_phdr(SFData *sf, int size); +static int load_pbag(SFData *sf, int size); +static int load_pmod(SFData *sf, int size); +static int load_pgen(SFData *sf, int size); +static int load_ihdr(SFData *sf, int size); +static int load_ibag(SFData *sf, int size); +static int load_imod(SFData *sf, int size); +static int load_igen(SFData *sf, int size); +static int load_shdr(SFData *sf, unsigned int size); +static int fixup_pgen(SFData *sf); +static int fixup_igen(SFData *sf); + +static int chunkid(unsigned int id); +static int read_listchunk(SFData *sf, SFChunk *chunk); +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size); +static int preset_compare_func(void *a, void *b); +static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); +static int valid_inst_genid(unsigned short genid); +static int valid_preset_genid(unsigned short genid); + + +static void delete_preset(SFPreset *preset); +static void delete_inst(SFInst *inst); +static void delete_zone(SFZone *zone); + +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); + +/* + * Open a SoundFont file and parse it's contents into a SFData structure. + * + * @param fname filename + * @param fcbs file callback structure + * @return the partially parsed SoundFont as SFData structure or NULL on error + */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) +{ + SFData *sf; + int fsize = 0; + + if(!(sf = FLUID_NEW(SFData))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sf, 0, sizeof(SFData)); + + sf->fcbs = fcbs; + + if((sf->sffd = fcbs->fopen(fname)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open file '%s'", fname); + goto error_exit; + } + + sf->fname = FLUID_STRDUP(fname); + + if(sf->fname == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* get size of file by seeking to end */ + if(fcbs->fseek(sf->sffd, 0L, SEEK_END) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Seek to end of file failed"); + goto error_exit; + } + + if((fsize = fcbs->ftell(sf->sffd)) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Get end of file position failed"); + goto error_exit; + } + + sf->filesize = fsize; + + if(fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Rewind to start of file failed"); + goto error_exit; + } + + if(!load_header(sf)) + { + goto error_exit; + } + + return sf; + +error_exit: + fluid_sffile_close(sf); + return NULL; +} + +/* + * Parse all preset information from the soundfont + * + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_sffile_parse_presets(SFData *sf) +{ + if(!load_body(sf)) + { + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Load sample data from the soundfont file + * + * This function will always return the sample data in WAV format. If the sample_type specifies an + * Ogg Vorbis compressed sample, it will be decompressed automatically before returning. + * + * @param sf SFData instance + * @param sample_start index of first sample point in Soundfont sample chunk + * @param sample_end index of last sample point in Soundfont sample chunk + * @param sample_type type of the sample in Soundfont + * @param data pointer to sample data pointer, will point to loaded sample data on success + * @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded + * 24-bit sample data on success or NULL if no 24-bit data is present in file + * + * @return The number of sample words in returned buffers or -1 on failure + */ +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24) +{ + int num_samples; + + if(sample_type & FLUID_SAMPLETYPE_OGG_VORBIS) + { + num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data); + } + else + { + num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24); + } + + return num_samples; +} + +/* + * Close a SoundFont file and free the SFData structure. + * + * @param sf pointer to SFData structure + * @param fcbs file callback structure + */ +void fluid_sffile_close(SFData *sf) +{ + fluid_list_t *entry; + SFPreset *preset; + SFInst *inst; + + if(sf->sffd) + { + sf->fcbs->fclose(sf->sffd); + } + + FLUID_FREE(sf->fname); + + entry = sf->info; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->info); + + entry = sf->preset; + + while(entry) + { + preset = (SFPreset *)fluid_list_get(entry); + delete_preset(preset); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->preset); + + entry = sf->inst; + + while(entry) + { + inst = (SFInst *)fluid_list_get(entry); + delete_inst(inst); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->inst); + + entry = sf->sample; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->sample); + + FLUID_FREE(sf); +} + + +/* + * Private functions + */ + +/* sound font file load functions */ +static int chunkid(unsigned int id) +{ + unsigned int i; + const unsigned int *p; + + p = (const unsigned int *)&idlist; + + for(i = 0; i < sizeof(idlist) / sizeof(int); i++, p += 1) + { + if(*p == id) + { + return (i + 1); + } + } + + return UNKN_ID; +} + +static int load_header(SFData *sf) +{ + SFChunk chunk; + + READCHUNK(sf, &chunk); /* load RIFF chunk */ + + if(chunkid(chunk.id) != RIFF_ID) + { + /* error if not RIFF */ + FLUID_LOG(FLUID_ERR, "Not a RIFF file"); + return FALSE; + } + + READID(sf, &chunk.id); /* load file ID */ + + if(chunkid(chunk.id) != SFBK_ID) + { + /* error if not SFBK_ID */ + FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); + return FALSE; + } + + if(chunk.size != sf->filesize - 8) + { + FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch"); + return FALSE; + } + + /* Process INFO block */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunkid(chunk.id) != INFO_ID) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); + return FALSE; + } + + if(!process_info(sf, chunk.size)) + { + return FALSE; + } + + /* Process sample chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunkid(chunk.id) != SDTA_ID) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); + return FALSE; + } + + if(!process_sdta(sf, chunk.size)) + { + return FALSE; + } + + /* process HYDRA chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunkid(chunk.id) != PDTA_ID) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); + return FALSE; + } + + sf->hydrapos = sf->fcbs->ftell(sf->sffd); + sf->hydrasize = chunk.size; + + return TRUE; +} + +static int load_body(SFData *sf) +{ + if(sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position"); + return FALSE; + } + + if(!process_pdta(sf, sf->hydrasize)) + { + return FALSE; + } + + if(!fixup_pgen(sf)) + { + return FALSE; + } + + if(!fixup_igen(sf)) + { + return FALSE; + } + + /* sort preset list by bank, preset # */ + sf->preset = fluid_list_sort(sf->preset, (fluid_compare_func_t)preset_compare_func); + + return TRUE; +} + +static int read_listchunk(SFData *sf, SFChunk *chunk) +{ + READCHUNK(sf, chunk); /* read list chunk */ + + if(chunkid(chunk->id) != LIST_ID) /* error if ! list chunk */ + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); + return FALSE; + } + + READID(sf, &chunk->id); /* read id string */ + chunk->size -= 4; + return TRUE; +} + +static int process_info(SFData *sf, int size) +{ + SFChunk chunk; + unsigned char id; + char *item; + unsigned short ver; + + while(size > 0) + { + READCHUNK(sf, &chunk); + size -= 8; + + id = chunkid(chunk.id); + + if(id == IFIL_ID) + { + /* sound font version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "Sound font version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->version.major = ver; + READW(sf, ver); + 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 FALSE; + } + + if(sf->version.major == 3) + { +#if !LIBSNDFILE_SUPPORT + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d but fluidsynth was compiled without" + " support for (v3.x)", + sf->version.major, sf->version.minor); + return FALSE; +#endif + } + else if(sf->version.major > 2) + { + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d which is newer than" + " what this version of fluidsynth was designed for (v2.0x)", + sf->version.major, sf->version.minor); + return FALSE; + } + } + else if(id == IVER_ID) + { + /* ROM version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "ROM version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->romver.major = ver; + READW(sf, ver); + sf->romver.minor = ver; + } + else if(id != UNKN_ID) + { + if((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) + { + FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes", + &chunk.id, chunk.size); + return FALSE; + } + + /* alloc for chunk id and da chunk */ + if(!(item = FLUID_MALLOC(chunk.size + 1))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ + sf->info = fluid_list_append(sf->info, item); + + *(unsigned char *)item = id; + + if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED) + { + return FALSE; + } + + /* force terminate info item (don't forget uint8 info ID) */ + *(item + chunk.size) = '\0'; + } + else + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in INFO chunk"); + return FALSE; + } + + size -= chunk.size; + } + + if(size < 0) + { + FLUID_LOG(FLUID_ERR, "INFO chunk size mismatch"); + return FALSE; + } + + return TRUE; +} + +static int process_sdta(SFData *sf, unsigned int size) +{ + SFChunk chunk; + + if(size == 0) + { + return TRUE; /* no sample data? */ + } + + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunkid(chunk.id) != SMPL_ID) + { + FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); + return FALSE; + } + + /* SDTA chunk may also contain sm24 chunk for 24 bit samples + * (not yet supported), only an error if SMPL chunk size is + * greater than SDTA. */ + if(chunk.size > size) + { + FLUID_LOG(FLUID_ERR, "SDTA chunk size mismatch"); + return FALSE; + } + + /* sample data follows */ + sf->samplepos = sf->fcbs->ftell(sf->sffd); + + /* used to check validity of sample headers */ + sf->samplesize = chunk.size; + + FSKIP(sf, chunk.size); + size -= chunk.size; + + if(sf->version.major >= 2 && sf->version.minor >= 4) + { + /* any chance to find another chunk here? */ + if(size > 8) + { + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunkid(chunk.id) == SM24_ID) + { + int sm24size, sdtahalfsize; + + FLUID_LOG(FLUID_DBG, "Found SM24 chunk"); + + if(chunk.size > size) + { + FLUID_LOG(FLUID_WARN, "SM24 exeeds SDTA chunk, ignoring SM24"); + goto ret; // no error + } + + sdtahalfsize = sf->samplesize / 2; + /* + 1 byte in the case that half the size of smpl chunk is an odd value */ + sdtahalfsize += sdtahalfsize % 2; + sm24size = chunk.size; + + if(sdtahalfsize != sm24size) + { + FLUID_LOG(FLUID_WARN, "SM24 not equal to half the size of SMPL chunk (0x%X != " + "0x%X), ignoring SM24", + sm24size, sdtahalfsize); + goto ret; // no error + } + + /* sample data24 follows */ + sf->sample24pos = sf->fcbs->ftell(sf->sffd); + sf->sample24size = sm24size; + } + } + } + +ret: + FSKIP(sf, size); + + return TRUE; +} + +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) +{ + unsigned int id; + const char *expstr; + + expstr = CHNKIDSTR(expid); /* in case we need it */ + + READCHUNK(sf, chunk); + *size -= 8; + + if((id = chunkid(chunk->id)) != expid) + { + FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr); + return FALSE; + } + + if(chunk->size % reclen) /* valid chunk size? */ + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", expstr, reclen); + return FALSE; + } + + if((*size -= chunk->size) < 0) + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", expstr); + return FALSE; + } + + return TRUE; +} + +static int process_pdta(SFData *sf, int size) +{ + SFChunk chunk; + + if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_phdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pbag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pmod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pgen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ihdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ibag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_imod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_igen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_shdr(sf, chunk.size)) + { + return FALSE; + } + + return TRUE; +} + +/* preset header loader */ +static int load_phdr(SFData *sf, int size) +{ + int i, i2; + SFPreset *preset, *prev_preset = NULL; + unsigned short pbag_idx, prev_pbag_idx = 0; + + if(size % SF_PHDR_SIZE || size == 0) + { + FLUID_LOG(FLUID_ERR, "Preset header chunk size is invalid"); + return FALSE; + } + + i = size / SF_PHDR_SIZE - 1; + + if(i == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no presets"); + FSKIP(sf, SF_PHDR_SIZE); + return TRUE; + } + + for(; i > 0; i--) + { + /* load all preset headers */ + if((preset = FLUID_NEW(SFPreset)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + sf->preset = fluid_list_append(sf->preset, preset); + preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */ + READSTR(sf, &preset->name); /* possible read failure ^ */ + READW(sf, preset->prenum); + READW(sf, preset->bank); + READW(sf, pbag_idx); + READD(sf, preset->libr); + READD(sf, preset->genre); + READD(sf, preset->morph); + + if(prev_preset) + { + /* not first preset? */ + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + } + else if(pbag_idx > 0) /* 1st preset, warn if ofs >0 */ + { + FLUID_LOG(FLUID_WARN, "%d preset zones not referenced, discarding", pbag_idx); + } + + prev_preset = preset; /* update preset ptr */ + prev_pbag_idx = pbag_idx; + } + + FSKIP(sf, 24); + READW(sf, pbag_idx); /* Read terminal generator index */ + FSKIP(sf, 12); + + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + + return TRUE; +} + +/* preset bag loader */ +static int load_pbag(SFData *sf, int size) +{ + fluid_list_t *p, *p2; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx; + unsigned short pgenndx = 0, pmodndx = 0; + unsigned short i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size is invalid"); + return FALSE; + } + + p = sf->preset; + + while(p) + { + /* traverse through presets */ + p2 = ((SFPreset *)(p->data))->zone; + + while(p2) + { + /* traverse preset's zones */ + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p2->data = z; + z->gen = NULL; /* Init gen and mod before possible failure, */ + z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ + READW(sf, genndx); /* possible read failure ^ */ + READW(sf, modndx); + z->instsamp = NULL; + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + 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 -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + 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 TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + 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 TRUE; +} + +/* preset modulator loader */ +static int load_pmod(SFData *sf, int size) +{ + 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 -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p3->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + 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 TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* ------------------------------------------------------------------- + * 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(SFData *sf, int size) +{ + 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 -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == Gen_KeyRange) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == Gen_VelRange) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == Gen_Instrument) + { + /* inst is last gen */ + level = 3; + READW(sf, genval.uword); + ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1); + break; /* break out of generator loop */ + } + else + { + level = 2; + + if(valid_preset_genid(genid)) + { + /* generator valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, z->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + 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(sf); + } + + 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); + *hz = fluid_list_remove(*hz, p2->data); + delete_zone((SFZone *)fluid_list_get(p2)); + } + } + + while(p3) + { + /* Kill any zones following an instrument */ + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + 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 TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* instrument header loader */ +static int load_ihdr(SFData *sf, int size) +{ + int i, i2; + SFInst *p, *pr = NULL; /* ptr to current & previous instrument */ + unsigned short zndx, pzndx = 0; + + if(size % SF_IHDR_SIZE || size == 0) /* chunk size is valid? */ + { + FLUID_LOG(FLUID_ERR, "Instrument header has invalid size"); + return FALSE; + } + + size = size / SF_IHDR_SIZE - 1; + + if(size == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no instruments"); + FSKIP(sf, SF_IHDR_SIZE); + return TRUE; + } + + for(i = 0; i < size; i++) + { + /* load all instrument headers */ + if((p = FLUID_NEW(SFInst)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + sf->inst = fluid_list_append(sf->inst, p); + p->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ + p->idx = i; + READSTR(sf, &p->name); /* Possible read failure ^ */ + READW(sf, zndx); + + if(pr) + { + /* not first instrument? */ + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + 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(sf, 20); + READW(sf, zndx); + + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + i2 = zndx - pzndx; + + while(i2--) + { + pr->zone = fluid_list_prepend(pr->zone, NULL); + } + + return TRUE; +} + +/* instrument bag loader */ +static int load_ibag(SFData *sf, int size) +{ + fluid_list_t *p, *p2; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; + int i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size is invalid"); + return FALSE; + } + + p = sf->inst; + + while(p) + { + /* traverse through inst */ + p2 = ((SFInst *)(p->data))->zone; + + while(p2) + { + /* load this inst's zones */ + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p2->data = z; + z->gen = NULL; /* In case of failure, */ + z->mod = NULL; /* fluid_sffile_close can clean up */ + READW(sf, genndx); /* READW = possible read failure */ + READW(sf, modndx); + z->instsamp = NULL; + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + 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 -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + 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 TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + 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 TRUE; +} + +/* instrument modulator loader */ +static int load_imod(SFData *sf, int size) +{ + 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 -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p3->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + 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 TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* load instrument generators (see load_pgen for loading rules) */ +static int load_igen(SFData *sf, int size) +{ + 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 -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == Gen_KeyRange) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == Gen_VelRange) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == Gen_SampleId) + { + /* sample is last gen */ + level = 3; + READW(sf, genval.uword); + ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1); + break; /* break out of generator loop */ + } + else + { + level = 2; + + if(valid_inst_genid(genid)) + { + /* gen valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, z->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + 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(sf); + } + + 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); + *hz = fluid_list_remove(*hz, p2->data); + delete_zone((SFZone *)fluid_list_get(p2)); + } + } + + while(p3) + { + /* Kill any zones following a sample */ + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + 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 TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* sample header loader */ +static int load_shdr(SFData *sf, unsigned int size) +{ + unsigned int i; + SFSample *p; + + if(size % SF_SHDR_SIZE || size == 0) /* size is multiple of SHDR size? */ + { + FLUID_LOG(FLUID_ERR, "Sample header has invalid size"); + return FALSE; + } + + size = size / SF_SHDR_SIZE - 1; + + if(size == 0) + { + /* at least one sample + term record? */ + FLUID_LOG(FLUID_WARN, "File contains no samples"); + FSKIP(sf, SF_SHDR_SIZE); + return TRUE; + } + + /* load all sample headers */ + for(i = 0; i < size; i++) + { + if((p = FLUID_NEW(SFSample)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + sf->sample = fluid_list_append(sf->sample, p); + READSTR(sf, &p->name); + READD(sf, p->start); + READD(sf, p->end); + READD(sf, p->loopstart); + READD(sf, p->loopend); + READD(sf, p->samplerate); + READB(sf, p->origpitch); + READB(sf, p->pitchadj); + FSKIPW(sf); /* skip sample link */ + READW(sf, p->sampletype); + p->samfile = 0; + } + + FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */ + + return TRUE; +} + +/* "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 = FLUID_POINTER_TO_INT(z->instsamp))) + { + /* load instrument # */ + p3 = fluid_list_nth(sf->inst, i - 1); + + if(!p3) + { + FLUID_LOG(FLUID_ERR, "Preset %03d %03d: Invalid instrument reference", + ((SFPreset *)(p->data))->bank, ((SFPreset *)(p->data))->prenum); + return FALSE; + } + + z->instsamp = p3; + } + else + { + z->instsamp = NULL; + } + + p2 = fluid_list_next(p2); + } + + p = fluid_list_next(p); + } + + return TRUE; +} + +/* "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 = FLUID_POINTER_TO_INT(z->instsamp))) + { + /* load sample # */ + p3 = fluid_list_nth(sf->sample, i - 1); + + if(!p3) + { + FLUID_LOG(FLUID_ERR, "Instrument '%s': Invalid sample reference", + ((SFInst *)(p->data))->name); + return FALSE; + } + + z->instsamp = p3; + } + + p2 = fluid_list_next(p2); + } + + p = fluid_list_next(p); + } + + return TRUE; +} + +static void delete_preset(SFPreset *preset) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!preset) + { + return; + } + + entry = preset->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(preset->zone); + + FLUID_FREE(preset); +} + +static void delete_inst(SFInst *inst) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!inst) + { + return; + } + + entry = inst->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(inst->zone); + + FLUID_FREE(inst); +} + + +/* Free all elements of a zone (Preset or Instrument) */ +static void delete_zone(SFZone *zone) +{ + fluid_list_t *entry; + + if(!zone) + { + return; + } + + entry = zone->gen; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->gen); + + entry = zone->mod; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->mod); + + FLUID_FREE(zone); +} + +/* preset sort function, first by bank, then by preset # */ +static int 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); +} + +/* Find a generator by its id in the passed in list. + * + * @return pointer to SFGen if found, otherwise NULL + */ +static fluid_list_t *find_gen_by_id(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 */ +static int valid_inst_genid(unsigned short genid) +{ + int i = 0; + + if(genid > Gen_MaxValid) + { + return FALSE; + } + + while(invalid_inst_gen[i] && invalid_inst_gen[i] != genid) + { + i++; + } + + return (invalid_inst_gen[i] == 0); +} + +/* check validity of preset generator */ +static int valid_preset_genid(unsigned short genid) +{ + int i = 0; + + if(!valid_inst_genid(genid)) + { + return FALSE; + } + + while(invalid_preset_gen[i] && invalid_preset_gen[i] != genid) + { + i++; + } + + return (invalid_preset_gen[i] == 0); +} + + +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24) +{ + short *loaded_data = NULL; + char *loaded_data24 = NULL; + + int num_samples = (end + 1) - start; + fluid_return_val_if_fail(num_samples > 0, -1); + + if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk"); + goto error_exit; + } + + /* Load 16-bit sample data */ + if(sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to sample position"); + goto error_exit; + } + + loaded_data = FLUID_ARRAY(short, num_samples); + + if(loaded_data == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read sample data"); + goto error_exit; + } + + /* If this machine is big endian, byte swap the 16 bit samples */ + if(FLUID_IS_BIG_ENDIAN) + { + int i; + + for(i = 0; i < num_samples; i++) + { + loaded_data[i] = FLUID_LE16TOH(loaded_data[i]); + } + } + + *data = loaded_data; + + /* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading + * the 24-bit sample data will be logged as errors but won't prevent the sample reading to + * fail, as sound output is still possible with the 16-bit sample data. */ + if(sf->sample24pos) + { + if((start > sf->sample24size) || (end > sf->sample24size)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk"); + goto error24_exit; + } + + if(sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file"); + goto error24_exit; + } + + loaded_data24 = FLUID_ARRAY(char, num_samples); + + if(loaded_data24 == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data"); + goto error24_exit; + } + + if(sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data"); + goto error24_exit; + } + } + + *data24 = loaded_data24; + + return num_samples; + +error24_exit: + FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer"); + FLUID_FREE(loaded_data24); + *data24 = NULL; + return num_samples; + +error_exit: + FLUID_FREE(loaded_data); + FLUID_FREE(loaded_data24); + return -1; +} + + +/* Ogg Vorbis loading and decompression */ +#if LIBSNDFILE_SUPPORT + +/* Virtual file access rountines to allow loading individually compressed + * samples from the Soundfont sample data chunk using the file callbacks + * passing in during opening of the file */ +typedef struct _sfvio_data_t +{ + SFData *sffile; + sf_count_t start; /* start byte offset of compressed data */ + sf_count_t end; /* end byte offset of compressed data */ + sf_count_t offset; /* current virtual file offset from start byte offset */ + +} sfvio_data_t; + +static sf_count_t sfvio_get_filelen(void *user_data) +{ + sfvio_data_t *data = user_data; + + return (data->end + 1) - data->start; +} + +static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t new_offset; + + switch(whence) + { + case SEEK_SET: + new_offset = offset; + break; + + case SEEK_CUR: + new_offset = data->offset + offset; + break; + + case SEEK_END: + new_offset = sfvio_get_filelen(user_data) + offset; + break; + + default: + goto fail; /* proper error handling not possible?? */ + } + + if(sf->fcbs->fseek(sf->sffd, sf->samplepos + data->start + new_offset, SEEK_SET) != FLUID_FAILED) + { + data->offset = new_offset; + } + +fail: + return data->offset; +} + +static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t remain; + + remain = sfvio_get_filelen(user_data) - data->offset; + + if(count > remain) + { + count = remain; + } + + if(count == 0) + { + return count; + } + + if(sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); + return 0; + } + + data->offset += count; + + return count; +} + +static sf_count_t sfvio_tell(void *user_data) +{ + sfvio_data_t *data = user_data; + + return data->offset; +} + +/** + * Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples + * in the decompressed WAV. Only 16-bit mono samples are supported. + * + * Note that this function takes byte indices for start and end source data. The sample headers in SF3 + * files use byte indices, so those pointers can be passed directly to this function. + * + * This function uses a virtual file structure in order to read the Ogg Vorbis + * data from arbitrary locations in the source file. + */ +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + SNDFILE *sndfile; + SF_INFO sfinfo; + SF_VIRTUAL_IO sfvio = + { + sfvio_get_filelen, + sfvio_seek, + sfvio_read, + NULL, + sfvio_tell + }; + sfvio_data_t sfdata; + short *wav_data = NULL; + + if((start_byte > sf->samplesize) || (end_byte > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk"); + return -1; + } + + // Initialize file position indicator and SF_INFO structure + sfdata.sffile = sf; + sfdata.start = start_byte; + sfdata.end = end_byte; + sfdata.offset = 0; + + memset(&sfinfo, 0, sizeof(sfinfo)); + + /* Seek to beginning of Ogg Vorbis data in Soundfont */ + if(sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to compressd sample position"); + return -1; + } + + // Open sample as a virtual file + sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata); + + if(!sndfile) + { + FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); + return -1; + } + + // Empty sample + if(!sfinfo.frames || !sfinfo.channels) + { + FLUID_LOG(FLUID_DBG, "Empty decompressed sample"); + *data = NULL; + sf_close(sndfile); + return 0; + } + + /* FIXME: ensure that the decompressed WAV data is 16-bit mono? */ + + wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels); + + if(!wav_data) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* Automatically decompresses the Ogg Vorbis data to 16-bit WAV */ + if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames) + { + FLUID_LOG(FLUID_DBG, "Decompression failed!"); + FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); + goto error_exit; + } + + sf_close(sndfile); + + *data = wav_data; + + return sfinfo.frames; + +error_exit: + FLUID_FREE(wav_data); + sf_close(sndfile); + return -1; +} +#else +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + return -1; +} +#endif diff --git a/libs/fluidsynth/src/fluid_sffile.h b/libs/fluidsynth/src/fluid_sffile.h new file mode 100644 index 0000000000..0c315c73b6 --- /dev/null +++ b/libs/fluidsynth/src/fluid_sffile.h @@ -0,0 +1,230 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SFFILE_H +#define _FLUID_SFFILE_H + + +#include "fluid_gen.h" +#include "fluid_list.h" +#include "fluid_mod.h" +#include "fluidsynth.h" +#include "fluidsynth_priv.h" + + +/* Sound Font structure defines */ + +/* Forward declarations */ +typedef union _SFGenAmount SFGenAmount; +typedef struct _SFVersion SFVersion; +typedef struct _SFMod SFMod; +typedef struct _SFGen SFGen; +typedef struct _SFZone SFZone; +typedef struct _SFSample SFSample; +typedef struct _SFInst SFInst; +typedef struct _SFPreset SFPreset; +typedef struct _SFData SFData; +typedef struct _SFChunk SFChunk; +typedef struct _SFPhdr SFPhdr; +typedef struct _SFBag SFBag; +typedef struct _SFIhdr SFIhdr; +typedef struct _SFShdr SFShdr; + + +struct _SFVersion +{ + /* version structure */ + unsigned short major; + unsigned short minor; +}; + +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 */ +}; + +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; +}; + +struct _SFGen +{ + /* Generator structure */ + unsigned short id; /* generator ID */ + SFGenAmount amount; /* generator value */ +}; + +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 */ +}; + +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 */ + fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */ +}; + +struct _SFInst +{ + /* Instrument structure */ + char name[21]; /* Name of instrument */ + int idx; /* Index of this instrument in the Soundfont */ + fluid_list_t *zone; /* list of instrument zones */ +}; + +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 */ +}; + +/* NOTE: sffd is also used to determine if sound font is new (NULL) */ +struct _SFData +{ + /* Sound font data structure */ + SFVersion version; /* sound font version */ + SFVersion romver; /* ROM version */ + + unsigned int filesize; + + unsigned int samplepos; /* position within sffd of the sample chunk */ + unsigned int samplesize; /* length within sffd of the sample chunk */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit + sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + + unsigned int hydrapos; + unsigned int hydrasize; + + char *fname; /* file name */ + FILE *sffd; /* loaded sfont file descriptor */ + const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */ + + 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 */ +}; + +/* functions */ + + +/*-----------------------------------sffile.h----------------------------*/ +/* + File structures and routines (used to be in sffile.h) +*/ + +/* sfont file data structures */ +struct _SFChunk +{ + /* RIFF file chunk structure */ + unsigned int id; /* chunk id */ + unsigned int size; /* size of the following chunk */ +}; + +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 */ +}; + +struct _SFBag +{ + unsigned short genndx; /* index into generator list */ + unsigned short modndx; /* index into modulator list */ +}; + +struct _SFIhdr +{ + char name[20]; /* Name of instrument */ + unsigned short ibagndx; /* Instrument bag index */ +}; + +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 */ +}; + +/* Public functions */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs); +void fluid_sffile_close(SFData *sf); +int fluid_sffile_parse_presets(SFData *sf); +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24); + +#endif /* _FLUID_SFFILE_H */ diff --git a/libs/fluidsynth/src/fluid_sfont.c b/libs/fluidsynth/src/fluid_sfont.c new file mode 100644 index 0000000000..405bec6c64 --- /dev/null +++ b/libs/fluidsynth/src/fluid_sfont.c @@ -0,0 +1,784 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sfont.h" +#include "fluid_sys.h" + + +void *default_fopen(const char *path) +{ + return FLUID_FOPEN(path, "rb"); +} + +int default_fclose(void *handle) +{ + return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; +} + +long default_ftell(void *handle) +{ + return FLUID_FTELL((FILE *)handle); +} + +int safe_fread(void *buf, int count, void *fd) +{ + if(FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1) + { + if(feof((FILE *)fd)) + { + FLUID_LOG(FLUID_ERR, "EOF while attemping to read %d bytes", count); + } + else + { + FLUID_LOG(FLUID_ERR, "File read failed"); + } + + return FLUID_FAILED; + } + + return FLUID_OK; +} + +int safe_fseek(void *fd, long ofs, int whence) +{ + if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) + { + FLUID_LOG(FLUID_ERR, "File seek failed with offset = %ld and whence = %d", ofs, whence); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/** + * Creates a new SoundFont loader. + * + * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t). + * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t). + * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader(). + * + * @return the SoundFont loader instance on success, NULL otherwise. + */ +fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free) +{ + fluid_sfloader_t *loader; + + fluid_return_val_if_fail(load != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + loader = FLUID_NEW(fluid_sfloader_t); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(loader, 0, sizeof(*loader)); + + loader->load = load; + loader->free = free; + fluid_sfloader_set_callbacks(loader, + default_fopen, + safe_fread, + safe_fseek, + default_ftell, + default_fclose); + + return loader; +} + +/** + * Frees a SoundFont loader created with new_fluid_sfloader(). + * + * @param loader The SoundFont loader instance to free. + */ +void delete_fluid_sfloader(fluid_sfloader_t *loader) +{ + fluid_return_if_fail(loader != NULL); + + FLUID_FREE(loader); +} + +/** + * Specify private data to be used by #fluid_sfloader_load_t. + * + * @param loader The SoundFont loader instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data) +{ + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + + loader->data = data; + return FLUID_OK; +} + +/** + * Obtain private data previously set with fluid_sfloader_set_data(). + * + * @param loader The SoundFont loader instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfloader_get_data(fluid_sfloader_t *loader) +{ + fluid_return_val_if_fail(loader != NULL, NULL); + + return loader->data; +} + +/** + * Set custom callbacks to be used upon soundfont loading. + * + * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. + * + * @param loader The SoundFont loader instance. + * @param open A function implementing #fluid_sfloader_callback_open_t. + * @param read A function implementing #fluid_sfloader_callback_read_t. + * @param seek A function implementing #fluid_sfloader_callback_seek_t. + * @param tell A function implementing #fluid_sfloader_callback_tell_t. + * @param close A function implementing #fluid_sfloader_callback_close_t. + * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close) +{ + fluid_file_callbacks_t *cb; + + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + fluid_return_val_if_fail(open != NULL, FLUID_FAILED); + fluid_return_val_if_fail(read != NULL, FLUID_FAILED); + fluid_return_val_if_fail(seek != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tell != NULL, FLUID_FAILED); + fluid_return_val_if_fail(close != NULL, FLUID_FAILED); + + cb = &loader->file_callbacks; + + cb->fopen = open; + cb->fread = read; + cb->fseek = seek; + cb->ftell = tell; + cb->fclose = close; + + return FLUID_OK; +} + +/** + * Creates a new virtual SoundFont instance structure. + * @param get_name A function implementing #fluid_sfont_get_name_t. + * @param get_preset A function implementing #fluid_sfont_get_preset_t. + * @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed. + * @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed. + * @param free A function implementing #fluid_sfont_free_t. + * @return The soundfont instance on success or NULL otherwise. + */ +fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free) +{ + fluid_sfont_t *sfont; + + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_preset != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + sfont = FLUID_NEW(fluid_sfont_t); + + if(sfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sfont, 0, sizeof(*sfont)); + + sfont->get_name = get_name; + sfont->get_preset = get_preset; + sfont->iteration_start = iter_start; + sfont->iteration_next = iter_next; + sfont->free = free; + + return sfont; +} + +/** + * Set private data to use with a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data) +{ + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + + sfont->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfont_get_data(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + + return sfont->data; +} + +/** + * Retrieve the unique ID of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The SoundFont ID. + */ +int fluid_sfont_get_id(fluid_sfont_t *sfont) +{ + return sfont->id; +} + +/** + * Retrieve the name of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The name of the SoundFont. + */ +const char *fluid_sfont_get_name(fluid_sfont_t *sfont) +{ + return sfont->get_name(sfont); +} + +/** + * Retrieve the preset assigned the a SoundFont instance + * for the given bank and preset number. + * @param sfont The SoundFont instance. + * @param bank bank number of the preset + * @param prenum program number of the preset + * @return The preset instance or NULL if none found. + */ +fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + return sfont->get_preset(sfont, bank, prenum); +} + + +/** + * Starts / re-starts virtual preset iteration in a SoundFont. + * @param sfont Virtual SoundFont instance + */ +void fluid_sfont_iteration_start(fluid_sfont_t *sfont) +{ + fluid_return_if_fail(sfont != NULL); + fluid_return_if_fail(sfont->iteration_start != NULL); + + sfont->iteration_start(sfont); +} + +/** + * Virtual SoundFont preset iteration function. + * + * Returns preset information to the caller and advances the + * internal iteration state to the next preset for subsequent calls. + * @param sfont The SoundFont instance. + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + */ +fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL); + + return sfont->iteration_next(sfont); +} + +/** + * Destroys a SoundFont instance created with new_fluid_sfont(). + * + * Implements #fluid_sfont_free_t. + * + * @param sfont The SoundFont instance to destroy. + * @return Always returns 0. + */ +int delete_fluid_sfont(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, 0); + + FLUID_FREE(sfont); + return 0; +} + +/** + * Create a virtual SoundFont preset instance. + * + * @param parent_sfont The SoundFont instance this preset shall belong to + * @param get_name A function implementing #fluid_preset_get_name_t + * @param get_bank A function implementing #fluid_preset_get_banknum_t + * @param get_num A function implementing #fluid_preset_get_num_t + * @param noteon A function implementing #fluid_preset_noteon_t + * @param free A function implementing #fluid_preset_free_t + * @return The preset instance on success, NULL otherwise. + */ +fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free) +{ + fluid_preset_t *preset; + + fluid_return_val_if_fail(parent_sfont != NULL, NULL); + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_bank != NULL, NULL); + fluid_return_val_if_fail(get_num != NULL, NULL); + fluid_return_val_if_fail(noteon != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + preset = FLUID_NEW(fluid_preset_t); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(preset, 0, sizeof(*preset)); + + preset->sfont = parent_sfont; + preset->get_name = get_name; + preset->get_banknum = get_bank; + preset->get_num = get_num; + preset->noteon = noteon; + preset->free = free; + + return preset; +} + +/** + * Set private data to use with a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_preset_set_data(fluid_preset_t *preset, void *data) +{ + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + + preset->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_preset_get_data(fluid_preset_t *preset) +{ + fluid_return_val_if_fail(preset != NULL, NULL); + + return preset->data; +} + +/** + * Retrieves the presets name by executing the \p get_name function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return Pointer to a NULL-terminated string containing the presets name. + */ +const char *fluid_preset_get_name(fluid_preset_t *preset) +{ + return preset->get_name(preset); +} + +/** + * Retrieves the presets bank number by executing the \p get_bank function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The bank number of \p preset. + */ +int fluid_preset_get_banknum(fluid_preset_t *preset) +{ + return preset->get_banknum(preset); +} + +/** + * Retrieves the presets (instrument) number by executing the \p get_num function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The number of \p preset. + */ +int fluid_preset_get_num(fluid_preset_t *preset) +{ + return preset->get_num(preset); +} + +/** + * Retrieves the presets parent SoundFont instance. + * + * @param preset The SoundFont preset instance. + * @return The parent SoundFont of \p preset. + */ +fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset) +{ + return preset->sfont; +} + +/** + * Destroys a SoundFont preset instance created with new_fluid_preset(). + * + * Implements #fluid_preset_free_t. + * + * @param preset The SoundFont preset instance to destroy. + */ +void delete_fluid_preset(fluid_preset_t *preset) +{ + fluid_return_if_fail(preset != NULL); + + FLUID_FREE(preset); +} + +/** + * Create a new sample instance. + * @return The sample on success, NULL otherwise. + */ +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; + } + + FLUID_MEMSET(sample, 0, sizeof(*sample)); + + return sample; +} + +/** + * Destroy a sample instance previously created with new_fluid_sample(). + * @param sample The sample to destroy. + */ +void +delete_fluid_sample(fluid_sample_t *sample) +{ + fluid_return_if_fail(sample != NULL); + + if(sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + FLUID_FREE(sample); +} + +/** + * Returns the size of the fluid_sample_t structure. + * + * Useful in low latency scenarios e.g. to allocate a sample on the stack. + * + * @return Size of fluid_sample_t in bytes + */ +size_t fluid_sample_sizeof() +{ + return sizeof(fluid_sample_t); +} + +/** + * Set the name of a SoundFont sample. + * @param sample SoundFont sample + * @param name Name to assign to sample (20 chars in length + zero terminator) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_sample_set_name(fluid_sample_t *sample, const char *name) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + FLUID_STRNCPY(sample->name, name, sizeof(sample->name)); + return FLUID_OK; +} + +/** + * Assign sample data to a SoundFont sample. + * @param sample SoundFont sample + * @param data Buffer containing 16 bit (mono-)audio sample data + * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples + * @param nbframes Number of samples in \a data + * @param sample_rate Sampling rate of the sample data + * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note If \a copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end and \a nbframes should be >=48 + */ +int +fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data + ) +{ + /* the number of samples before the start and after the end */ +#define SAMPLE_LOOP_MARGIN 8U + + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED); + + /* in case we already have some data */ + if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + sample->data = NULL; + sample->data24 = NULL; + } + + if(copy_data) + { + unsigned int storedNbFrames; + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + + if(storedNbFrames < 48) + { + storedNbFrames = 48; + } + + storedNbFrames += 2 * SAMPLE_LOOP_MARGIN; + + sample->data = FLUID_ARRAY(short, storedNbFrames); + + if(sample->data == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data, 0, storedNbFrames); + FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short)); + + if(data24 != NULL) + { + sample->data24 = FLUID_ARRAY(char, storedNbFrames); + + if(sample->data24 == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data24, 0, storedNbFrames); + FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char)); + } + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1; + } + else + { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->data24 = data24; + sample->start = 0; + sample->end = nbframes - 1; + } + + sample->samplerate = sample_rate; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->auto_free = copy_data; + + return FLUID_OK; + +error_rec: + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + return FLUID_FAILED; + +#undef SAMPLE_LOOP_MARGIN +} + +/** + * Set the loop of a sample. + * + * @param sample SoundFont sample + * @param loop_start Start sample index of the loop. + * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played). + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + + sample->loopstart = loop_start; + sample->loopend = loop_end; + + return FLUID_OK; +} + +/** + * Set the pitch of a sample. + * + * @param sample SoundFont sample + * @param root_key Root MIDI note of sample (0-127) + * @param fine_tune Fine tune in cents + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED); + + sample->origpitch = root_key; + sample->pitchadj = fine_tune; + + return FLUID_OK; +} + + +/** + * Validate parameters of a sample + * + */ +int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) +{ + /* ROM samples are unusable for us by definition */ + if(sample->sampletype & FLUID_SAMPLETYPE_ROM) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name); + return FLUID_FAILED; + } + + /* Ogg vorbis compressed samples in the SF3 format use byte indices for + * sample start and end pointers before decompression. Standard SF2 samples + * use sample word indices for all pointers, so use half the buffer_size + * for validation. */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + if(buffer_size % 2) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name); + return FLUID_FAILED; + } + + buffer_size /= 2; + } + + if((sample->end > buffer_size) || (sample->start >= sample->end)) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Check the sample loop pointers and optionally convert them to something + * usable in case they are broken. Return a boolean indicating if the pointers + * have been modified, so the user can be notified of possible audio glitches. + */ +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size) +{ + int modified = FALSE; + unsigned int max_end = buffer_size / 2; + /* In fluid_sample_t the sample end pointer points to the last sample, not + * to the data word after the last sample. FIXME: why? */ + unsigned int sample_end = sample->end + 1; + + if(sample->loopstart == sample->loopend) + { + /* Some SoundFonts disable loops by setting loopstart = loopend. While + * technically invalid, we decided to accept those samples anyway. Just + * ensure that those two pointers are within the sampledata by setting + * them to 0. Don't report the modification, as this change has no audible + * effect. */ + sample->loopstart = sample->loopend = 0; + return FALSE; + } + else if(sample->loopstart > sample->loopend) + { + unsigned int tmp; + + /* If loop start and end are reversed, try to swap them around and + * continue validation */ + FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix", + sample->name, sample->loopstart, sample->loopend); + tmp = sample->loopstart; + sample->loopstart = sample->loopend; + sample->loopend = tmp; + modified = TRUE; + } + + /* The SoundFont 2.4 spec defines the loopstart index as the first sample + * point of the loop while loopend is the first point AFTER the last sample + * of the loop. However we cannot be sure whether any of loopend or end is + * correct. Hours of thinking through this have concluded that it would be + * best practice to mangle with loops as little as necessary by only making + * sure the pointers are within sample->start to max_end. Incorrect + * soundfont shall preferably fail loudly. */ + if((sample->loopstart < sample->start) || (sample->loopstart > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'", + sample->name, sample->loopstart, sample->start); + sample->loopstart = sample->start; + modified = TRUE; + } + + if((sample->loopend < sample->start) || (sample->loopend > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'", + sample->name, sample->loopend, sample_end); + sample->loopend = sample_end; + modified = TRUE; + } + + if((sample->loopstart > sample_end) || (sample->loopend > sample_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway", + sample->name, sample->loopstart, sample->loopend, sample_end); + } + + return modified; +} diff --git a/libs/fluidsynth/src/fluid_sfont.h b/libs/fluidsynth/src/fluid_sfont.h index 51e941e440..24773353df 100644 --- a/libs/fluidsynth/src/fluid_sfont.h +++ b/libs/fluidsynth/src/fluid_sfont.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -22,6 +22,10 @@ #ifndef _PRIV_FLUID_SFONT_H #define _PRIV_FLUID_SFONT_H +#include "fluidsynth.h" + +int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end); +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end); /* * Utility macros to access soundfonts, presets, and samples @@ -31,24 +35,12 @@ #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 fluid_sfont_delete_internal(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) -#define delete_fluid_preset(_preset) \ +#define fluid_preset_delete_internal(_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) @@ -65,4 +57,135 @@ +/** + * File callback structure to enable custom soundfont loading (e.g. from memory). + */ +struct _fluid_file_callbacks_t +{ + fluid_sfloader_callback_open_t fopen; + fluid_sfloader_callback_read_t fread; + fluid_sfloader_callback_seek_t fseek; + fluid_sfloader_callback_close_t fclose; + fluid_sfloader_callback_tell_t ftell; +}; + +/** + * SoundFont loader structure. + */ +struct _fluid_sfloader_t +{ + void *data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ + + /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ + fluid_file_callbacks_t file_callbacks; + + fluid_sfloader_free_t free; + + fluid_sfloader_load_t load; +}; + +/** + * Virtual SoundFont instance structure. + */ +struct _fluid_sfont_t +{ + void *data; /**< User defined data */ + int id; /**< SoundFont ID */ + int refcount; /**< SoundFont reference count (1 if no presets referencing it) */ + int bankofs; /**< Bank offset */ + + fluid_sfont_free_t free; + + fluid_sfont_get_name_t get_name; + + fluid_sfont_get_preset_t get_preset; + + fluid_sfont_iteration_start_t iteration_start; + + fluid_sfont_iteration_next_t iteration_next; +}; + +/** + * Virtual SoundFont preset. + */ +struct _fluid_preset_t +{ + void *data; /**< User supplied data */ + fluid_sfont_t *sfont; /**< Parent virtual SoundFont */ + + fluid_preset_free_t free; + + fluid_preset_get_name_t get_name; + + fluid_preset_get_banknum_t get_banknum; + + fluid_preset_get_num_t get_num; + + fluid_preset_noteon_t noteon; + + /** + * Virtual SoundFont preset notify method. + * @param preset Virtual SoundFont preset + * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED + * @param chan MIDI channel number + * @return Should return #FLUID_OK + * + * Implement this optional method if the preset needs to be notified about + * preset select and unselect events. + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + */ + int (*notify)(fluid_preset_t *preset, int reason, int chan); +}; + +/** + * Virtual SoundFont sample. + */ +struct _fluid_sample_t +{ + char name[21]; /**< Sample name */ + + /* The following for sample pointers store the original pointers from the Soundfont + * file. They are never changed after loading and are used to re-create the + * actual sample pointers after a sample has been unloaded and loaded again. The + * actual sample pointers get modified during loading for SF3 (compressed) samples + * and individually loaded SF2 samples. */ + unsigned int source_start; + unsigned int source_end; + unsigned int source_loopstart; + unsigned int source_loopend; + + unsigned int start; /**< Start index */ + unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ + unsigned int loopstart; /**< Loop start index */ + unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ + + unsigned int samplerate; /**< Sample rate */ + int origpitch; /**< Original pitch (MIDI note number, 0-127) */ + int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ + int sampletype; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ + int auto_free; /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */ + short *data; /**< Pointer to the sample's 16 bit PCM data */ + char *data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ + + int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ + double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ + + unsigned int refcount; /**< Count of voices using this sample */ + int preset_count; /**< Count of selected presets using this sample (used for dynamic sample loading) */ + + /** + * Implement this function to receive notification when sample is no longer used. + * @param sample Virtual SoundFont sample + * @param reason #FLUID_SAMPLE_DONE only currently + * @return Should return #FLUID_OK + */ + int (*notify)(fluid_sample_t *sample, int reason); + + void *userdata; /**< User defined data */ +}; + + #endif /* _PRIV_FLUID_SFONT_H */ diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c index 8a30e250ba..e8845632f1 100644 --- a/libs/fluidsynth/src/fluid_synth.c +++ b/libs/fluidsynth/src/fluid_synth.c @@ -3,30 +3,27 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ -#include - #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_chan.h" #include "fluid_tuning.h" #include "fluid_settings.h" #include "fluid_sfont.h" -#include "fluid_hash.h" #include "fluid_defsfont.h" #ifdef TRAP_ON_FPE @@ -34,82 +31,113 @@ #include /* seems to not be declared in fenv.h */ -extern int feenableexcept (int excepts); +extern int feenableexcept(int excepts); #endif -static void fluid_synth_init(void); +#define FLUID_API_RETURN(return_value) \ + do { fluid_synth_api_exit(synth); \ + return return_value; } while (0) -static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, - int vel); -static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key); -static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num); -static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name, - int value); -static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name, - fluid_real_t value); -static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, - int len, char *response, - int *response_len, int avail_response, - int *handled, int dryrun); -static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); -static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); -static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); -static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, - int is_cc, int ctrl); -static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan); -static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int channum); -static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan); -static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan); -static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, - fluid_preset_t *preset); -static fluid_preset_t* -fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, - unsigned int banknum, unsigned int prognum); -static fluid_preset_t* -fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, - unsigned int banknum, unsigned int prognum); - -static void fluid_synth_update_presets(fluid_synth_t* synth); -static int fluid_synth_update_sample_rate(fluid_synth_t* synth, - char* name, double value); -static int fluid_synth_update_gain(fluid_synth_t* synth, - char* name, double value); -static void fluid_synth_update_gain_LOCAL(fluid_synth_t* synth); -static int fluid_synth_update_polyphony(fluid_synth_t* synth, - char* name, int value); -static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony); -static void init_dither(void); -static inline int roundi (float x); -static int fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount); -//static void fluid_synth_core_thread_func (void* data); -//static FLUID_INLINE void fluid_synth_process_event_queue_LOCAL -// (fluid_synth_t *synth, fluid_event_queue_t *queue); -static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth); -static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, - fluid_voice_t* new_voice); -static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth, - fluid_sfont_t *sfont); -static int fluid_synth_sfunload_callback(void* data, unsigned int msec); -static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, - int chan, int key); -static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth, - int bank, int prog); -static int fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, - fluid_tuning_t *tuning, - int bank, int prog, int apply); -static void fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, - fluid_tuning_t *old_tuning, - fluid_tuning_t *new_tuning, - int apply, int unref_new); -static void fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, - fluid_channel_t *channel); -static int fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, - fluid_tuning_t *tuning, int apply); -static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, - int param, float value, int absolute); -static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); +#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \ + do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \ + {} \ + else \ + { FLUID_API_RETURN(return_value); } \ + } while (0) +#define FLUID_API_ENTRY_CHAN(fail_value) \ + fluid_return_val_if_fail (synth != NULL, fail_value); \ + fluid_return_val_if_fail (chan >= 0, fail_value); \ + fluid_synth_api_enter(synth); \ + if (chan >= synth->midi_channels) { \ + FLUID_API_RETURN(fail_value); \ + } \ +static void fluid_synth_init(void); +static void fluid_synth_api_enter(fluid_synth_t *synth); +static void fluid_synth_api_exit(fluid_synth_t *synth); + +static int fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, + int vel); +static int fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num); +static int fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_system_reset_LOCAL(fluid_synth_t *synth); +static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, + int is_cc, int ctrl); +static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int channum); +static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_set_preset(fluid_synth_t *synth, int chan, + fluid_preset_t *preset); +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum); +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum); + +static void fluid_synth_update_presets(fluid_synth_t *synth); +static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth); +static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony); +static void init_dither(void); +static FLUID_INLINE int roundi(float x); +static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount); + +static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth); +static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice); +static int fluid_synth_sfunload_callback(void *data, unsigned int msec); +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, + int chan, int key); +static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth, + int bank, int prog); +static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, + fluid_tuning_t *tuning, + int bank, int prog, int apply); +static void fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, + fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, + int apply, int unref_new); +static void fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, + fluid_channel_t *channel); +static int fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply); +static void fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, + int param, float value, int absolute); +static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); + + +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); + + +/* Callback handlers for real-time settings */ +static void fluid_synth_handle_sample_rate(void *data, const char *name, double value); +static void fluid_synth_handle_gain(void *data, const char *name, double value); +static void fluid_synth_handle_polyphony(void *data, const char *name, int value); +static void fluid_synth_handle_device_id(void *data, const char *name, int value); +static void fluid_synth_handle_overflow(void *data, const char *name, double value); +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value); +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value); +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value); + + +static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan); +static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val); +static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val); +static int fluid_synth_set_reverb_full_LOCAL(fluid_synth_t *synth, int set, double roomsize, + double damping, double width, double level); + +static int fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int nr, double level, + double speed, double depth_ms, int type); /*************************************************************** * @@ -117,7 +145,8 @@ static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); */ /* has the synth module been initialized? */ -static int fluid_synth_initialized = 0; +/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ +static fluid_atomic_int_t fluid_synth_initialized = {0}; static void fluid_synth_init(void); static void init_dither(void); @@ -128,26 +157,34 @@ static void init_dither(void); * 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 */ +static fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ +/*not static */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ +static fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ +static fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ +static fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ +static fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ +static fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ +static fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ +static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ +static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ +static fluid_mod_t custom_balance_mod; /* Non-standard modulator */ + + +/* custom_breath2att_modulator is not a default modulator specified in SF +it is intended to replace default_vel2att_mod on demand using +API fluid_set_breath_mode() or command shell setbreathmode. +*/ +static fluid_mod_t custom_breath2att_mod; /* 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 } +static const 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 }, }; @@ -156,76 +193,60 @@ static fluid_revmodel_presets_t revmodel_preset[] = { * INITIALIZATION & UTILITIES */ -static void fluid_synth_register_overflow(fluid_settings_t* settings, - fluid_num_update_t update_func, - void* update_data) -{ - fluid_settings_register_num(settings, "synth.overflow.percussion", - 4000, -10000, 10000, 0, update_func, update_data); - fluid_settings_register_num(settings, "synth.overflow.sustained", - -1000, -10000, 10000, 0, update_func, update_data); - fluid_settings_register_num(settings, "synth.overflow.released", - -2000, -10000, 10000, 0, update_func, update_data); - fluid_settings_register_num(settings, "synth.overflow.age", - 1000, -10000, 10000, 0, update_func, update_data); - fluid_settings_register_num(settings, "synth.overflow.volume", - 500, -10000, 10000, 0, update_func, update_data); -} - -void fluid_synth_settings(fluid_settings_t* settings) -{ - fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.dump", 0, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL); - - fluid_settings_register_str(settings, "synth.default-soundfont", - DEFAULT_SOUNDFONT, 0, NULL, NULL); - - fluid_settings_register_int(settings, "synth.polyphony", - 256, 1, 65535, 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, 128, 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.audio-groups", - 1, 1, 128, 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, 8000.0f, 96000.0f, - 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.device-id", - 0, 0, 126, 0, NULL, NULL); - fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0, NULL, NULL); - - fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0, NULL, NULL); - - fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - fluid_settings_register_int(settings, "synth.parallel-render", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); - - fluid_synth_register_overflow(settings, NULL, NULL); - - fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0, NULL, NULL); - fluid_settings_add_option(settings, "synth.midi-bank-select", "gm"); - fluid_settings_add_option(settings, "synth.midi-bank-select", "gs"); - fluid_settings_add_option(settings, "synth.midi-bank-select", "xg"); - fluid_settings_add_option(settings, "synth.midi-bank-select", "mma"); - +void fluid_synth_settings(fluid_settings_t *settings) +{ + fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0f, 100.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0f, 1.0f, 0); + + fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0); + fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0); + + fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_str(settings, "midi.portname", "", 0); + +#ifdef DEFAULT_SOUNDFONT + fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0); +#endif + + fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0); + fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0); + fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0); + fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0); + fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0); + fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0); + fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0); + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0); + + fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0); + + fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0); + fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0); + + fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gm"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gs"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "xg"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "mma"); + + fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED); } /** @@ -236,9 +257,9 @@ void fluid_synth_settings(fluid_settings_t* settings) */ void fluid_version(int *major, int *minor, int *micro) { - *major = FLUIDSYNTH_VERSION_MAJOR; - *minor = FLUIDSYNTH_VERSION_MINOR; - *micro = FLUIDSYNTH_VERSION_MICRO; + *major = FLUIDSYNTH_VERSION_MAJOR; + *minor = FLUIDSYNTH_VERSION_MINOR; + *micro = FLUIDSYNTH_VERSION_MICRO; } /** @@ -246,25 +267,12 @@ void fluid_version(int *major, int *minor, int *micro) * @return FluidSynth version string, which is internal and should not be * modified or freed. */ -char * -fluid_version_str (void) +const char * +fluid_version_str(void) { - return FLUIDSYNTH_VERSION; + return FLUIDSYNTH_VERSION; } -#define FLUID_API_ENTRY_CHAN(fail_value) \ - fluid_return_val_if_fail (synth != NULL, fail_value); \ - fluid_return_val_if_fail (chan >= 0, fail_value); \ - fluid_synth_api_enter(synth); \ - if (chan >= synth->midi_channels) { \ - fluid_synth_api_exit(synth); \ - return fail_value; \ - } \ - -#define FLUID_API_RETURN(return_value) \ - do { fluid_synth_api_exit(synth); \ - return return_value; } while (0) - /* * void fluid_synth_init * @@ -273,253 +281,286 @@ fluid_version_str (void) static void fluid_synth_init(void) { - fluid_synth_initialized++; - #ifdef TRAP_ON_FPE - /* Turn on floating point exception traps */ - feenableexcept (FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); + /* Turn on floating point exception traps */ + feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); #endif - fluid_conversion_config(); - - fluid_rvoice_dsp_config(); - - fluid_sys_config(); - - init_dither(); - - - /* 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 Chorus 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 */ -} - -static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t* synth) -{ - if (synth->eventhandler->is_threadsafe) + fluid_conversion_config(); + + fluid_rvoice_dsp_config(); + + init_dither(); + + /* custom_breath2att_mod is not a default modulator specified in SF2.01. + it is intended to replace default_vel2att_mod on demand using + API fluid_set_breath_mode() or command shell setbreathmode. + */ + fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */ + BREATH_MSB, /* Source. breath MSB corresponds to 2. */ + FLUID_MOD_CC /* 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(&custom_breath2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ + + /* 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, FLUID_PEAK_ATTENUATION); /* 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, MODULATION_MSB, /* 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, VOLUME_MSB, /* 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, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ + fluid_mod_set_source1(&default_pan_mod, PAN_MSB, /* 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, EXPRESSION_MSB, /* 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, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ + fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* 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 Chorus send */ + fluid_mod_set_source1(&default_chorus_mod, EFFECTS_DEPTH3, /* 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 */ + + + /* Non-standard MIDI continuous controller 8 to channel stereo balance */ + fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&custom_balance_mod, 0, 0); + fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ + /* Amount: 96 dB of attenuation (on the opposite channel) */ + fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ +} + +static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth) +{ return fluid_atomic_int_get(&synth->ticks_since_start); - else - return synth->ticks_since_start; } -static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t* synth, int val) +static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t *synth, int val) { - if (synth->eventhandler->is_threadsafe) - fluid_atomic_int_add((int*) &synth->ticks_since_start, val); - else - synth->ticks_since_start += val; + fluid_atomic_int_add(&synth->ticks_since_start, val); } /*************************************************************** - * FLUID SAMPLE TIMERS - * Timers that use written audio data as timing reference + * FLUID SAMPLE TIMERS + * Timers that use written audio data as timing reference */ struct _fluid_sample_timer_t { - fluid_sample_timer_t* next; /* Single linked list of timers */ - unsigned long starttick; - fluid_timer_callback_t callback; - void* data; - int isfinished; + fluid_sample_timer_t *next; /* Single linked list of timers */ + unsigned long starttick; + fluid_timer_callback_t callback; + void *data; + int isfinished; }; /* * fluid_sample_timer_process - called when synth->ticks is updated */ -static void fluid_sample_timer_process(fluid_synth_t* synth) -{ - fluid_sample_timer_t* st; - long msec; - int cont; - unsigned int ticks = fluid_synth_get_ticks(synth); - - for (st=synth->sample_timers; st; st=st->next) { - if (st->isfinished) { - continue; - } - - msec = (long) (1000.0*((double) (ticks - st->starttick))/synth->sample_rate); - cont = (*st->callback)(st->data, msec); - if (cont == 0) { - st->isfinished = 1; - } - } -} - -fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data) -{ - fluid_sample_timer_t* result = FLUID_NEW(fluid_sample_timer_t); - if (result == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - result->starttick = fluid_synth_get_ticks(synth); - result->isfinished = 0; - result->data = data; - result->callback = callback; - result->next = synth->sample_timers; - synth->sample_timers = result; - return result; -} - -int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer) -{ - fluid_sample_timer_t** ptr = &synth->sample_timers; - while (*ptr) { - if (*ptr == timer) { - *ptr = timer->next; - FLUID_FREE(timer); - return FLUID_OK; - } - ptr = &((*ptr)->next); - } - FLUID_LOG(FLUID_ERR,"delete_fluid_sample_timer failed, no timer found"); - return FLUID_FAILED; +static void fluid_sample_timer_process(fluid_synth_t *synth) +{ + fluid_sample_timer_t *st, *stnext; + long msec; + int cont; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(st = synth->sample_timers; st; st = stnext) + { + /* st may be freed in the callback below. cache it's successor now to avoid use after free */ + stnext = st->next; + + if(st->isfinished) + { + continue; + } + + msec = (long)(1000.0 * ((double)(ticks - st->starttick)) / synth->sample_rate); + cont = (*st->callback)(st->data, msec); + + if(cont == 0) + { + st->isfinished = 1; + } + } +} + +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data) +{ + fluid_sample_timer_t *result = FLUID_NEW(fluid_sample_timer_t); + + if(result == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + result->starttick = fluid_synth_get_ticks(synth); + result->isfinished = 0; + result->data = data; + result->callback = callback; + result->next = synth->sample_timers; + synth->sample_timers = result; + return result; +} + +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer) +{ + fluid_sample_timer_t **ptr; + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(timer != NULL); + + ptr = &synth->sample_timers; + + while(*ptr) + { + if(*ptr == timer) + { + *ptr = timer->next; + FLUID_FREE(timer); + return; + } + + ptr = &((*ptr)->next); + } } @@ -529,362 +570,536 @@ int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer) */ static FLUID_INLINE void -fluid_synth_update_mixer(fluid_synth_t* synth, void* method, int intparam, - fluid_real_t realparam) +fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t method, int intparam, + fluid_real_t realparam) { - fluid_return_if_fail(synth != NULL || synth->eventhandler != NULL); - fluid_return_if_fail(synth->eventhandler->mixer != NULL); - fluid_rvoice_eventhandler_push(synth->eventhandler, method, - synth->eventhandler->mixer, - intparam, realparam); + fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL); + fluid_return_if_fail(synth->eventhandler->mixer != NULL); + fluid_rvoice_eventhandler_push_int_real(synth->eventhandler, method, + synth->eventhandler->mixer, + intparam, realparam); } +static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth) +{ + int i; + fluid_settings_getint(synth->settings, "synth.min-note-length", &i); + return (unsigned int)(i * synth->sample_rate / 1000.0f); +} /** * Create new FluidSynth instance. * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * - * NOTE: The settings parameter is used directly and should not be modified + * @note The settings parameter is used directly and should not be modified * or freed independently. */ -fluid_synth_t* +fluid_synth_t * new_fluid_synth(fluid_settings_t *settings) { - fluid_synth_t* synth; - fluid_sfloader_t* loader; - double gain; - int i, nbuf; + fluid_synth_t *synth; + fluid_sfloader_t *loader; + char *important_channels; + int i, nbuf, prio_level = 0; + int with_ladspa = 0; + + /* initialize all the conversion tables and other stuff */ + if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1)) + { + fluid_synth_init(); + } - /* initialize all the conversion tables and other stuff */ - if (fluid_synth_initialized == 0) { - fluid_synth_init(); - } + /* allocate a new synthesizer object */ + synth = FLUID_NEW(fluid_synth_t); - /* 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_rec_mutex_init(synth->mutex); - fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); - synth->public_api_count = 0; - - synth->settings = settings; - - fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb); - fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus); - fluid_settings_getint(settings, "synth.verbose", &synth->verbose); - fluid_settings_getint(settings, "synth.dump", &synth->dump); - - 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", &gain); - synth->gain = gain; - fluid_settings_getint(settings, "synth.device-id", &synth->device_id); - fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); - - /* register the callbacks */ - fluid_settings_register_num(settings, "synth.sample-rate", - 44100.0f, 8000.0f, 96000.0f, 0, - (fluid_num_update_t) fluid_synth_update_sample_rate, synth); - 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, 1, 65535, 0, - (fluid_int_update_t) fluid_synth_update_polyphony, - synth); - fluid_settings_register_int(settings, "synth.device-id", - synth->device_id, 126, 0, 0, - (fluid_int_update_t) fluid_synth_update_device_id, synth); - - fluid_synth_register_overflow(settings, - (fluid_num_update_t) fluid_synth_update_overflow, 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. */ - nbuf = synth->audio_channels; - if (synth->audio_groups > nbuf) { - nbuf = synth->audio_groups; - } - - /* as soon as the synth is created it starts playing. */ - synth->state = FLUID_SYNTH_PLAYING; - synth->sfont_info = NULL; - synth->sfont_hash = new_fluid_hashtable (NULL, NULL); - synth->noteid = 0; - synth->ticks_since_start = 0; - synth->tuning = NULL; - fluid_private_init(synth->tuning_iter); - - /* Allocate event queue for rvoice mixer */ - fluid_settings_getint(settings, "synth.parallel-render", &i); - /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ - synth->eventhandler = new_fluid_rvoice_eventhandler(i, synth->polyphony*64, - synth->polyphony, nbuf, synth->effects_channels, synth->sample_rate); - - if (synth->eventhandler == NULL) - goto error_recovery; + if(synth == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); + + fluid_rec_mutex_init(synth->mutex); + fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); + synth->public_api_count = 0; + + synth->settings = settings; + + fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb); + fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus); + fluid_settings_getint(settings, "synth.verbose", &synth->verbose); + + 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_getint(settings, "synth.effects-groups", &synth->effects_groups); + fluid_settings_getnum_float(settings, "synth.gain", &synth->gain); + fluid_settings_getint(settings, "synth.device-id", &synth->device_id); + fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); + + fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion); + fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released); + fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained); + fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume); + fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age); + fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important); + + /* register the callbacks */ + fluid_settings_callback_num(settings, "synth.sample-rate", + fluid_synth_handle_sample_rate, synth); + fluid_settings_callback_num(settings, "synth.gain", + fluid_synth_handle_gain, synth); + fluid_settings_callback_int(settings, "synth.polyphony", + fluid_synth_handle_polyphony, synth); + fluid_settings_callback_int(settings, "synth.device-id", + fluid_synth_handle_device_id, synth); + fluid_settings_callback_num(settings, "synth.overflow.percussion", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.sustained", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.released", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.age", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.volume", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.important", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_str(settings, "synth.overflow.important-channels", + fluid_synth_handle_important_channels, synth); + fluid_settings_callback_num(settings, "synth.reverb.room-size", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.damp", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.width", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_int(settings, "synth.reverb.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.nr", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_num(settings, "synth.chorus.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.depth", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.speed", + fluid_synth_handle_reverb_chorus_num, 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. */ + nbuf = synth->audio_channels; + + if(synth->audio_groups > nbuf) + { + nbuf = synth->audio_groups; + } + + if(fluid_settings_dupstr(settings, "synth.overflow.important-channels", + &important_channels) == FLUID_OK) + { + if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels"); + } + + FLUID_FREE(important_channels); + } + + /* as soon as the synth is created it starts playing. */ + synth->state = FLUID_SYNTH_PLAYING; + + synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */ + + fluid_atomic_int_set(&synth->ticks_since_start, 0); + synth->tuning = NULL; + fluid_private_init(synth->tuning_iter); + + /* Initialize multi-core variables if multiple cores enabled */ + if(synth->cores > 1) + { + fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level); + } + + /* Allocate event queue for rvoice mixer */ + /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ + synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64, + synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups, synth->sample_rate, synth->cores - 1, prio_level); + + if(synth->eventhandler == NULL) + { + goto error_recovery; + } + /* Setup the list of default modulators. + * Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */ + synth->default_mod = NULL; + fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD); + + /* Create and initialize the Fx unit.*/ + fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa); + + if(with_ladspa) + { #ifdef LADSPA - /* Create and initialize the Fx unit.*/ - synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth); - fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->LADSPA_FxUnit); -#endif - - /* allocate and add the default sfont loader */ - loader = new_fluid_defsfloader(settings); - - 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; - } - } - - fluid_synth_set_sample_rate(synth, synth->sample_rate); - - fluid_synth_update_overflow(synth, "", 0.0f); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, - synth->polyphony, 0.0f); - fluid_synth_set_reverb_on(synth, synth->with_reverb); - fluid_synth_set_chorus_on(synth, synth->with_chorus); - - synth->cur = FLUID_BUFSIZE; - synth->curmax = 0; - synth->dither_index = 0; - - synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE; - synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP; - synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH; - synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL; - - fluid_rvoice_eventhandler_push5(synth->eventhandler, - fluid_rvoice_mixer_set_reverb_params, - synth->eventhandler->mixer, - FLUID_REVMODEL_SET_ALL, synth->reverb_roomsize, - synth->reverb_damping, synth->reverb_width, - synth->reverb_level, 0.0f); - - /* Initialize multi-core variables if multiple cores enabled */ - if (synth->cores > 1) - { - int prio_level = 0; - fluid_settings_getint (synth->settings, "audio.realtime-prio", &prio_level); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_threads, - synth->cores-1, prio_level); - } - - synth->bank_select = FLUID_BANK_STYLE_GS; - if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gm") == 1) - synth->bank_select = FLUID_BANK_STYLE_GM; - else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gs") == 1) + synth->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate, + FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE); + + if(synth->ladspa_fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx, + synth->audio_groups); +#else /* LADSPA */ + FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support"); +#endif /* LADSPA */ + } + + /* allocate and add the default sfont loader */ + loader = new_fluid_defsfloader(settings); + + 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->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + goto error_recovery; + } + } + + /* sets a default basic channel */ + /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */ + /* (i.e all channels are polyphonic) */ + /* Must be called after channel objects allocation */ + fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + fluid_synth_set_reverb_on(synth, synth->with_reverb); + fluid_synth_set_chorus_on(synth, synth->with_chorus); + + synth->cur = FLUID_BUFSIZE; + synth->curmax = 0; + synth->dither_index = 0; + + { + double room, damp, width, level; + + fluid_settings_getnum(settings, "synth.reverb.room-size", &room); + fluid_settings_getnum(settings, "synth.reverb.damp", &damp); + fluid_settings_getnum(settings, "synth.reverb.width", &width); + fluid_settings_getnum(settings, "synth.reverb.level", &level); + + fluid_synth_set_reverb_full_LOCAL(synth, + FLUID_REVMODEL_SET_ALL, + room, + damp, + width, + level); + } + + { + double level, speed, depth; + + fluid_settings_getint(settings, "synth.chorus.nr", &i); + fluid_settings_getnum(settings, "synth.chorus.level", &level); + fluid_settings_getnum(settings, "synth.chorus.speed", &speed); + fluid_settings_getnum(settings, "synth.chorus.depth", &depth); + + fluid_synth_set_chorus_full_LOCAL(synth, + FLUID_CHORUS_SET_ALL, + i, + level, + speed, + depth, + FLUID_CHORUS_DEFAULT_TYPE); + } + + synth->bank_select = FLUID_BANK_STYLE_GS; - else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "xg") == 1) - synth->bank_select = FLUID_BANK_STYLE_XG; - else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "mma") == 1) - synth->bank_select = FLUID_BANK_STYLE_MMA; - fluid_synth_process_event_queue(synth); + if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm")) + { + synth->bank_select = FLUID_BANK_STYLE_GM; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs")) + { + synth->bank_select = FLUID_BANK_STYLE_GS; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg")) + { + synth->bank_select = FLUID_BANK_STYLE_XG; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma")) + { + synth->bank_select = FLUID_BANK_STYLE_MMA; + } + + fluid_synth_process_event_queue(synth); - /* FIXME */ - synth->start = fluid_curtime(); + /* FIXME */ + synth->start = fluid_curtime(); - return synth; + return synth; - error_recovery: - delete_fluid_synth(synth); - return NULL; +error_recovery: + delete_fluid_synth(synth); + return NULL; } /** * Delete a FluidSynth instance. * @param synth FluidSynth instance to delete - * @return FLUID_OK * - * NOTE: Other users of a synthesizer instance, such as audio and MIDI drivers, + * @note Other users of a synthesizer instance, such as audio and MIDI drivers, * should be deleted prior to freeing the FluidSynth instance. */ -int -delete_fluid_synth(fluid_synth_t* synth) +void +delete_fluid_synth(fluid_synth_t *synth) { - int i, k; - fluid_list_t *list; - fluid_sfont_info_t* sfont_info; -// fluid_event_queue_t* queue; - fluid_sfloader_t* loader; + int i, k; + fluid_list_t *list; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + fluid_mod_t *default_mod; + fluid_mod_t *mod; - if (synth == NULL) { - return FLUID_OK; - } - - fluid_profiling_print(); - - /* turn off all voices, needed to unload SoundFont data */ - if (synth->voice != NULL) { - for (i = 0; i < synth->nvoice; i++) { - fluid_voice_t* voice = synth->voice[i]; - if (!voice) - continue; - fluid_voice_unlock_rvoice(voice); - fluid_voice_overflow_rvoice_finished(voice); - if (fluid_voice_is_playing(voice)) - fluid_voice_off(voice); - } - } - - /* also unset all presets for clean SoundFont unload */ - if (synth->channel != NULL) - for (i = 0; i < synth->midi_channels; i++) - if (synth->channel[i] != NULL) - fluid_channel_set_preset(synth->channel[i], NULL); - - if (synth->eventhandler) - delete_fluid_rvoice_eventhandler(synth->eventhandler); + fluid_return_if_fail(synth != NULL); + + fluid_profiling_print(); + + /* turn off all voices, needed to unload SoundFont data */ + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + fluid_voice_t *voice = synth->voice[i]; + + if(!voice) + { + continue; + } + + fluid_voice_unlock_rvoice(voice); + fluid_voice_overflow_rvoice_finished(voice); + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + /* If we only use fluid_voice_off(voice) it will trigger a delayed + * fluid_voice_stop(voice) via fluid_synth_check_finished_voices(). + * But here, we are deleting the fluid_synth_t instance so + * fluid_voice_stop() will be never triggered resulting in + * SoundFont data never unloaded (i.e a serious memory leak). + * So, fluid_voice_stop() must be explicitly called to insure + * unloading SoundFont data + */ + fluid_voice_stop(voice); + } + } + } - /* delete all the SoundFonts */ - for (list = synth->sfont_info; list; list = fluid_list_next (list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); - delete_fluid_sfont (sfont_info->sfont); - FLUID_FREE (sfont_info); - } + /* also unset all presets for clean SoundFont unload */ + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_set_preset(synth->channel[i], NULL); + } + } - delete_fluid_list(synth->sfont_info); + delete_fluid_rvoice_eventhandler(synth->eventhandler); + /* delete all the SoundFonts */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + fluid_sfont_delete_internal(sfont); + } - /* Delete the SoundFont info hash */ - if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash); + delete_fluid_list(synth->sfont); + /* delete all the SoundFont loaders */ - /* 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); + } - 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); - delete_fluid_list(synth->loaders); + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + delete_fluid_channel(synth->channel[i]); + } - 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); } - 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]); - } + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + delete_fluid_voice(synth->voice[i]); + } + + FLUID_FREE(synth->voice); } - FLUID_FREE(synth->voice); - } - /* 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) { - delete_fluid_tuning(synth->tuning[i][k]); - } - } - FLUID_FREE(synth->tuning[i]); - } + /* 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++) + { + delete_fluid_tuning(synth->tuning[i][k]); + } + + FLUID_FREE(synth->tuning[i]); + } + } + + FLUID_FREE(synth->tuning); } - FLUID_FREE(synth->tuning); - } - fluid_private_free (synth->tuning_iter); + fluid_private_free(synth->tuning_iter); #ifdef LADSPA - /* Release the LADSPA Fx unit */ - fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); - FLUID_FREE(synth->LADSPA_FxUnit); + /* Release the LADSPA effects unit */ + delete_fluid_ladspa_fx(synth->ladspa_fx); #endif - fluid_rec_mutex_destroy(synth->mutex); + /* delete all default modulators */ + default_mod = synth->default_mod; + + while(default_mod != NULL) + { + mod = default_mod; + default_mod = mod->next; + delete_fluid_mod(mod); + } - FLUID_FREE(synth); + FLUID_FREE(synth->overflow.important_channels); - return FLUID_OK; + fluid_rec_mutex_destroy(synth->mutex); + + FLUID_FREE(synth); } /** @@ -896,10 +1111,10 @@ delete_fluid_synth(fluid_synth_t* synth) */ /* FIXME - The error messages are not thread-safe, yet. They are still stored * in a global message buffer (see fluid_sys.c). */ -char* -fluid_synth_error(fluid_synth_t* synth) +const char * +fluid_synth_error(fluid_synth_t *synth) { - return fluid_error(); + return fluid_error(); } /** @@ -908,288 +1123,630 @@ fluid_synth_error(fluid_synth_t* synth) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @param vel MIDI velocity (0-127, 0=noteoff) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) +fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel) { - int result; - fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); - fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 0 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - result = fluid_synth_noteon_LOCAL (synth, chan, key, vel); - FLUID_API_RETURN(result); + result = fluid_synth_noteon_LOCAL(synth, chan, key, vel); + FLUID_API_RETURN(result); } /* Local synthesis thread variant of fluid_synth_noteon */ static int -fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) +fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, int vel) { - fluid_channel_t* channel; - - /* notes with velocity zero go to noteoff */ - if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key); + fluid_channel_t *channel ; - 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, - fluid_synth_get_ticks(synth) / 44100.0f, - (fluid_curtime() - synth->start) / 1000.0f, - 0.0f, 0, "channel has no preset"); + /* notes with velocity zero go to noteoff */ + if(vel == 0) + { + return fluid_synth_noteoff_LOCAL(synth, chan, key); } - 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_LOCAL(synth, chan, key); + channel = synth->channel[chan]; + + /* makes 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, + fluid_synth_get_ticks(synth) / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, 0, "channel has no preset"); + } + return FLUID_FAILED; + } - return fluid_preset_noteon(channel->preset, synth, chan, key, vel); + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOn in monophonic */ + return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); + } + else + { + /* channel is poly and legato CC is Off) */ + + /* plays the noteOn in polyphonic */ + /* Sets the note at first position in monophonic list */ + /* In the case where the musician intends to inter the channel in monophonic + (by depressing the CC legato on), the next noteOn mono could be played legato + with the previous note poly (if the musician choose this). + */ + fluid_channel_set_onenote_monolist(channel, (unsigned char) key, + (unsigned char) vel); + + /* 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_LOCAL(synth, chan, key); + + /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato(). + This allows an opportunity to get this note played legato with a previous + note if a CC PTC have been received before this noteon. This behavior is + a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11 + for details). + */ + return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel); + } } /** - * Send a note-off event to a FluidSynth object. + * Sends a note-off event to a FluidSynth object. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no + * @return #FLUID_OK on success, #FLUID_FAILED otherwise (may just mean that no * voices matched the note off event) */ int -fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) +fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key) { - int result; - fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - result = fluid_synth_noteoff_LOCAL (synth, chan, key); - - FLUID_API_RETURN(result); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteoff_LOCAL(synth, chan, key); + FLUID_API_RETURN(result); } /* Local synthesis thread variant of fluid_synth_noteoff */ static int -fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) -{ - fluid_voice_t* voice; - int status = FLUID_FAILED; - int i; - - 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%d", - voice->chan, voice->key, 0, voice->id, - (fluid_curtime() - synth->start) / 1000.0f, - used_voices); - } /* if verbose */ - - fluid_voice_noteoff(voice); - status = FLUID_OK; - } /* if voice on */ - } /* for all voices */ - return status; -} - -/* Damp voices on a channel (turn notes off), if they're sustained by +fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + fluid_channel_t *channel = synth->channel[chan]; + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOff in monophonic */ + status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); + } + else + { + /* channel is poly and legato CC is Off) */ + /* removes the note from the monophonic list */ + if(key == fluid_channel_last_note(channel)) + { + fluid_channel_clear_monolist(channel); + } + + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + /* Changes the state (Valid/Invalid) of the most recent note played in a + staccato manner */ + fluid_channel_invalid_prev_note_staccato(channel); + return status; +} + +/* Damps voices on a channel (turn notes off), if they're sustained by sustain pedal */ static int -fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t *synth, int chan) { - fluid_voice_t* voice; - int i; + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - if ((voice->chan == chan) && _SUSTAINED(voice)) - fluid_voice_release(voice); - } + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sustain pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } - return FLUID_OK; + return FLUID_OK; } -/* Damp voices on a channel (turn notes off), if they're sustained by +/* Damps voices on a channel (turn notes off), if they're sustained by sostenuto pedal */ static int -fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan) { - fluid_voice_t* voice; - int i; + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - if ((voice->chan == chan) && _HELD_BY_SOSTENUTO(voice)) - fluid_voice_release(voice); - } + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sostenuto pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } - return FLUID_OK; + return FLUID_OK; } - /** - * Send a MIDI controller event on a MIDI channel. + * Adds the specified modulator \c mod as default modulator to the synth. \c mod will + * take effect for any subsequently created voice. * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param num MIDI controller number (0-127) - * @param val MIDI controller value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards) + * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory allocation) and therefore should not be called + * from synthesis context at the risk of stalling audio output. */ int -fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) +fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode) { - int result; - fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED); - fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + fluid_mod_t *default_mod; + fluid_mod_t *last_mod = NULL; + fluid_mod_t *new_mod; - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - fluid_channel_set_cc (synth->channel[chan], num, val); - result = fluid_synth_cc_LOCAL (synth, chan, num); - FLUID_API_RETURN(result); -} + default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(mode == FLUID_SYNTH_ADD) + { + default_mod->amount += mod->amount; + } + else if(mode == FLUID_SYNTH_OVERWRITE) + { + default_mod->amount = mod->amount; + } + else + { + FLUID_API_RETURN(FLUID_FAILED); + } + + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + /* Add a new modulator (no existing modulator to add / overwrite). */ + new_mod = new_fluid_mod(); + + if(new_mod == NULL) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + fluid_mod_clone(new_mod, mod); + new_mod->next = NULL; + + if(last_mod == NULL) + { + synth->default_mod = new_mod; + } + else + { + last_mod->next = new_mod; + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Removes the specified modulator \c mod from the synth's default modulator list. + * fluid_mod_test_identity() will be used to test modulator matching. + * @param synth synth instance + * @param mod The modulator to remove + * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory allocation) and therefore should not be called + * from synthesis context at the risk of stalling audio output. + */ +int +fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod) +{ + fluid_mod_t *default_mod; + fluid_mod_t *last_mod; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + last_mod = default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(synth->default_mod == default_mod) + { + synth->default_mod = synth->default_mod->next; + } + else + { + last_mod->next = default_mod->next; + } + + delete_fluid_mod(default_mod); + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + FLUID_API_RETURN(FLUID_FAILED); +} + + +/** + * Send a MIDI controller event on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param val MIDI controller value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function supports MIDI Global Controllers which will be sent to + * all channels of the basic channel if this basic channel is in mode OmniOff/Mono. + * This is accomplished by sending the CC one MIDI channel below the basic + * channel of the receiver. + * Examples: let a synthesizer with 16 MIDI channels: + * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it + * could be used as CC global for all channels belonging to basic channel 7. + * - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used + * as CC global for all channels belonging to basic channel 0. + */ +int +fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) +{ + int result = FLUID_FAILED; + fluid_channel_t *channel; + fluid_return_val_if_fail(num >= 0 && num <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->mode & FLUID_CHANNEL_ENABLED) + { + /* chan is enabled */ + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + } + + fluid_channel_set_cc(channel, num, val); + result = fluid_synth_cc_LOCAL(synth, chan, num); + } + else /* chan is disabled so it is a candidate for global channel */ + { + /* looks for next basic channel */ + int n_chan = synth->midi_channels; /* MIDI Channels number */ + int basicchan ; + + if(chan < n_chan - 1) + { + basicchan = chan + 1; /* next channel */ + } + else + { + basicchan = 0; /* wrap to 0 */ + } + + channel = synth->channel[basicchan]; + + /* Channel must be a basicchan in mode OMNIOFF_MONO */ + if((channel->mode & FLUID_CHANNEL_BASIC) && + ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO)) + { + /* sends cc to all channels in this basic channel */ + int i, nbr = channel->mode_val; + + for(i = basicchan; i < basicchan + nbr; i++) + { + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + } + + fluid_channel_set_cc(synth->channel[i], num, val); + result = fluid_synth_cc_LOCAL(synth, i, num); + } + } + /* The channel chan is not a valid 'global channel' */ + else + { + result = FLUID_FAILED; + } + } + + FLUID_API_RETURN(result); +} /* Local synthesis thread variant of MIDI CC set function. */ static int -fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) -{ - fluid_channel_t* chan = synth->channel[channum]; - int nrpn_select; - int value; - - value = fluid_channel_get_cc (chan, num); - - switch (num) { - case SUSTAIN_SWITCH: - /* Release voices if Sustain switch is released */ - if (value < 64) /* Sustain is released */ - fluid_synth_damp_voices_by_sustain_LOCAL (synth, channum); - break; - - case SOSTENUTO_SWITCH: - /* Release voices if Sostetuno switch is released */ - if (value < 64) /* Sostenuto is released */ - fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); - else /* Sostenuto is depressed */ - /* Update sostenuto order id when pedaling on Sostenuto */ - chan->sostenuto_orderid = synth->noteid; /* future voice id value */ - break; - - case BANK_SELECT_MSB: - fluid_channel_set_bank_msb (chan, value & 0x7F); - break; - case BANK_SELECT_LSB: - fluid_channel_set_bank_lsb (chan, value & 0x7F); - break; - case ALL_NOTES_OFF: - fluid_synth_all_notes_off_LOCAL (synth, channum); - break; - case ALL_SOUND_OFF: - fluid_synth_all_sounds_off_LOCAL (synth, channum); - break; - case ALL_CTRL_OFF: - fluid_channel_init_ctrl (chan, 1); - fluid_synth_modulate_voices_all_LOCAL (synth, channum); - break; - case DATA_ENTRY_MSB: - { - int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB); - - if (chan->nrpn_active) /* NRPN is active? */ - { /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if ((fluid_channel_get_cc (chan, NRPN_MSB) == 120) - && (fluid_channel_get_cc (chan, NRPN_LSB) < 100)) - { - nrpn_select = chan->nrpn_select; - - if (nrpn_select < GEN_LAST) - { - float val = fluid_gen_scale_nrpn (nrpn_select, data); - fluid_synth_set_gen_LOCAL (synth, channum, nrpn_select, val, FALSE); - } - - chan->nrpn_select = 0; /* Reset to 0 */ - } - } - else if (fluid_channel_get_cc (chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ - { - switch (fluid_channel_get_cc (chan, RPN_LSB)) - { - case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */ - fluid_channel_set_pitch_wheel_sensitivity (synth->channel[channum], value); - fluid_synth_update_pitch_wheel_sens_LOCAL (synth, channum); /* Update bend range */ - /* FIXME - Handle LSB? (Fine bend range in cents) */ - break; - case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over 1 semitone (+/- 50 cents, 8192 = center) */ - fluid_synth_set_gen_LOCAL (synth, channum, GEN_FINETUNE, - (data - 8192) / 8192.0 * 50.0, FALSE); - break; - case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ - fluid_synth_set_gen_LOCAL (synth, channum, GEN_COARSETUNE, - value - 64, FALSE); - break; - case RPN_TUNING_PROGRAM_CHANGE: - fluid_channel_set_tuning_prog (chan, value); - fluid_synth_activate_tuning (synth, channum, - fluid_channel_get_tuning_bank (chan), - value, TRUE); - break; - case RPN_TUNING_BANK_SELECT: - fluid_channel_set_tuning_bank (chan, value); - break; - case RPN_MODULATION_DEPTH_RANGE: - break; +fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) +{ + fluid_channel_t *chan = synth->channel[channum]; + int nrpn_select; + int value; + + value = fluid_channel_get_cc(chan, num); + + switch(num) + { + + /* CC omnioff, omnion, mono, poly */ + case POLY_OFF: + case POLY_ON: + case OMNI_OFF: + case OMNI_ON: + + /* allowed only if channum is a basic channel */ + if(chan->mode & FLUID_CHANNEL_BASIC) + { + /* Construction of new_mode from current channel mode and this CC mode */ + int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK; + + switch(num) + { + case POLY_OFF: + new_mode |= FLUID_CHANNEL_POLY_OFF; + break; + + case POLY_ON: + new_mode &= ~FLUID_CHANNEL_POLY_OFF; + break; + + case OMNI_OFF: + new_mode |= FLUID_CHANNEL_OMNI_OFF; + break; + + case OMNI_ON: + new_mode &= ~FLUID_CHANNEL_OMNI_OFF; + break; + + default: /* should never happen */ + return FLUID_FAILED; + } + + /* MIDI specs: if value is 0 it means all channels from channum to next + basic channel minus 1 (if any) or to MIDI channel count minus 1. + However, if value is > 0 (e.g. 4), the group of channels will be be + limited to 4. + value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode + implies a group of only one channel. + */ + /* Checks value range and changes this existing basic channel group */ + value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value); + + if(value != FLUID_FAILED) + { + /* reset the current basic channel before changing it */ + fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val); + fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value); + break; /* FLUID_OK */ + } + } + + return FLUID_FAILED; + + case LEGATO_SWITCH: + /* handles Poly/mono commutation on Legato pedal On/Off.*/ + fluid_channel_cc_legato(chan, value); + break; + + case PORTAMENTO_SWITCH: + /* Special handling of the monophonic list */ + /* Invalids the most recent note played in a staccato manner */ + fluid_channel_invalid_prev_note_staccato(chan); + break; + + case SUSTAIN_SWITCH: + + /* Release voices if Sustain switch is released */ + if(value < 64) /* Sustain is released */ + { + fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum); + } + + break; + + case SOSTENUTO_SWITCH: + + /* Release voices if Sostetuno switch is released */ + if(value < 64) /* Sostenuto is released */ + { + fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); + } + else /* Sostenuto is depressed */ + /* Update sostenuto order id when pedaling on Sostenuto */ + { + chan->sostenuto_orderid = synth->noteid; /* future voice id value */ + } + + break; + + case BANK_SELECT_MSB: + fluid_channel_set_bank_msb(chan, value & 0x7F); + break; + + case BANK_SELECT_LSB: + fluid_channel_set_bank_lsb(chan, value & 0x7F); + break; + + case ALL_NOTES_OFF: + fluid_synth_all_notes_off_LOCAL(synth, channum); + break; + + case ALL_SOUND_OFF: + fluid_synth_all_sounds_off_LOCAL(synth, channum); + break; + + case ALL_CTRL_OFF: + fluid_channel_init_ctrl(chan, 1); + fluid_synth_modulate_voices_all_LOCAL(synth, channum); + break; + + case DATA_ENTRY_MSB: + { + int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); + + if(chan->nrpn_active) /* NRPN is active? */ + { + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if((fluid_channel_get_cc(chan, NRPN_MSB) == 120) + && (fluid_channel_get_cc(chan, NRPN_LSB) < 100)) + { + nrpn_select = chan->nrpn_select; + + if(nrpn_select < GEN_LAST) + { + float val = fluid_gen_scale_nrpn(nrpn_select, data); + fluid_synth_set_gen_LOCAL(synth, channum, nrpn_select, val, FALSE); + } + + chan->nrpn_select = 0; /* Reset to 0 */ + } + } + else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ + { + switch(fluid_channel_get_cc(chan, RPN_LSB)) + { + case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */ + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], value); + fluid_synth_update_pitch_wheel_sens_LOCAL(synth, channum); /* Update bend range */ + /* FIXME - Handle LSB? (Fine bend range in cents) */ + break; + + case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE, + (data - 8192) / 8192.0 * 100.0, FALSE); + break; + + case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_COARSETUNE, + value - 64, FALSE); + break; + + case RPN_TUNING_PROGRAM_CHANGE: + fluid_channel_set_tuning_prog(chan, value); + fluid_synth_activate_tuning(synth, channum, + fluid_channel_get_tuning_bank(chan), + value, TRUE); + break; + + case RPN_TUNING_BANK_SELECT: + fluid_channel_set_tuning_bank(chan, value); + break; + + case RPN_MODULATION_DEPTH_RANGE: + break; + } + } + + break; + } + + case NRPN_MSB: + fluid_channel_set_cc(chan, NRPN_LSB, 0); + chan->nrpn_select = 0; + chan->nrpn_active = 1; + break; + + case NRPN_LSB: + + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if(fluid_channel_get_cc(chan, 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; + } } - } - break; - } - case NRPN_MSB: - fluid_channel_set_cc (chan, NRPN_LSB, 0); - chan->nrpn_select = 0; - chan->nrpn_active = 1; - break; - case NRPN_LSB: - /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if (fluid_channel_get_cc (chan, 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; - } - } - - chan->nrpn_active = 1; - break; - case RPN_MSB: - case RPN_LSB: - chan->nrpn_active = 0; - break; - default: - return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num); - } - - return FLUID_OK; + + chan->nrpn_active = 1; + break; + + case RPN_MSB: + case RPN_LSB: + chan->nrpn_active = 0; + break; + + case BREATH_MSB: + /* handles CC Breath On/Off noteOn/noteOff mode */ + fluid_channel_cc_breath_note_on_off(chan, value); + + /* fall-through */ + default: + return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); + } + + return FLUID_OK; } /** @@ -1198,30 +1755,35 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param num MIDI controller number (0-127) * @param pval Location to store MIDI controller value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) +fluid_synth_get_cc(fluid_synth_t *synth, int chan, int num, int *pval) { - fluid_return_val_if_fail (num >= 0 && num < 128, FLUID_FAILED); - fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); + fluid_return_val_if_fail(num >= 0 && num < 128, FLUID_FAILED); + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - *pval = fluid_channel_get_cc (synth->channel[chan], num); - FLUID_API_RETURN(FLUID_OK); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_cc(synth->channel[chan], num); + FLUID_API_RETURN(FLUID_OK); } /* * Handler for synth.device-id setting. */ -static int -fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value) +static void +fluid_synth_handle_device_id(void *data, const char *name, int value) { - fluid_synth_api_enter(synth); - synth->device_id = value; - fluid_synth_api_exit(synth); - return 0; + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + synth->device_id = value; + fluid_synth_api_exit(synth); } /** @@ -1237,7 +1799,7 @@ fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value) * recognized and handled or not (set to TRUE if it was handled) * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX * command (useful for checking if a SYSEX message would be handled) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ /* SYSEX format (0xF0 and 0xF7 not passed to this function): @@ -1249,397 +1811,502 @@ int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int *handled, int dryrun) { - int avail_response = 0; + int avail_response = 0; - if (handled) *handled = FALSE; + if(handled) + { + *handled = FALSE; + } - if (response_len) - { - avail_response = *response_len; - *response_len = 0; - } + if(response_len) + { + avail_response = *response_len; + *response_len = 0; + } - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (data != NULL, FLUID_FAILED); - fluid_return_val_if_fail (len > 0, FLUID_FAILED); - fluid_return_val_if_fail (!response || response_len, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(!response || response_len, FLUID_FAILED); - if (len < 4) return FLUID_OK; + if(len < 4) + { + return FLUID_OK; + } - /* MIDI tuning SYSEX message? */ - if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) - && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) - && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) - { - int result; - fluid_synth_api_enter(synth); - result = fluid_synth_sysex_midi_tuning (synth, data, len, response, - response_len, avail_response, - handled, dryrun); + /* MIDI tuning SYSEX message? */ + if((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_midi_tuning(synth, data, len, response, + response_len, avail_response, + handled, dryrun); - FLUID_API_RETURN(result); - } - return FLUID_OK; + FLUID_API_RETURN(result); + } + + return FLUID_OK; } /* Handler for MIDI tuning SYSEX messages */ static int -fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len, - char *response, int *response_len, int avail_response, - int *handled, int dryrun) -{ - int realtime, msgid; - int bank = 0, prog, channels; - double tunedata[128]; - int keys[128]; - char name[17]; - int note, frac, frac2; - uint8 chksum; - int i, count, index; - const char *dataptr; - char *resptr;; - - realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; - msgid = data[3]; - - switch (msgid) - { +fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int realtime, msgid; + int bank = 0, prog, channels; + double tunedata[128]; + int keys[128]; + char name[17]; + int note, frac, frac2; + uint8_t chksum; + int i, count, index; + const char *dataptr; + char *resptr;; + + realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; + msgid = data[3]; + + switch(msgid) + { case MIDI_SYSEX_TUNING_BULK_DUMP_REQ: case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK: - if (data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) - { - if (len != 5 || data[4] & 0x80 || !response) - return FLUID_OK; - - *response_len = 406; - prog = data[4]; - } - else - { - if (len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) - return FLUID_OK; - - *response_len = 407; - bank = data[4]; - prog = data[5]; - } - - if (dryrun) - { - if (handled) *handled = TRUE; - return FLUID_OK; - } + if(data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + if(len != 5 || data[4] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 406; + prog = data[4]; + } + else + { + if(len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 407; + bank = data[4]; + prog = data[5]; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } - if (avail_response < *response_len) return FLUID_FAILED; + return FLUID_OK; + } - /* Get tuning data, return if tuning not found */ - if (fluid_synth_tuning_dump (synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) - { - *response_len = 0; - return FLUID_OK; - } + if(avail_response < *response_len) + { + return FLUID_FAILED; + } + + /* Get tuning data, return if tuning not found */ + if(fluid_synth_tuning_dump(synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) + { + *response_len = 0; + return FLUID_OK; + } + + resptr = response; - resptr = response; + *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; + *resptr++ = synth->device_id; + *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; + *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; - *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; - *resptr++ = synth->device_id; - *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; - *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) + { + *resptr++ = bank; + } - if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) - *resptr++ = bank; + *resptr++ = prog; + FLUID_STRNCPY(resptr, name, 16); + resptr += 16; - *resptr++ = prog; - FLUID_STRNCPY (resptr, name, 16); - resptr += 16; + for(i = 0; i < 128; i++) + { + note = tunedata[i] / 100.0; + fluid_clip(note, 0, 127); - for (i = 0; i < 128; i++) - { - note = tunedata[i] / 100.0; - fluid_clip (note, 0, 127); + frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; + fluid_clip(frac, 0, 16383); - frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; - fluid_clip (frac, 0, 16383); + *resptr++ = note; + *resptr++ = frac >> 7; + *resptr++ = frac & 0x7F; + } - *resptr++ = note; - *resptr++ = frac >> 7; - *resptr++ = frac & 0x7F; - } + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + /* NOTE: Checksum is not as straight forward as the bank based messages */ + chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID + ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; + + for(i = 21; i < 128 * 3 + 21; i++) + { + chksum ^= response[i]; + } + } + else + { + for(i = 1, chksum = 0; i < 406; i++) + { + chksum ^= response[i]; + } + } - if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) - { /* NOTE: Checksum is not as straight forward as the bank based messages */ - chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID - ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; + *resptr++ = chksum & 0x7F; - for (i = 21; i < 128 * 3 + 21; i++) - chksum ^= response[i]; - } - else - { - for (i = 1, chksum = 0; i < 406; i++) - chksum ^= response[i]; - } + if(handled) + { + *handled = TRUE; + } - *resptr++ = chksum & 0x7F; + break; - if (handled) *handled = TRUE; - break; case MIDI_SYSEX_TUNING_NOTE_TUNE: case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK: - dataptr = data + 4; - - if (msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) - { - if (len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) - return FLUID_OK; - } - else - { - if (len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 - || len != data[5] * 4 + 7) - return FLUID_OK; - - bank = *dataptr++; - } - - if (dryrun) - { - if (handled) *handled = TRUE; - return FLUID_OK; - } + dataptr = data + 4; + + if(msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) + { + if(len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) + { + return FLUID_OK; + } + } + else + { + if(len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 + || len != data[6] * 4 + 7) + { + return FLUID_OK; + } + + bank = *dataptr++; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + prog = *dataptr++; + count = *dataptr++; + + for(i = 0, index = 0; i < count; i++) + { + note = *dataptr++; + + if(note & 0x80) + { + return FLUID_OK; + } - prog = *dataptr++; - count = *dataptr++; + keys[index] = note; - for (i = 0, index = 0; i < count; i++) - { - note = *dataptr++; - if (note & 0x80) return FLUID_OK; - keys[index] = note; + note = *dataptr++; + frac = *dataptr++; + frac2 = *dataptr++; - note = *dataptr++; - frac = *dataptr++; - frac2 = *dataptr++; + if(note & 0x80 || frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } - if (note & 0x80 || frac & 0x80 || frac2 & 0x80) - return FLUID_OK; + frac = frac << 7 | frac2; - frac = frac << 7 | frac2; + /* No change pitch value? Doesn't really make sense to send that, but.. */ + if(note == 0x7F && frac == 16383) + { + continue; + } - /* No change pitch value? Doesn't really make sense to send that, but.. */ - if (note == 0x7F && frac == 16383) continue; + tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); + index++; + } + + if(index > 0) + { + if(fluid_synth_tune_notes(synth, bank, prog, index, keys, tunedata, + realtime) == FLUID_FAILED) + { + return FLUID_FAILED; + } + } - tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); - index++; - } + if(handled) + { + *handled = TRUE; + } - if (index > 0) - { - if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata, - realtime) == FLUID_FAILED) - return FLUID_FAILED; - } + break; - if (handled) *handled = TRUE; - break; case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE: case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE: - if ((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) - || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) - return FLUID_OK; + if((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) + || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) + { + return FLUID_OK; + } - if (data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) - return FLUID_OK; + if(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) + { + return FLUID_OK; + } - if (dryrun) - { - if (handled) *handled = TRUE; - return FLUID_OK; - } + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } - channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; + channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; - if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) - { - for (i = 0; i < 12; i++) + if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) { - frac = data[i + 7]; - if (frac & 0x80) return FLUID_OK; - tunedata[i] = (int)frac - 64; + for(i = 0; i < 12; i++) + { + frac = data[i + 7]; + + if(frac & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (int)frac - 64; + } } - } - else - { - for (i = 0; i < 12; i++) + else { - frac = data[i * 2 + 7]; - frac2 = data[i * 2 + 8]; - if (frac & 0x80 || frac2 & 0x80) return FLUID_OK; - tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); + for(i = 0; i < 12; i++) + { + frac = data[i * 2 + 7]; + frac2 = data[i * 2 + 8]; + + if(frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); + } } - } - if (fluid_synth_activate_octave_tuning (synth, 0, 0, "SYSEX", + if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX", tunedata, realtime) == FLUID_FAILED) - return FLUID_FAILED; + { + return FLUID_FAILED; + } + + if(channels) + { + for(i = 0; i < 16; i++) + { + if(channels & (1 << i)) + { + fluid_synth_activate_tuning(synth, i, 0, 0, realtime); + } + } + } - if (channels) - { - for (i = 0; i < 16; i++) + if(handled) { - if (channels & (1 << i)) - fluid_synth_activate_tuning (synth, i, 0, 0, realtime); + *handled = TRUE; } - } - if (handled) *handled = TRUE; - break; - } + break; + } - return FLUID_OK; + return FLUID_OK; } /** * Turn off all notes on a MIDI channel (put them into release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.4 */ int -fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) +fluid_synth_all_notes_off(fluid_synth_t *synth, int chan) { - int result; + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (chan >= -1, FLUID_FAILED); - fluid_synth_api_enter(synth); - if (chan >= synth->midi_channels) - result = FLUID_FAILED; - else - result = fluid_synth_all_notes_off_LOCAL (synth, chan); - FLUID_API_RETURN(result); + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_notes_off_LOCAL(synth, chan); + } + + FLUID_API_RETURN(result); } /* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ -static int -fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) +//static int +int +fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan) { - fluid_voice_t* voice; - int i; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_noteoff(voice); + } + } - if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan))) - fluid_voice_noteoff(voice); - } - return FLUID_OK; + return FLUID_OK; } /** * Immediately stop all notes on a MIDI channel (skips release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.4 */ int -fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) +fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan) { - int result; + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_sounds_off_LOCAL(synth, chan); + } - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (chan >= -1, FLUID_FAILED); - fluid_synth_api_enter(synth); - if (chan >= synth->midi_channels) - result = FLUID_FAILED; - else - result = fluid_synth_all_sounds_off_LOCAL (synth, chan); - FLUID_API_RETURN(result); + FLUID_API_RETURN(result); } /* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */ static int -fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan) { - fluid_voice_t* voice; - int i; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_off(voice); + } + } - if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan))) - fluid_voice_off(voice); - } - return FLUID_OK; + return FLUID_OK; } /** * Reset reverb engine * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_reset_reverb(fluid_synth_t* synth) +fluid_synth_reset_reverb(fluid_synth_t *synth) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); - FLUID_API_RETURN(FLUID_OK); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); } /** * Reset chorus engine * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_reset_chorus(fluid_synth_t* synth) +fluid_synth_reset_chorus(fluid_synth_t *synth) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); - FLUID_API_RETURN(FLUID_OK); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); } /** - * Send MIDI system reset command (big red 'panic' button), turns off notes and - * resets controllers. + * Send MIDI system reset command (big red 'panic' button), turns off notes, resets + * controllers and restores initial basic channel configuration. * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_system_reset(fluid_synth_t* synth) +fluid_synth_system_reset(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - result = fluid_synth_system_reset_LOCAL (synth); - FLUID_API_RETURN(result); + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + result = fluid_synth_system_reset_LOCAL(synth); + FLUID_API_RETURN(result); } /* Local variant of the system reset command */ static int -fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) +fluid_synth_system_reset_LOCAL(fluid_synth_t *synth) { - fluid_voice_t* voice; - int i; + int i; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + fluid_synth_all_sounds_off_LOCAL(synth, -1); - if (_PLAYING(voice)) - fluid_voice_off(voice); - } + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_reset(synth->channel[i]); + } - for (i = 0; i < synth->midi_channels; i++) - fluid_channel_reset(synth->channel[i]); + /* Basic channel 0, Mode Omni On Poly */ + fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); - return FLUID_OK; + return FLUID_OK; } /** @@ -1648,42 +2315,50 @@ fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param is_cc Boolean value indicating if ctrl is a CC controller or not * @param ctrl MIDI controller value - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int -fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl) +fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl) { - fluid_voice_t* voice; - int i; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate(voice, is_cc, ctrl); + } + } - if (voice->chan == chan) - fluid_voice_modulate(voice, is_cc, ctrl); - } - return FLUID_OK; + return FLUID_OK; } /** * Update voices on a MIDI channel after all MIDI controllers have been changed. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int -fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan) { - fluid_voice_t* voice; - int i; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate_all(voice); + } + } - if (voice->chan == chan) - fluid_voice_modulate_all(voice); - } - return FLUID_OK; + return FLUID_OK; } /** @@ -1691,30 +2366,93 @@ fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI channel pressure value (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) +fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val) { - int result; - fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); + int result; + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); + } - fluid_channel_set_channel_pressure (synth->channel[chan], val); + fluid_channel_set_channel_pressure(synth->channel[chan], val); + result = fluid_synth_update_channel_pressure_LOCAL(synth, chan); - result = fluid_synth_update_channel_pressure_LOCAL (synth, chan); - FLUID_API_RETURN(result); + FLUID_API_RETURN(result); } /* Updates channel pressure from within synthesis thread */ static int -fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); +} + +/** + * Set the MIDI polyphonic key pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI key number (0-127) + * @param val MIDI key pressure value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 2.0.0 + */ +int +fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); + } + + fluid_channel_set_key_pressure(synth->channel[chan], key, val); + result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key); + + FLUID_API_RETURN(result); +} + +/* Updates key pressure from within synthesis thread */ +static int +fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key) { - return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); + fluid_voice_t *voice; + int i; + int result = FLUID_OK; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(voice->chan == chan && voice->key == key) + { + result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); + + if(result != FLUID_OK) + { + return result; + } + } + } + + return result; } /** @@ -1722,29 +2460,34 @@ fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI pitch bend value (0-16383 with 8192 being center) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) +fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val) { - int result; - fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + int result; + fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + } - fluid_channel_set_pitch_bend (synth->channel[chan], val); + fluid_channel_set_pitch_bend(synth->channel[chan], val); + result = fluid_synth_update_pitch_bend_LOCAL(synth, chan); - result = fluid_synth_update_pitch_bend_LOCAL (synth, chan); - FLUID_API_RETURN(result); + FLUID_API_RETURN(result); } /* Local synthesis thread variant of pitch bend */ static int -fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan) { - return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL); + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL); } /** @@ -1753,16 +2496,22 @@ fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan) * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with * 8192 being center) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) +fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend) { - fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + int result; + fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -1770,29 +2519,34 @@ fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val Pitch wheel sensitivity value in semitones - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) +fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val) { - int result; - fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + int result; + fluid_return_val_if_fail(val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + } - fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val); + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val); + result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan); - result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan); - FLUID_API_RETURN(result); + FLUID_API_RETURN(result); } /* Local synthesis thread variant of set pitch wheel sensitivity */ static int -fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan) +fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan) { - return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); } /** @@ -1800,17 +2554,23 @@ fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param pval Location to store pitch wheel sensitivity value in semitones - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since Sometime AFTER v1.0 API freeze. */ int -fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) +fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval) { - fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + int result; + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -1818,108 +2578,97 @@ fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ static int -fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset) +fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset) { - fluid_channel_t *channel; + fluid_channel_t *channel; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); - channel = synth->channel[chan]; + channel = synth->channel[chan]; - return fluid_channel_set_preset (channel, preset); + return fluid_channel_set_preset(channel, preset); } /* Get a preset by SoundFont, bank and program numbers. * Returns preset pointer or NULL. - * - * NOTE: The returned preset has been allocated, caller owns it and should - * free it when finished using it. */ -static fluid_preset_t* -fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, - unsigned int banknum, unsigned int prognum) +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum) { - fluid_preset_t *preset = NULL; - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; - - /* 128 indicates an "unset" operation" */ - if (prognum == FLUID_UNSET_PROGRAM) return NULL; + fluid_sfont_t *sfont; + fluid_list_t *list; - for (list = synth->sfont_info; list; list = fluid_list_next (list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + /* 128 indicates an "unset" operation" */ + if(prognum == FLUID_UNSET_PROGRAM) + { + return NULL; + } - if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum) + for(list = synth->sfont; list; list = fluid_list_next(list)) { - preset = fluid_sfont_get_preset (sfont_info->sfont, - banknum - sfont_info->bankofs, prognum); - if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ - break; + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfontnum) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } } - } - return preset; + return NULL; } /* Get a preset by SoundFont name, bank and program. * Returns preset pointer or NULL. - * - * NOTE: The returned preset has been allocated, caller owns it and should - * free it when finished using it. */ -static fluid_preset_t* -fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, - unsigned int banknum, unsigned int prognum) +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum) { - fluid_preset_t *preset = NULL; - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; - - for (list = synth->sfont_info; list; list = fluid_list_next (list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + fluid_sfont_t *sfont; + fluid_list_t *list; - if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0) + for(list = synth->sfont; list; list = fluid_list_next(list)) { - preset = fluid_sfont_get_preset (sfont_info->sfont, - banknum - sfont_info->bankofs, prognum); - if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ - break; + sfont = fluid_list_get(list); + + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } } - } - return preset; + return NULL; } /* Find a preset by bank and program numbers. * Returns preset pointer or NULL. - * - * NOTE: The returned preset has been allocated, caller owns it and should - * free it when finished using it. */ -fluid_preset_t* -fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, - unsigned int prognum) + */ +fluid_preset_t * +fluid_synth_find_preset(fluid_synth_t *synth, int banknum, + int prognum) { - fluid_preset_t *preset = NULL; - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; - - for (list = synth->sfont_info; list; list = fluid_list_next (list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + fluid_preset_t *preset; + fluid_sfont_t *sfont; + fluid_list_t *list; - preset = fluid_sfont_get_preset (sfont_info->sfont, - banknum - sfont_info->bankofs, prognum); - if (preset) + for(list = synth->sfont; list; list = fluid_list_next(list)) { - sfont_info->refcount++; /* Add reference to SoundFont */ - break; + sfont = fluid_list_get(list); + + preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + + if(preset) + { + return preset; + } } - } - return preset; + return NULL; } /** @@ -1927,7 +2676,7 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param prognum MIDI program number (0-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ /* FIXME - Currently not real-time safe, due to preset allocation and mutex lock, * and may be called from within synthesis context. */ @@ -1935,72 +2684,90 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, /* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented * since fluid_synth_unset_program() should be used instead. */ int -fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) -{ - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - int subst_bank, subst_prog, banknum = 0, result; - - fluid_return_val_if_fail (prognum >= 0 && prognum <= 128, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - channel = synth->channel[chan]; - if (channel->channel_type == CHANNEL_TYPE_DRUM) - banknum = DRUM_INST_BANK; - else - fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); - - /* I think this is a hack for MIDI files that do bank changes in GM mode. - * Proper way to handle this would probably be to ignore bank changes when in - * GM mode. - JG - * This is now possible by setting synth.midi-bank-select=gm, but let the hack - * stay for the time being. - DH - */ - if (prognum != FLUID_UNSET_PROGRAM) - { - subst_bank = banknum; - subst_prog = prognum; - - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - - /* Fallback to another preset if not found */ - if (!preset) { - /* Percussion: Fallback to preset 0 in percussion bank */ - if (subst_bank == DRUM_INST_BANK) { - subst_prog = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - } - /* Melodic instrument */ - else { - /* Fallback first to bank 0:prognum */ - subst_bank = 0; +fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED; + + fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + banknum = DRUM_INST_BANK; + } + else + { + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); + } + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + } + + /* I think this is a hack for MIDI files that do bank changes in GM mode. + * Proper way to handle this would probably be to ignore bank changes when in + * GM mode. - JG + * This is now possible by setting synth.midi-bank-select=gm, but let the hack + * stay for the time being. - DH + */ + if(prognum != FLUID_UNSET_PROGRAM) + { + subst_bank = banknum; + subst_prog = prognum; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - /* Fallback to first preset in bank 0 (usually piano...) */ - if (!preset) + /* Fallback to another preset if not found */ + if(!preset) { - subst_prog = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + /* Percussion: Fallback to preset 0 in percussion bank */ + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + subst_prog = 0; + subst_bank = DRUM_INST_BANK; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + /* Melodic instrument */ + else + { + /* Fallback first to bank 0:prognum */ + subst_bank = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to first preset in bank 0 (usually piano...) */ + if(!preset) + { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + } + + if(preset) + { + FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", + chan, banknum, prognum, subst_bank, subst_prog); + } + else + { + FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum); + } } - } - - if (preset) - FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", - chan, banknum, prognum, subst_bank, subst_prog); - else - FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", - chan, banknum, prognum); } - } - /* Assign the SoundFont ID and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, - -1, prognum); - result = fluid_synth_set_preset (synth, chan, preset); - FLUID_API_RETURN(result); + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, preset ? fluid_sfont_get_id(preset->sfont) : 0, + -1, prognum); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); } /** @@ -2008,16 +2775,28 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param bank MIDI bank number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_program_change(). If you still want + * instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having set up the bank configuration. + * */ int -fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) +fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank) { - fluid_return_val_if_fail (bank <= 16383, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); - FLUID_API_RETURN(FLUID_OK); + int result; + fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -2025,16 +2804,25 @@ fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_id ID of a loaded SoundFont - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change(). + * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having selected the soundfont. */ int -fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) +fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id) { - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + int result; + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - FLUID_API_RETURN(FLUID_OK); + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); } /** @@ -2044,18 +2832,15 @@ fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.1 * - * Note: Channel retains its SoundFont ID and bank numbers, while the program + * @note Channel retains its SoundFont ID and bank numbers, while the program * number is set to an "unset" state. MIDI program changes may re-assign a * preset if one matches. */ int -fluid_synth_unset_program (fluid_synth_t *synth, int chan) +fluid_synth_unset_program(fluid_synth_t *synth, int chan) { - int result; - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM); - FLUID_API_RETURN(result); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM)); } /** @@ -2065,27 +2850,35 @@ fluid_synth_unset_program (fluid_synth_t *synth, int chan) * @param sfont_id Location to store SoundFont ID * @param bank_num Location to store MIDI bank number * @param preset_num Location to store MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, - unsigned int* bank_num, unsigned int* preset_num) +fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num) { - fluid_channel_t* channel; + int result; + fluid_channel_t *channel; + + fluid_return_val_if_fail(sfont_id != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num != NULL, FLUID_FAILED); + fluid_return_val_if_fail(preset_num != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED); - fluid_return_val_if_fail (bank_num != NULL, FLUID_FAILED); - fluid_return_val_if_fail (preset_num != NULL, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - channel = synth->channel[chan]; - fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, - (int *)preset_num); + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + + /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ + if(*preset_num == FLUID_UNSET_PROGRAM) + { + *preset_num = 0; + } - /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ - if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; + result = FLUID_OK; - FLUID_API_RETURN(FLUID_OK); + FLUID_API_RETURN(result); } /** @@ -2095,34 +2888,40 @@ fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, * @param sfont_id ID of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, - unsigned int bank_num, unsigned int preset_num) +fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num) { - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - int result; - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); - channel = synth->channel[chan]; + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - /* ++ Allocate preset */ - preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); - 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); - FLUID_API_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); + FLUID_API_RETURN(FLUID_FAILED); + } - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); - - FLUID_API_RETURN(result); + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); } /** @@ -2132,37 +2931,42 @@ fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id * @param sfont_name Name of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int -fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, - const char *sfont_name, unsigned int bank_num, - unsigned int preset_num) -{ - fluid_preset_t* preset = NULL; - fluid_channel_t* channel; - int result; - fluid_return_val_if_fail (sfont_name != NULL, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - channel = synth->channel[chan]; - - /* ++ Allocate preset */ - preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, 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 %s", - bank_num, preset_num, sfont_name); - FLUID_API_RETURN(FLUID_FAILED); - } +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(sfont_name != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), - bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); - FLUID_API_RETURN(result); + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset_by_sfont_name(synth, sfont_name, 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 %s", + bank_num, preset_num, sfont_name); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, fluid_sfont_get_id(preset->sfont), + bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); } /* @@ -2171,63 +2975,66 @@ fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, * unloaded or reloaded. */ static void -fluid_synth_update_presets(fluid_synth_t* synth) +fluid_synth_update_presets(fluid_synth_t *synth) { - fluid_channel_t *channel; - fluid_preset_t *preset; - int sfont, bank, prog; - int chan; + fluid_channel_t *channel; + fluid_preset_t *preset; + int sfont, bank, prog; + int chan; - for (chan = 0; chan < synth->midi_channels; chan++) { - channel = synth->channel[chan]; - fluid_channel_get_sfont_bank_prog (channel, &sfont, &bank, &prog); - preset = fluid_synth_get_preset (synth, sfont, bank, prog); - fluid_synth_set_preset (synth, chan, preset); - } + for(chan = 0; chan < synth->midi_channels; chan++) + { + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, &sfont, &bank, &prog); + preset = fluid_synth_get_preset(synth, sfont, bank, prog); + fluid_synth_set_preset(synth, chan, preset); + } } -/* Handler for synth.gain setting. */ -static int -fluid_synth_update_sample_rate(fluid_synth_t* synth, char* name, double value) +/* Handler for synth.sample-rate setting. */ +static void +fluid_synth_handle_sample_rate(void *data, const char *name, double value) { - fluid_synth_set_sample_rate(synth, (float) value); - return 0; + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_sample_rate(synth, (float) value); } + /** - * Set sample rate of the synth. - * NOTE: This function is currently experimental and should only be - * used when no voices or notes are active, and before any rendering calls. + * Set sample rate of the synth. + * @note This function should only be used when no voices or notes are active. * @param synth FluidSynth instance * @param sample_rate New sample rate (Hz) * @since 1.1.2 */ -void -fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate) +void +fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate) { - int i; - fluid_return_if_fail (synth != NULL); - fluid_synth_api_enter(synth); - fluid_clip (sample_rate, 8000.0f, 96000.0f); - synth->sample_rate = sample_rate; - - fluid_settings_getint(synth->settings, "synth.min-note-length", &i); - synth->min_note_length_ticks = (unsigned int) (i*synth->sample_rate/1000.0f); - - for (i=0; i < synth->polyphony; i++) - fluid_voice_set_output_rate(synth->voice[i], sample_rate); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, - 0, sample_rate); - fluid_synth_api_exit(synth); + int i; + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + fluid_clip(sample_rate, 8000.0f, 96000.0f); + synth->sample_rate = sample_rate; + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_set_output_rate(synth->voice[i], sample_rate); + } + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, + 0, sample_rate); + fluid_synth_api_exit(synth); } /* Handler for synth.gain setting. */ -static int -fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) +static void +fluid_synth_handle_gain(void *data, const char *name, double value) { - fluid_synth_set_gain(synth, (float) value); - return 0; + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_gain(synth, (float) value); } /** @@ -2236,33 +3043,37 @@ fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) * @param gain Gain value (function clamps value to the range 0.0 to 10.0) */ void -fluid_synth_set_gain(fluid_synth_t* synth, float gain) +fluid_synth_set_gain(fluid_synth_t *synth, float gain) { - fluid_return_if_fail (synth != NULL); - fluid_synth_api_enter(synth); - - fluid_clip (gain, 0.0f, 10.0f); + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_clip(gain, 0.0f, 10.0f); - synth->gain = gain; - fluid_synth_update_gain_LOCAL (synth); - fluid_synth_api_exit(synth); + synth->gain = gain; + fluid_synth_update_gain_LOCAL(synth); + fluid_synth_api_exit(synth); } /* Called by synthesis thread to update the gain in all voices */ static void -fluid_synth_update_gain_LOCAL(fluid_synth_t* synth) +fluid_synth_update_gain_LOCAL(fluid_synth_t *synth) { - fluid_voice_t *voice; - float gain; - int i; + fluid_voice_t *voice; + float gain; + int i; - gain = synth->gain; + gain = synth->gain; - for (i = 0; i < synth->polyphony; i++) - { - voice = synth->voice[i]; - if (_PLAYING (voice)) fluid_voice_set_gain (voice, gain); - } + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_set_gain(voice, gain); + } + } } /** @@ -2271,119 +3082,144 @@ fluid_synth_update_gain_LOCAL(fluid_synth_t* synth) * @return Synth gain value (0.0 to 10.0) */ float -fluid_synth_get_gain(fluid_synth_t* synth) +fluid_synth_get_gain(fluid_synth_t *synth) { - float result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + float result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = synth->gain; - FLUID_API_RETURN(result); + result = synth->gain; + FLUID_API_RETURN(result); } /* * Handler for synth.polyphony setting. */ -static int -fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) +static void +fluid_synth_handle_polyphony(void *data, const char *name, int value) { - fluid_synth_set_polyphony(synth, value); - return 0; + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_polyphony(synth, value); } /** * Set synthesizer polyphony (max number of voices). * @param synth FluidSynth instance * @param polyphony Polyphony to assign - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.0.6 */ int -fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) +fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony) { - int result; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (polyphony >= 1 && polyphony <= 65535, FLUID_FAILED); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(polyphony >= 1 && polyphony <= 65535, FLUID_FAILED); + fluid_synth_api_enter(synth); - result = fluid_synth_update_polyphony_LOCAL(synth, polyphony); + result = fluid_synth_update_polyphony_LOCAL(synth, polyphony); - FLUID_API_RETURN(result); + FLUID_API_RETURN(result); } /* Called by synthesis thread to update the polyphony value */ static int -fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony) -{ - fluid_voice_t *voice; - int i; - - if (new_polyphony > synth->nvoice) { - /* Create more voices */ - fluid_voice_t** new_voices = FLUID_REALLOC(synth->voice, - sizeof(fluid_voice_t*) * new_polyphony); - if (new_voices == NULL) - return FLUID_FAILED; - synth->voice = new_voices; - for (i = synth->nvoice; i < new_polyphony; i++) { - synth->voice[i] = new_fluid_voice(synth->sample_rate); - if (synth->voice[i] == NULL) - return FLUID_FAILED; - } - synth->nvoice = new_polyphony; - } - - synth->polyphony = new_polyphony; - /* turn off any voices above the new limit */ - for (i = synth->polyphony; i < synth->nvoice; i++) - { - voice = synth->voice[i]; - if (_PLAYING (voice)) fluid_voice_off (voice); - } - - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, - synth->polyphony, 0.0f); - - return FLUID_OK; -} - -/** - * Get current synthesizer polyphony (max number of voices). - * @param synth FluidSynth instance - * @return Synth polyphony value. - * @since 1.0.6 - */ -int -fluid_synth_get_polyphony(fluid_synth_t* synth) +fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony) { - int result; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - result = synth->polyphony; - FLUID_API_RETURN(result); -} + fluid_voice_t *voice; + int i; -/** - * Get current number of active voices. - * @param synth FluidSynth instance - * @return Number of currently active voices. - * @since 1.1.0 - * - * Note: To generate accurate continuous statistics of the voice count, caller - * should ensure this function is called synchronously with the audio synthesis - * process. This can be done in the new_fluid_audio_driver2() audio callback - * function for example. - */ -int -fluid_synth_get_active_voice_count(fluid_synth_t* synth) + if(new_polyphony > synth->nvoice) + { + /* Create more voices */ + fluid_voice_t **new_voices = FLUID_REALLOC(synth->voice, + sizeof(fluid_voice_t *) * new_polyphony); + + if(new_voices == NULL) + { + return FLUID_FAILED; + } + + synth->voice = new_voices; + + for(i = synth->nvoice; i < new_polyphony; i++) + { + synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + return FLUID_FAILED; + } + + fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags); + } + + synth->nvoice = new_polyphony; + } + + synth->polyphony = new_polyphony; + + /* turn off any voices above the new limit */ + for(i = synth->polyphony; i < synth->nvoice; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + } + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + + return FLUID_OK; +} + +/** + * Get current synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @return Synth polyphony value. + * @since 1.0.6 + */ +int +fluid_synth_get_polyphony(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = synth->polyphony; + FLUID_API_RETURN(result); +} + +/** + * @brief Get current number of active voices. + * + * I.e. the no. of voices that have been + * started and have not yet finished. Unless called from synthesis context, + * this number does not necessarily have to be equal to the number of voices + * currently processed by the DSP loop, see below. + * @param synth FluidSynth instance + * @return Number of currently active voices. + * @since 1.1.0 + * + * @note To generate accurate continuous statistics of the voice count, caller + * should ensure this function is called synchronously with the audio synthesis + * process. This can be done in the new_fluid_audio_driver2() audio callback + * function for example. Otherwise every call to this function may return different + * voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by + * e.g. an audio driver or the applications audio rendering thread. + */ +int +fluid_synth_get_active_voice_count(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - result = synth->active_voice_count; - FLUID_API_RETURN(result); + result = synth->active_voice_count; + FLUID_API_RETURN(result); } /** @@ -2394,171 +3230,485 @@ fluid_synth_get_active_voice_count(fluid_synth_t* synth) * Audio is synthesized this number of frames at a time. Defaults to 64 frames. */ int -fluid_synth_get_internal_bufsize(fluid_synth_t* synth) +fluid_synth_get_internal_bufsize(fluid_synth_t *synth) { - return FLUID_BUFSIZE; + return FLUID_BUFSIZE; } /** - * Resend a bank select and a program change for every channel. + * Resend a bank select and a program change for every channel and assign corresponding instruments. * @param synth FluidSynth instance - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * This function is called mainly after a SoundFont has been loaded, * unloaded or reloaded. */ int -fluid_synth_program_reset(fluid_synth_t* synth) +fluid_synth_program_reset(fluid_synth_t *synth) { - int i, prog; - fluid_synth_api_enter(synth); - /* try to set the correct presets */ - for (i = 0; i < synth->midi_channels; i++){ - fluid_channel_get_sfont_bank_prog (synth->channel[i], NULL, NULL, &prog); - fluid_synth_program_change(synth, i, prog); - } - FLUID_API_RETURN(FLUID_OK); + int i, prog; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* try to set the correct presets */ + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_get_sfont_bank_prog(synth->channel[i], NULL, NULL, &prog); + fluid_synth_program_change(synth, i, prog); + } + + FLUID_API_RETURN(FLUID_OK); } /** - * Synthesize a block of floating point audio to audio buffers. + * Synthesize a block of floating point audio to separate audio buffers (multichannel rendering). First effect channel used by reverb, second for chorus. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize - * @param left Array of floats to store left channel of audio (len in size) - * @param right Array of floats to store right channel of audio (len in size) - * @param fx_left Not currently used - * @param fx_right Not currently used - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size) + * @param right Array of float buffers to store right channel of planar audio (size: dito) + * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size) + * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Should only be called from synthesis thread. * - * NOTE: Should only be called from synthesis thread. + * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more powerful and flexible fluid_synth_process(). + * + * Usage example: + * @code{.cpp} + const int FramesToRender = 64; + int channels; + // retrieve number of stereo audio channels + fluid_settings_getint(settings, "synth.audio-channels", &channels); + + // we need twice as many (mono-)buffers + channels *= 2; + + // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM + float** mix_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + mix_buf[i] = new float[FramesToRender]; + } + + // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan)) + fluid_settings_getint(settings, "synth.effects-channels", &channels); + channels *= 2; + + float** fx_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + fx_buf[i] = new float[FramesToRender]; + } + + float** mix_buf_l = mix_buf; + float** mix_buf_r = &mix_buf[channels/2]; + + float** fx_buf_l = fx_buf; + float** fx_buf_r = &fx_buf[channels/2]; + + fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r) + * @endcode */ 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; - fluid_real_t** right_in; - double time = fluid_utime(); - int i, num, available, count; +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, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + double time = fluid_utime(); + int i, num, available, count; #ifdef WITH_FLOAT - int bytes; + int bytes; #endif - float cpu_load; + float cpu_load; - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_enter(synth); - - /* First, take what's still available in the buffer */ - count = 0; - num = synth->cur; - if (synth->cur < FLUID_BUFSIZE) { - available = FLUID_BUFSIZE - synth->cur; - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(left != NULL, FLUID_FAILED); + fluid_return_val_if_fail(right != NULL, FLUID_FAILED); + + /* First, take what's still available in the buffer */ + count = 0; + num = synth->cur; + + if(synth->cur < FLUID_BUFSIZE) + { + available = FLUID_BUFSIZE - synth->cur; + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); - num = (available > len)? len : available; + num = (available > len) ? len : available; #ifdef WITH_FLOAT - bytes = num * sizeof(float); + bytes = num * sizeof(float); #endif - for (i = 0; i < synth->audio_channels; i++) { + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { #ifdef WITH_FLOAT - FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes); - FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes); + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + #else //WITH_FLOAT - int j; - for (j = 0; j < num; j++) { - left[i][j] = (float) left_in[i][j + synth->cur]; - right[i][j] = (float) right_in[i][j + synth->cur]; - } + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + #endif //WITH_FLOAT + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ } - 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_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0); - fluid_synth_render_blocks(synth, 1); // TODO: - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + /* Then, run one_block() and copy till we have 'len' samples */ + while(count < len) + { + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0); + fluid_synth_render_blocks(synth, 1); // TODO: + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); - num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE; + num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE; #ifdef WITH_FLOAT - bytes = num * sizeof(float); + bytes = num * sizeof(float); #endif - for (i = 0; i < synth->audio_channels; i++) { + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { #ifdef WITH_FLOAT - FLUID_MEMCPY(left[i] + count, left_in[i], bytes); - FLUID_MEMCPY(right[i] + count, right_in[i], bytes); + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + #else //WITH_FLOAT - int j; - for (j = 0; j < num; j++) { - left[i][j + count] = (float) left_in[i][j]; - right[i][j + count] = (float) right_in[i][j]; - } + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + #endif //WITH_FLOAT + } + + count += num; } - count += num; - } + synth->cur = num; - synth->cur = num; + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); - time = fluid_utime() - time; - cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); - fluid_atomic_float_set (&synth->cpu_load, cpu_load); + return FLUID_OK; +} - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_exit(synth); - - return FLUID_OK; +/** + * mixes the samples of \p in to \p out + * + * @param out the output sample buffer to mix to + * @param ooff sample offset in \p out + * @param in the rvoice_mixer input sample buffer to mix from + * @param ioff sample offset in \p in + * @param buf_idx the sample buffer index of \p in to mix from + * @param num number of samples to mix + */ +static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out, + int ooff, + const fluid_real_t *FLUID_RESTRICT in, + int ioff, + int buf_idx, + int num) +{ + if(out != NULL) + { + int j; + for(j = 0; j < num; j++) + { + out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; + } + } } /** - * Synthesize floating point audio to audio buffers. + * @brief Synthesize floating point audio to stereo audio channels (implements the default interface #fluid_audio_func_t). + * + * Synthesize and mix audio to a given number of planar audio buffers. + * Therefore pass nout = N*2 float buffers to \p out in order to render + * the synthesized audio to \p N stereo channels. Each float buffer must be + * able to hold \p len elements. + * + * \p out contains an array of planar buffers for normal, dry, stereo + * audio (alternating left and right). Like: +@code{.cpp} +out[0] = left_buffer_audio_channel_0 +out[1] = right_buffer_audio_channel_0 +out[2] = left_buffer_audio_channel_1 +out[3] = right_buffer_audio_channel_1 +... +out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i +out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i +@endcode + * + * for zero-based channel index \p i. + * The buffer layout of \p fx used for storing effects + * like reverb and chorus looks similar: +@code{.cpp} +fx[0] = left_buffer_channel_of_reverb_unit_0 +fx[1] = right_buffer_channel_of_reverb_unit_0 +fx[2] = left_buffer_channel_of_chorus_unit_0 +fx[3] = right_buffer_channel_of_chorus_unit_0 +fx[4] = left_buffer_channel_of_reverb_unit_1 +fx[5] = right_buffer_channel_of_reverb_unit_1 +fx[6] = left_buffer_channel_of_chorus_unit_1 +fx[7] = right_buffer_channel_of_chorus_unit_1 +fx[8] = left_buffer_channel_of_reverb_unit_2 +... +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k +@endcode + * where 0 <= k < fluid_synth_count_effects_groups() is a zero-based index denoting the effects unit and + * 0 <= j < fluid_synth_count_effects_channels() is a zero-based index denoting the effect channel within + * unit \p k. + * + * Any voice playing is assigned to audio channels based on the MIDI channel its playing on. Let \p chan be the + * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is + * going to be rendered to use: + * + * i = chan % fluid_synth_count_audio_groups() + * + * k = chan % fluid_synth_count_effects_groups() + * * @param synth FluidSynth instance - * @param len Count of audio frames to synthesize - * @param nin Ignored - * @param in Ignored - * @param nout Count of arrays in 'out' - * @param out Array of arrays to store audio to - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. + * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) + * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * @param fx Array of buffers to store effects audio to. Buffers may +alias with buffers of \c out. NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * @param nout Count of arrays in \c out. Must be a multiple of 2 +(because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). + * @param out Array of buffers to store (dry) audio to. Buffers may +alias with buffers of \c fx. NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. * - * This function implements the default interface defined in fluidsynth/audio.h. - * NOTE: Should only be called from synthesis thread. - */ -/* - * FIXME: Currently if nout != 2 memory allocation will occur! + * @parblock + * @note The owner of the sample buffers must zero them out before calling this + * function, because any synthesized audio is mixed (i.e. added) to the buffers. + * E.g. if fluid_synth_process() is called from a custom audio driver process function + * (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers. + * @endparblock + * + * @parblock + * @note No matter how many buffers you pass in, fluid_synth_process() + * will always render all audio channels to the + * buffers in \c out and all effects channels to the + * buffers in \c fx, provided that nout > 0 and nfx > 0 respectively. If + * nout/2 < fluid_synth_count_audio_channels() it will wrap around. Same + * is true for effects audio if nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * See usage examples below. + * @endparblock + * + * @parblock + * @note Should only be called from synthesis thread. + * @endparblock */ int -fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, - int nout, float** out) -{ - if (nout==2) { - return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1); - } - else { - float **left, **right; - int i; - left = FLUID_ARRAY(float*, nout/2); - right = FLUID_ARRAY(float*, nout/2); - if ((left == NULL) || (right == NULL)) { - FLUID_LOG(FLUID_ERR, "Out of memory."); - FLUID_FREE(left); - FLUID_FREE(right); - return FLUID_FAILED; - } - for(i=0; ieffects_channels; + nfxunits = synth->effects_groups; + naudchan = synth->audio_channels; + + fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED); + fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED); + + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE); + + + /* First, take what's still available in the buffer */ + count = 0; + num = synth->cur; + + if(synth->cur < FLUID_BUFSIZE) + { + int available = FLUID_BUFSIZE - synth->cur; + num = (available > len) ? len : available; + + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num); + + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num); + } + } + + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); + + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num); + } + } + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, render blocks and copy till we have 'len' samples */ + while(count < len) + { + int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + int blockcount = fluid_synth_render_blocks(synth, blocksleft); + + num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; + + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num); + + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num); + } + } + + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); + + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); + } + } + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + return FLUID_OK; - } } /** @@ -2571,59 +3721,63 @@ fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, * @param rout Array of floats to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, * lincr = 2, rincr = 2). * - * NOTE: Should only be called from synthesis thread. + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. */ 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; - fluid_real_t** right_in; - double time = fluid_utime(); - float cpu_load; - - fluid_profile_ref_var (prof_ref); - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_enter(synth); - - fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); - l = synth->cur; - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); - - for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) { - /* fill up the buffers as needed */ - if (l >= synth->curmax) { - int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE; - synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); +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; + fluid_real_t *right_in; + double time = fluid_utime(); + float cpu_load; + + fluid_profile_ref_var(prof_ref); + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); + + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); + l = synth->cur; + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); - l = 0; - } + for(i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) + { + /* fill up the buffers as needed */ + if(l >= synth->curmax) + { + int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + + l = 0; + } - left_out[j] = (float) left_in[0][l]; - right_out[k] = (float) right_in[0][l]; - } + left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; + right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; + } - synth->cur = l; + synth->cur = l; - time = fluid_utime() - time; - cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); - fluid_atomic_float_set (&synth->cpu_load, cpu_load); + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_exit(synth); - fluid_profile(FLUID_PROF_WRITE, prof_ref); - - return FLUID_OK; + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return FLUID_OK; } #define DITHER_SIZE 48000 @@ -2632,31 +3786,39 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; /* Init dither table */ -static void +static void init_dither(void) { - float d, dp; - int c, i; + float d, dp; + int c, i; - for (c = 0; c < DITHER_CHANNELS; c++) { - dp = 0; - for (i = 0; i < DITHER_SIZE-1; i++) { - d = rand() / (float)RAND_MAX - 0.5f; - rand_table[c][i] = d - dp; - dp = d; + for(c = 0; c < DITHER_CHANNELS; c++) + { + dp = 0; + + for(i = 0; i < DITHER_SIZE - 1; i++) + { + d = rand() / (float)RAND_MAX - 0.5f; + rand_table[c][i] = d - dp; + dp = d; + } + + rand_table[c][DITHER_SIZE - 1] = 0 - dp; } - rand_table[c][DITHER_SIZE-1] = 0 - dp; - } } /* A portable replacement for roundf(), seems it may actually be faster too! */ -static inline int -roundi (float x) +static FLUID_INLINE int +roundi(float x) { - if (x >= 0.0f) - return (int)(x+0.5f); - else - return (int)(x-0.5f); + if(x >= 0.0f) + { + return (int)(x + 0.5f); + } + else + { + return (int)(x - 0.5f); + } } /** @@ -2669,84 +3831,98 @@ roundi (float x) * @param rout Array of 16 bit words to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' - * @return FLUID_OK on success, FLUID_FAIL otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, * lincr = 2, rincr = 2). * - * NOTE: Should only be called from synthesis thread. - * NOTE: Dithering is performed when converting from internal floating point to + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to * 16 bit audio. */ 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; - fluid_real_t** right_in; - fluid_real_t left_sample; - fluid_real_t right_sample; - double time = fluid_utime(); - int di; - //double prof_ref_on_block; - float cpu_load; - fluid_profile_ref_var (prof_ref); - - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_enter(synth); - - fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); +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; + fluid_real_t *right_in; + fluid_real_t left_sample; + fluid_real_t right_sample; + double time = fluid_utime(); + int di; + float cpu_load; + + fluid_profile_ref_var(prof_ref); + + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); - cur = synth->cur; - di = synth->dither_index; + cur = synth->cur; + di = synth->dither_index; - for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) { + for(i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) + { - /* fill up the buffers as needed */ - if (cur >= synth->curmax) { - int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE; - //prof_ref_on_block = fluid_profile_ref(); - synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); - fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); - cur = 0; + /* fill up the buffers as needed */ + if(cur >= synth->curmax) + { + int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } - //fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block); - } + left_sample = roundi(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]); + right_sample = roundi(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); - left_sample = roundi (left_in[0][cur] * 32766.0f + rand_table[0][di]); - right_sample = roundi (right_in[0][cur] * 32766.0f + rand_table[1][di]); + di++; - di++; - if (di >= DITHER_SIZE) di = 0; + if(di >= DITHER_SIZE) + { + di = 0; + } + + /* digital clipping */ + if(left_sample > 32767.0f) + { + left_sample = 32767.0f; + } + + if(left_sample < -32768.0f) + { + left_sample = -32768.0f; + } - /* digital clipping */ - if (left_sample > 32767.0f) left_sample = 32767.0f; - if (left_sample < -32768.0f) left_sample = -32768.0f; - if (right_sample > 32767.0f) right_sample = 32767.0f; - if (right_sample < -32768.0f) right_sample = -32768.0f; + if(right_sample > 32767.0f) + { + right_sample = 32767.0f; + } - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; - } + if(right_sample < -32768.0f) + { + right_sample = -32768.0f; + } - synth->cur = cur; - synth->dither_index = di; /* keep dither buffer continous */ + left_out[j] = (signed short) left_sample; + right_out[k] = (signed short) right_sample; + } - fluid_profile(FLUID_PROF_WRITE, prof_ref); + synth->cur = cur; + synth->dither_index = di; /* keep dither buffer continous */ - time = fluid_utime() - time; - cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); - fluid_atomic_float_set (&synth->cpu_load, cpu_load); + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); - if (!synth->eventhandler->is_threadsafe) - fluid_synth_api_exit(synth); - - return 0; + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return 0; } /** @@ -2764,63 +3940,87 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len, * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' * - * NOTE: Currently private to libfluidsynth. + * @note Currently private to libfluidsynth. */ void -fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr) -{ - int i, j, k; - signed short* left_out = (signed short*) lout; - signed short* right_out = (signed short*) rout; - fluid_real_t left_sample; - fluid_real_t right_sample; - int di = *dither_index; - fluid_profile_ref_var (prof_ref); +fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + int i, j, k; + signed short *left_out = (signed short *) lout; + signed short *right_out = (signed short *) rout; + fluid_real_t left_sample; + fluid_real_t right_sample; + int di = *dither_index; + fluid_profile_ref_var(prof_ref); + + for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) + { - for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) { + left_sample = roundi(lin[i] * 32766.0f + rand_table[0][di]); + right_sample = roundi(rin[i] * 32766.0f + rand_table[1][di]); - left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]); - right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]); + di++; - di++; - if (di >= DITHER_SIZE) di = 0; + if(di >= DITHER_SIZE) + { + di = 0; + } + + /* digital clipping */ + if(left_sample > 32767.0f) + { + left_sample = 32767.0f; + } + + if(left_sample < -32768.0f) + { + left_sample = -32768.0f; + } - /* digital clipping */ - if (left_sample > 32767.0f) left_sample = 32767.0f; - if (left_sample < -32768.0f) left_sample = -32768.0f; - if (right_sample > 32767.0f) right_sample = 32767.0f; - if (right_sample < -32768.0f) right_sample = -32768.0f; + if(right_sample > 32767.0f) + { + right_sample = 32767.0f; + } + + if(right_sample < -32768.0f) + { + right_sample = -32768.0f; + } - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; - } + left_out[j] = (signed short) left_sample; + right_out[k] = (signed short) right_sample; + } - *dither_index = di; /* keep dither buffer continous */ + *dither_index = di; /* keep dither buffer continous */ - fluid_profile(FLUID_PROF_WRITE, prof_ref); + fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len); } static void -fluid_synth_check_finished_voices(fluid_synth_t* synth) -{ - int j; - fluid_rvoice_t* fv; - - while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) { - for (j=0; j < synth->polyphony; j++) { - if (synth->voice[j]->rvoice == fv) { - fluid_voice_unlock_rvoice(synth->voice[j]); - fluid_voice_off(synth->voice[j]); - break; - } - else if (synth->voice[j]->overflow_rvoice == fv) { - fluid_voice_overflow_rvoice_finished(synth->voice[j]); - break; - } +fluid_synth_check_finished_voices(fluid_synth_t *synth) +{ + int j; + fluid_rvoice_t *fv; + + while(NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) + { + for(j = 0; j < synth->polyphony; j++) + { + if(synth->voice[j]->rvoice == fv) + { + fluid_voice_unlock_rvoice(synth->voice[j]); + fluid_voice_stop(synth->voice[j]); + break; + } + else if(synth->voice[j]->overflow_rvoice == fv) + { + fluid_voice_overflow_rvoice_finished(synth->voice[j]); + break; + } + } } - } } /** @@ -2828,9 +4028,9 @@ fluid_synth_check_finished_voices(fluid_synth_t* synth) * Make sure no (other) rendering is running in parallel when * you call this function! */ -void fluid_synth_process_event_queue(fluid_synth_t* synth) +void fluid_synth_process_event_queue(fluid_synth_t *synth) { - fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); } @@ -2840,102 +4040,206 @@ void fluid_synth_process_event_queue(fluid_synth_t* synth) * @return number of blocks rendered. Might (often) return less than requested */ static int -fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount) +fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount) { - int i; - fluid_profile_ref_var (prof_ref); + int i, maxblocks; + fluid_profile_ref_var(prof_ref); - /* Assign ID of synthesis thread */ + /* Assign ID of synthesis thread */ // synth->synth_thread_id = fluid_thread_get_id (); - fluid_check_fpe("??? Just starting up ???"); - - fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); - - for (i=0; i < blockcount; i++) { - fluid_sample_timer_process(synth); - fluid_synth_add_ticks(synth, FLUID_BUFSIZE); - if (fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) { - // Something has happened, we can't process more - blockcount = i+1; - break; - } - } - - fluid_check_fpe("fluid_sample_timer_process"); - - blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount); - - /* 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_profile(FLUID_PROF_ONE_BLOCK, prof_ref); - return blockcount; -} + fluid_check_fpe("??? Just starting up ???"); + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); -static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name, - fluid_real_t value) -{ - double d; - fluid_synth_api_enter(synth); - - fluid_settings_getnum(synth->settings, "synth.overflow.percussion", &d); - synth->overflow.percussion = d; - fluid_settings_getnum(synth->settings, "synth.overflow.released", &d); - synth->overflow.released = d; - fluid_settings_getnum(synth->settings, "synth.overflow.sustained", &d); - synth->overflow.sustained = d; - fluid_settings_getnum(synth->settings, "synth.overflow.volume", &d); - synth->overflow.volume = d; - fluid_settings_getnum(synth->settings, "synth.overflow.age", &d); - synth->overflow.age = d; - - FLUID_API_RETURN(0); -} + /* do not render more blocks than we can store internally */ + maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer); + if(blockcount > maxblocks) + { + blockcount = maxblocks; + } -/* Selects a voice for killing. */ -static fluid_voice_t* -fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) -{ - int i; - fluid_real_t best_prio = OVERFLOW_PRIO_CANNOT_KILL-1; - fluid_real_t this_voice_prio; - fluid_voice_t* voice; - int best_voice_index=-1; - unsigned int ticks = fluid_synth_get_ticks(synth); - - for (i = 0; i < synth->polyphony; i++) { - - voice = synth->voice[i]; + for(i = 0; i < blockcount; i++) + { + fluid_sample_timer_process(synth); + fluid_synth_add_ticks(synth, FLUID_BUFSIZE); - /* safeguard against an available voice. */ - if (_AVAILABLE(voice)) { - return voice; + /* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all() + * (should only happen with parallel render) stop processing and go for rendering + */ + if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) + { + // Something has happened, we can't process more + blockcount = i + 1; + break; + } } - this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow, - ticks); - /* 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; - } - } + fluid_check_fpe("fluid_sample_timer_process"); - if (best_voice_index < 0) { - return NULL; - } + blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount); - voice = synth->voice[best_voice_index]; - FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ", - voice->id, best_voice_index, voice->chan, voice->key); - fluid_voice_off(voice); + /* Testcase, that provokes a denormal floating point error */ +#if 0 + { + float num = 1; - return voice; + while(num != 0) + { + num *= 0.5; + }; + }; +#endif + fluid_check_fpe("??? Remainder of synth_one_block ???"); + fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + blockcount * FLUID_BUFSIZE); + return blockcount; +} + +/* + * Handler for synth.reverb.* and synth.chorus.* double settings. + */ +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0) + { + fluid_synth_set_reverb_roomsize(synth, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0) + { + fluid_synth_set_reverb_damp(synth, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.width") == 0) + { + fluid_synth_set_reverb_width(synth, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.level") == 0) + { + fluid_synth_set_reverb_level(synth, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0) + { + fluid_synth_set_chorus_depth(synth, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0) + { + fluid_synth_set_chorus_speed(synth, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.level") == 0) + { + fluid_synth_set_chorus_level(synth, value); + } +} + +/* + * Handler for synth.reverb.* and synth.chorus.* integer settings. + */ +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.active") == 0) + { + fluid_synth_set_reverb_on(synth, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.active") == 0) + { + fluid_synth_set_chorus_on(synth, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0) + { + fluid_synth_set_chorus_nr(synth, value); + } +} + +/* + * Handler for synth.overflow.* settings. + */ +static void fluid_synth_handle_overflow(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + + if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0) + { + synth->overflow.percussion = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.released") == 0) + { + synth->overflow.released = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0) + { + synth->overflow.sustained = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0) + { + synth->overflow.volume = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.age") == 0) + { + synth->overflow.age = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.important") == 0) + { + synth->overflow.important = value; + } + + fluid_synth_api_exit(synth); +} + +/* Selects a voice for killing. */ +static fluid_voice_t * +fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth) +{ + int i; + float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1; + float this_voice_prio; + fluid_voice_t *voice; + int best_voice_index = -1; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(i = 0; i < synth->polyphony; i++) + { + + voice = synth->voice[i]; + + /* safeguard against an available voice. */ + if(_AVAILABLE(voice)) + { + return voice; + } + + this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow, + ticks); + + /* 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_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ", + fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice)); + fluid_voice_off(voice); + + return voice; } @@ -2952,108 +4256,150 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) * The returned voice comes with default modulators and generators. * A single noteon event may create any number of voices, when the preset is layered. * - * NOTE: Should only be called from within synthesis thread, which includes + * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. */ -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; - unsigned int ticks; - - fluid_return_val_if_fail (sample != NULL, NULL); - FLUID_API_ENTRY_CHAN(NULL); - - /* 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) { - FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice"); - voice = fluid_synth_free_voice_by_kill_LOCAL(synth); - } - - if (voice == NULL) { - FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); - FLUID_API_RETURN(NULL); - } - ticks = fluid_synth_get_ticks(synth); - - 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) 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, ticks, synth->gain) != FLUID_OK) { - FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); - FLUID_API_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 */ - - FLUID_API_RETURN(voice); +fluid_voice_t * +fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample, + int chan, int key, int vel) +{ + fluid_return_val_if_fail(sample != NULL, NULL); + FLUID_API_ENTRY_CHAN(NULL); + FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); + +} + +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range) +{ + int i, k; + fluid_voice_t *voice = NULL; + fluid_channel_t *channel = NULL; + unsigned int ticks; + + /* 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) + { + FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice"); + voice = fluid_synth_free_voice_by_kill_LOCAL(synth); + } + + if(voice == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); + return NULL; + } + + ticks = fluid_synth_get_ticks(synth); + + 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) ticks / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, + k); + } + + channel = synth->channel[chan]; + + if(fluid_voice_init(voice, sample, zone_range, channel, key, vel, + synth->storeid, ticks, synth->gain) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); + return NULL; + } + + /* add the default modulators to the synthesis process. */ + /* custom_breath2att_modulator is not a default modulator specified in SF + it is intended to replace default_vel2att_mod for this channel on demand using + API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel. + */ + { + int mono = fluid_channel_is_playing_mono(channel); + fluid_mod_t *default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if( + /* See if default_mod is the velocity_to_attenuation modulator */ + fluid_mod_test_identity(default_mod, &default_vel2att_mod) && + // See if a replacement by custom_breath2att_modulator has been demanded + // for this channel + ((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) || + (mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO))) + ) + { + // Replacement of default_vel2att modulator by custom_breath2att_modulator + fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT); + } + else + { + fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT); + } + + // Next default modulator to add to the voice + default_mod = default_mod->next; + } + } + + return voice; } /* Kill all voices on a given channel, which have the same exclusive class * generator as new_voice. */ static void -fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, - fluid_voice_t* new_voice) +fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice) { - int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS); - fluid_voice_t* existing_voice; - int i; + int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS); + int i; - /* Excl. class 0: No exclusive class */ - if (excl_class == 0) return; + /* Excl. class 0: No exclusive class */ + if(excl_class == 0) + { + return; + } - /* Kill all notes on the same channel with the same exclusive class */ - for (i = 0; i < synth->polyphony; i++) { - existing_voice = synth->voice[i]; + /* 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]; + int existing_excl_class = fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS); - /* If voice is playing, on the same channel, has same exclusive - * class and is not part of the same noteon event (voice group), then kill it */ + /* If voice is playing, on the same channel, has same exclusive + * class and is not part of the same noteon event (voice group), then kill it */ - if (_PLAYING(existing_voice) - && existing_voice->chan == new_voice->chan - && (int)_GEN (existing_voice, GEN_EXCLUSIVECLASS) == excl_class - && fluid_voice_get_id (existing_voice) != fluid_voice_get_id(new_voice)) - fluid_voice_kill_excl(existing_voice); - } + if(fluid_voice_is_playing(existing_voice) + && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice) + && existing_excl_class == excl_class + && fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice)) + { + fluid_voice_kill_excl(existing_voice); + } + } } /** @@ -3064,34 +4410,33 @@ fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, * This function is called by a SoundFont's preset in response to a noteon * event. Exclusive classes are processed here. * - * NOTE: Should only be called from within synthesis thread, which includes + * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. */ void -fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) +fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice) { - fluid_return_if_fail (synth != NULL); - fluid_return_if_fail (voice != NULL); + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(voice != NULL); // fluid_return_if_fail (fluid_synth_is_synth_thread (synth)); - fluid_synth_api_enter(synth); + fluid_synth_api_enter(synth); - /* 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_LOCAL(synth, voice); + /* 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_LOCAL(synth, voice); - fluid_voice_start(voice); /* Start the new voice */ - if (synth->eventhandler->is_threadsafe) + fluid_voice_start(voice); /* Start the new voice */ fluid_voice_lock_rvoice(voice); - fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice); - fluid_synth_api_exit(synth); + fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice); + fluid_synth_api_exit(synth); } /** - * Add a SoundFont loader interface. + * Add a SoundFont loader to the synth. This function takes ownership of \c loader + * and frees it automatically upon \c synth destruction. * @param synth FluidSynth instance - * @param loader Loader API structure, used directly and should remain allocated - * as long as the synth instance is used. + * @param loader Loader API structure * * SoundFont loaders are used to add custom instrument loading to FluidSynth. * The caller supplied functions for loading files, allocating presets, @@ -3099,20 +4444,22 @@ fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) * method even non SoundFont instruments can be synthesized, although limited * to the SoundFont synthesis model. * - * NOTE: Should only be called before any SoundFont files are loaded. + * @note Should only be called before any SoundFont files are loaded. */ void -fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) +fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader) { - gboolean sfont_already_loaded; + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(loader != NULL); + fluid_synth_api_enter(synth); + + /* Test if sfont is already loaded */ + if(synth->sfont == NULL) + { + synth->loaders = fluid_list_prepend(synth->loaders, loader); + } - fluid_return_if_fail (synth != NULL); - fluid_return_if_fail (loader != NULL); - fluid_synth_api_enter(synth); - sfont_already_loaded = synth->sfont_info != NULL; - if (!sfont_already_loaded) - synth->loaders = fluid_list_prepend(synth->loaders, loader); - fluid_synth_api_exit(synth); + fluid_synth_api_exit(synth); } /** @@ -3121,301 +4468,287 @@ fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) * 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 SoundFont instance + * @param synth FluidSynth instance * @param filename File to load - * @param reset_presets TRUE to re-assign presets for all MIDI channels - * @return SoundFont ID on success, FLUID_FAILED on error + * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset()) + * @return SoundFont ID on success, #FLUID_FAILED on error */ int -fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets) +fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets) { - fluid_sfont_info_t *sfont_info; - fluid_sfont_t *sfont; - fluid_list_t *list; - fluid_sfloader_t *loader; - unsigned int sfont_id; - - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (filename != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - /* MT NOTE: Loaders list should not change. */ - - for (list = synth->loaders; list; list = fluid_list_next(list)) { - loader = (fluid_sfloader_t*) fluid_list_get(list); - - sfont = fluid_sfloader_load(loader, filename); + fluid_sfont_t *sfont; + fluid_list_t *list; + fluid_sfloader_t *loader; + int sfont_id; - if (sfont != NULL) { - sfont_info = new_fluid_sfont_info (synth, sfont); - - if (!sfont_info) - { - delete_fluid_sfont (sfont); - FLUID_API_RETURN(FLUID_FAILED); - } + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(filename != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - sfont->id = sfont_id = ++synth->sfont_id; - synth->sfont_info = fluid_list_prepend(synth->sfont_info, sfont_info); /* prepend to list */ - fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + sfont_id = synth->sfont_id; - /* reset the presets for all channels if requested */ - if (reset_presets) fluid_synth_program_reset(synth); + if(++sfont_id != FLUID_FAILED) + { + /* MT NOTE: Loaders list should not change. */ - FLUID_API_RETURN((int)sfont_id); - } - } + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); - FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - FLUID_API_RETURN(FLUID_FAILED); -} + sfont = fluid_sfloader_load(loader, filename); -/* Create a new SoundFont info structure, free with FLUID_FREE */ -static fluid_sfont_info_t * -new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont) -{ - fluid_sfont_info_t *sfont_info; + if(sfont != NULL) + { + sfont->refcount++; + synth->sfont_id = sfont->id = sfont_id; - sfont_info = FLUID_NEW (fluid_sfont_info_t); + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ - if (!sfont_info) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } + /* reset the presets for all channels if requested */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } - sfont_info->sfont = sfont; - sfont_info->synth = synth; - sfont_info->refcount = 1; /* Start with refcount of 1 for owning synth */ - sfont_info->bankofs = 0; + FLUID_API_RETURN(sfont_id); + } + } + } - return (sfont_info); + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + FLUID_API_RETURN(FLUID_FAILED); } /** * Unload a SoundFont. - * @param synth SoundFont instance + * @param synth FluidSynth instance * @param id ID of SoundFont to unload * @param reset_presets TRUE to re-assign presets for all MIDI channels - * @return FLUID_OK on success, FLUID_FAILED on error + * @return #FLUID_OK on success, #FLUID_FAILED on error */ int -fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) +fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) { - fluid_sfont_info_t *sfont_info = NULL; - fluid_list_t *list; + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - /* remove the SoundFont from the list */ - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - if (fluid_sfont_get_id (sfont_info->sfont) == id) + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) { - synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); - break; + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont); + break; + } } - } - if (!list) { - FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); - FLUID_API_RETURN(FLUID_FAILED); - } + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + FLUID_API_RETURN(FLUID_FAILED); + } - /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ - if (reset_presets) fluid_synth_program_reset (synth); - else fluid_synth_update_presets (synth); + /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } + else + { + fluid_synth_update_presets(synth); + } - /* -- Remove synth->sfont_info list's reference to SoundFont */ - fluid_synth_sfont_unref (synth, sfont_info->sfont); + /* -- Remove synth->sfont list's reference to SoundFont */ + fluid_synth_sfont_unref(synth, sfont); - FLUID_API_RETURN(FLUID_OK); + FLUID_API_RETURN(FLUID_OK); } /* Unref a SoundFont and destroy if no more references */ void -fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont) +fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont) { - fluid_sfont_info_t *sfont_info; - int refcount = 0; - - sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont); - - if (sfont_info) - { - sfont_info->refcount--; /* -- Remove the sfont_info list's reference */ - refcount = sfont_info->refcount; - - if (refcount == 0) /* Remove SoundFont from hash if no more references */ - fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); - } + fluid_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */ - fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */ + sfont->refcount--; /* -- Remove the sfont list's reference */ - if (refcount == 0) /* No more references? - Attempt delete */ - { - if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */ + if(sfont->refcount == 0) /* No more references? - Attempt delete */ { - FLUID_FREE (sfont_info); - FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); - } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ - else new_fluid_timer (100, fluid_synth_sfunload_callback, sfont_info, TRUE, TRUE, FALSE); - } + if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */ + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ + else + { + new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, TRUE, FALSE); + } + } } /* Callback to continually attempt to unload a SoundFont, * only if a SoundFont loader blocked the unload operation */ static int -fluid_synth_sfunload_callback(void* data, unsigned int msec) +fluid_synth_sfunload_callback(void *data, unsigned int msec) { - fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data; + fluid_sfont_t *sfont = data; - if (delete_fluid_sfont (sfont_info->sfont) == 0) - { - FLUID_FREE (sfont_info); - FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); - return FALSE; - } - else return TRUE; + if(fluid_sfont_delete_internal(sfont) == 0) + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + return FALSE; + } + else + { + return TRUE; + } } /** * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack. - * @param synth SoundFont instance + * @param synth FluidSynth instance * @param id ID of SoundFont to reload - * @return SoundFont ID on success, FLUID_FAILED on error + * @return SoundFont ID on success, #FLUID_FAILED on error */ int -fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) -{ - char filename[1024]; - fluid_sfont_info_t *sfont_info, *old_sfont_info; - fluid_sfont_t* sfont; - fluid_sfloader_t* loader; - fluid_list_t *list; - int index; - - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - /* Search for SoundFont and get its index */ - for (list = synth->sfont_info, index = 0; list; list = fluid_list_next (list), index++) { - old_sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); - if (fluid_sfont_get_id (old_sfont_info->sfont) == id) break; - } - - if (!list) { - FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); - FLUID_API_RETURN(FLUID_FAILED); - } +fluid_synth_sfreload(fluid_synth_t *synth, int id) +{ + char *filename = NULL; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + fluid_list_t *list; + int index, ret = FLUID_FAILED; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - /* keep a copy of the SoundFont's filename */ - FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont)); + /* Search for SoundFont and get its index */ + for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++) + { + sfont = fluid_list_get(list); - if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK) - FLUID_API_RETURN(FLUID_FAILED); + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + goto exit; + } - /* MT Note: SoundFont loader list will not change */ + /* keep a copy of the SoundFont's filename */ + filename = FLUID_STRDUP(fluid_sfont_get_name(sfont)); - for (list = synth->loaders; list; list = fluid_list_next(list)) { - loader = (fluid_sfloader_t*) fluid_list_get(list); + if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK) + { + goto exit; + } - sfont = fluid_sfloader_load(loader, filename); + /* MT Note: SoundFont loader list will not change */ - if (sfont != NULL) { - sfont->id = id; + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); - sfont_info = new_fluid_sfont_info (synth, sfont); + sfont = fluid_sfloader_load(loader, filename); - if (!sfont_info) - { - delete_fluid_sfont (sfont); - FLUID_API_RETURN(FLUID_FAILED); - } + if(sfont != NULL) + { + sfont->id = id; + sfont->refcount++; - synth->sfont_info = fluid_list_insert_at(synth->sfont_info, index, sfont_info); /* insert the sfont at the same index */ - fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */ - /* reset the presets for all channels */ - fluid_synth_update_presets(synth); - FLUID_API_RETURN(sfont->id); + /* reset the presets for all channels */ + fluid_synth_update_presets(synth); + ret = id; + goto exit; + } } - } - FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - FLUID_API_RETURN(FLUID_FAILED); + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + +exit: + FLUID_FREE(filename); + FLUID_API_RETURN(ret); } /** * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack. * @param synth FluidSynth instance * @param sfont SoundFont to add - * @return New assigned SoundFont ID or FLUID_FAILED on error + * @return New assigned SoundFont ID or #FLUID_FAILED on error */ int -fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) { - fluid_sfont_info_t *sfont_info; - unsigned int sfont_id; + int sfont_id; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (sfont != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - sfont_info = new_fluid_sfont_info (synth, sfont); - if (!sfont_info) - FLUID_API_RETURN(FLUID_FAILED); + sfont_id = synth->sfont_id; - sfont->id = sfont_id = ++synth->sfont_id; - synth->sfont_info = fluid_list_prepend (synth->sfont_info, sfont_info); /* prepend to list */ - fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + if(++sfont_id != FLUID_FAILED) + { + synth->sfont_id = sfont->id = sfont_id; + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ - /* reset the presets for all channels */ - fluid_synth_program_reset (synth); + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + } - FLUID_API_RETURN(sfont_id); + FLUID_API_RETURN(sfont_id); } /** * Remove a SoundFont from the SoundFont stack without deleting it. * @param synth FluidSynth instance * @param sfont SoundFont to remove + * @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise * * SoundFont is not freed and is left as the responsibility of the caller. * - * NOTE: The SoundFont should only be freed after there are no presets + * @note The SoundFont should only be freed after there are no presets * referencing it. This can only be ensured by the SoundFont loader and * therefore this function should not normally be used. */ -void -fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +int +fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) { - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; + fluid_sfont_t *sfont_tmp; + fluid_list_t *list; + int ret = FLUID_FAILED; - fluid_return_if_fail (synth != NULL); - fluid_return_if_fail (sfont != NULL); - fluid_synth_api_enter(synth); - - /* remove the SoundFont from the list */ - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); - if (sfont_info->sfont == sfont) + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) { - synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); + sfont_tmp = fluid_list_get(list); - /* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */ - fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); - break; + if(sfont_tmp == sfont) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp); + ret = FLUID_OK; + break; + } } - } - /* reset the presets for all channels */ - fluid_synth_program_reset (synth); - fluid_synth_api_exit(synth); + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + + FLUID_API_RETURN(ret); } /** @@ -3424,14 +4757,14 @@ fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) * @return Count of loaded SoundFont files. */ int -fluid_synth_sfcount(fluid_synth_t* synth) +fluid_synth_sfcount(fluid_synth_t *synth) { - int count; - - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); - count = fluid_list_size (synth->sfont_info); - FLUID_API_RETURN(count); + int count; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + count = fluid_list_size(synth->sfont); + FLUID_API_RETURN(count); } /** @@ -3440,20 +4773,25 @@ fluid_synth_sfcount(fluid_synth_t* synth) * @param num SoundFont index on the stack (starting from 0 for top of stack). * @return SoundFont instance or NULL if invalid index * - * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * -fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) +fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num) { - fluid_sfont_t *sfont = NULL; - fluid_list_t *list; + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); + list = fluid_list_nth(synth->sfont, num); - fluid_return_val_if_fail (synth != NULL, NULL); - fluid_synth_api_enter(synth); - list = fluid_list_nth (synth->sfont_info, num); - if (list) sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; - FLUID_API_RETURN(sfont); + if(list) + { + sfont = fluid_list_get(list); + } + + FLUID_API_RETURN(sfont); } /** @@ -3462,25 +4800,29 @@ fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) * @param id SoundFont ID * @return SoundFont instance or NULL if invalid ID * - * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * -fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) +fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id) { - fluid_sfont_t* sfont = NULL; - fluid_list_t* list; + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); - fluid_return_val_if_fail (synth != NULL, NULL); - fluid_synth_api_enter(synth); + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; - if (fluid_sfont_get_id (sfont) == id) - break; - } + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } - FLUID_API_RETURN(list ? sfont : NULL); + FLUID_API_RETURN(list ? sfont : NULL); } /** @@ -3490,137 +4832,92 @@ fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) * @return SoundFont instance or NULL if invalid name * @since 1.1.0 * - * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * -fluid_synth_get_sfont_by_name(fluid_synth_t* synth, const char *name) +fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name) { - fluid_sfont_t* sfont = NULL; - fluid_list_t* list; + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_synth_api_enter(synth); - fluid_return_val_if_fail (synth != NULL, NULL); - fluid_return_val_if_fail (name != NULL, NULL); - fluid_synth_api_enter(synth); + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; - if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) - break; - } + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) + { + break; + } + } - FLUID_API_RETURN(list ? sfont : NULL); + FLUID_API_RETURN(list ? sfont : NULL); } /** * Get active preset on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @return Preset or NULL if no preset active on channel - * @deprecated fluid_synth_get_channel_info() should replace most use cases. + * @return Preset or NULL if no preset active on \c chan * - * NOTE: Should only be called from within synthesis thread, which includes - * SoundFont loader preset noteon methods. Not thread safe otherwise. + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Not thread safe otherwise. */ fluid_preset_t * -fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan) -{ - fluid_preset_t* result; - fluid_channel_t *channel; - FLUID_API_ENTRY_CHAN(NULL); - - channel = synth->channel[chan]; - result = channel->preset; - fluid_synth_api_exit(synth); - return result; -} - -/** - * Get information on the currently selected preset on a MIDI channel. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param info Caller supplied structure to fill with preset information - * @return #FLUID_OK on success, #FLUID_FAILED otherwise - * @since 1.1.1 - */ -int -fluid_synth_get_channel_info (fluid_synth_t *synth, int chan, - fluid_synth_channel_info_t *info) +fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan) { - fluid_channel_t *channel; - fluid_preset_t *preset; - char *name; - - if (info) - { - info->assigned = FALSE; - info->name[0] = '\0'; - } - - fluid_return_val_if_fail (info != NULL, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - channel = synth->channel[chan]; - preset = channel->preset; - - if (preset) - { - info->assigned = TRUE; - name = fluid_preset_get_name (preset); - - if (name) - { - strncpy (info->name, name, FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE); - info->name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE - 1] = '\0'; - } - else info->name[0] = '\0'; + fluid_preset_t *result; + fluid_channel_t *channel; + FLUID_API_ENTRY_CHAN(NULL); - info->sfont_id = preset->sfont->id; - info->bank = fluid_preset_get_banknum (preset); - info->program = fluid_preset_get_num (preset); - } - else - { - info->assigned = FALSE; - fluid_channel_get_sfont_bank_prog (channel, &info->sfont_id, &info->bank, &info->program); - info->name[0] = '\0'; - } - - fluid_synth_api_exit(synth); - return FLUID_OK; + channel = synth->channel[chan]; + result = channel->preset; + fluid_synth_api_exit(synth); + return result; } /** - * Get list of voices. + * Get list of currently playing voices. * @param synth FluidSynth instance * @param buf Array to store voices to (NULL terminated if not filled completely) * @param bufsize Count of indexes in buf * @param id Voice ID to search for or < 0 to return list of all playing voices * - * NOTE: Should only be called from within synthesis thread, which includes + * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon methods. Voices are only guaranteed to remain * unchanged until next synthesis process iteration. */ void -fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, +fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize, int id) { - int count = 0; - int i; + int count = 0; + int i; + + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(buf != NULL); + fluid_synth_api_enter(synth); - fluid_return_if_fail (synth != NULL); - fluid_return_if_fail (buf != NULL); - fluid_synth_api_enter(synth); + for(i = 0; i < synth->polyphony && count < bufsize; i++) + { + fluid_voice_t *voice = synth->voice[i]; - for (i = 0; i < synth->polyphony && count < bufsize; i++) { - fluid_voice_t* voice = synth->voice[i]; + if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id)) + { + buf[count++] = voice; + } + } - if (_PLAYING(voice) && (id < 0 || (int)voice->id == id)) - buf[count++] = voice; - } + if(count < bufsize) + { + buf[count] = NULL; + } - if (count < bufsize) buf[count] = NULL; - fluid_synth_api_exit(synth); + fluid_synth_api_exit(synth); } /** @@ -3629,56 +4926,94 @@ fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsiz * @param on TRUE to enable reverb, FALSE to disable */ void -fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) +fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) { - fluid_return_if_fail (synth != NULL); + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); - fluid_atomic_int_set (&synth->with_reverb, on != 0); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, - on != 0, 0.0f); + synth->with_reverb = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); } /** * Activate a reverb preset. * @param synth FluidSynth instance * @param num Reverb preset number - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * - * NOTE: Currently private to libfluidsynth. + * @note Currently private to libfluidsynth. */ int -fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) +fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num) { - int i = 0; - while (revmodel_preset[i].name != NULL) { - if (i == num) { - fluid_synth_set_reverb (synth, revmodel_preset[i].roomsize, - revmodel_preset[i].damp, revmodel_preset[i].width, - revmodel_preset[i].level); - return FLUID_OK; - } - i++; - } - return FLUID_FAILED; + fluid_return_val_if_fail( + num < FLUID_N_ELEMENTS(revmodel_preset), + FLUID_FAILED + ); + + fluid_synth_set_reverb(synth, revmodel_preset[num].roomsize, + revmodel_preset[num].damp, revmodel_preset[num].width, + revmodel_preset[num].level); + return FLUID_OK; } /** * Set reverb parameters. * @param synth FluidSynth instance - * @param roomsize Reverb room size value (0.0-1.2) + * @param roomsize Reverb room size value (0.0-1.0) * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * - * NOTE: Not realtime safe and therefore should not be called from synthesis + * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. */ -void -fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, +int +fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, double width, double level) { - fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ALL, - roomsize, damping, width, level); + return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ALL, + roomsize, damping, width, level); +} + +/** + * Set reverb roomsize. See fluid_synth_set_reverb() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize) +{ + return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ROOMSIZE, roomsize, 0, 0, 0); +} + +/** + * Set reverb damping. See fluid_synth_set_reverb() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping) +{ + return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_DAMPING, 0, damping, 0, 0); +} + +/** + * Set reverb width. See fluid_synth_set_reverb() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width) +{ + return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_WIDTH, 0, 0, width, 0); +} + +/** + * Set reverb level. See fluid_synth_set_reverb() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_LEVEL, 0, 0, 0, level); } /** @@ -3689,42 +5024,66 @@ fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * - * NOTE: Not realtime safe and therefore should not be called from synthesis + * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. */ int -fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, +fluid_synth_set_reverb_full(fluid_synth_t *synth, int set, double roomsize, double damping, double width, double level) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + int ret; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); - if (!(set & FLUID_REVMODEL_SET_ALL)) - set = FLUID_REVMODEL_SET_ALL; + /* Synth shadow values are set here so that they will be returned if querried */ - /* Synth shadow values are set here so that they will be returned if querried */ + fluid_synth_api_enter(synth); + ret = fluid_synth_set_reverb_full_LOCAL(synth, set, roomsize, damping, width, level); + FLUID_API_RETURN(ret); +} - fluid_synth_api_enter(synth); +static int +fluid_synth_set_reverb_full_LOCAL(fluid_synth_t *synth, int set, double roomsize, + double damping, double width, double level) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; - if (set & FLUID_REVMODEL_SET_ROOMSIZE) - fluid_atomic_float_set (&synth->reverb_roomsize, roomsize); + if(set & FLUID_REVMODEL_SET_ROOMSIZE) + { + synth->reverb_roomsize = roomsize; + } - if (set & FLUID_REVMODEL_SET_DAMPING) - fluid_atomic_float_set (&synth->reverb_damping, damping); + if(set & FLUID_REVMODEL_SET_DAMPING) + { + synth->reverb_damping = damping; + } - if (set & FLUID_REVMODEL_SET_WIDTH) - fluid_atomic_float_set (&synth->reverb_width, width); + if(set & FLUID_REVMODEL_SET_WIDTH) + { + synth->reverb_width = width; + } - if (set & FLUID_REVMODEL_SET_LEVEL) - fluid_atomic_float_set (&synth->reverb_level, level); + if(set & FLUID_REVMODEL_SET_LEVEL) + { + synth->reverb_level = level; + } - fluid_rvoice_eventhandler_push5(synth->eventhandler, - fluid_rvoice_mixer_set_reverb_params, - synth->eventhandler->mixer, set, - roomsize, damping, width, level, 0.0f); - - FLUID_API_RETURN(FLUID_OK); + param[0].i = set; + param[1].real = roomsize; + param[2].real = damping; + param[3].real = width; + param[4].real = level; + /* finally enqueue an rvoice event to the mixer to actual update reverb */ + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_reverb_params, + synth->eventhandler->mixer, + param); + return ret; } /** @@ -3733,13 +5092,13 @@ fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, * @return Reverb room size (0.0-1.2) */ double -fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) +fluid_synth_get_reverb_roomsize(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->reverb_roomsize); - FLUID_API_RETURN(result); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); + result = synth->reverb_roomsize; + FLUID_API_RETURN(result); } /** @@ -3748,14 +5107,14 @@ fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) * @return Reverb damping value (0.0-1.0) */ double -fluid_synth_get_reverb_damp(fluid_synth_t* synth) +fluid_synth_get_reverb_damp(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->reverb_damping); - FLUID_API_RETURN(result); + result = synth->reverb_damping; + FLUID_API_RETURN(result); } /** @@ -3764,14 +5123,14 @@ fluid_synth_get_reverb_damp(fluid_synth_t* synth) * @return Reverb level value (0.0-1.0) */ double -fluid_synth_get_reverb_level(fluid_synth_t* synth) +fluid_synth_get_reverb_level(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->reverb_level); - FLUID_API_RETURN(result); + result = synth->reverb_level; + FLUID_API_RETURN(result); } /** @@ -3780,14 +5139,14 @@ fluid_synth_get_reverb_level(fluid_synth_t* synth) * @return Reverb width value (0.0-100.0) */ double -fluid_synth_get_reverb_width(fluid_synth_t* synth) +fluid_synth_get_reverb_width(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->reverb_width); - FLUID_API_RETURN(result); + result = synth->reverb_width; + FLUID_API_RETURN(result); } /** @@ -3795,20 +5154,21 @@ fluid_synth_get_reverb_width(fluid_synth_t* synth) * @param synth FluidSynth instance * @param on TRUE to enable chorus, FALSE to disable */ -void -fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) +void +fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) { - fluid_return_if_fail (synth != NULL); - fluid_synth_api_enter(synth); + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); - fluid_atomic_int_set (&synth->with_chorus, on != 0); - fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled, - on != 0, 0.0f); - fluid_synth_api_exit(synth); + synth->with_chorus = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); } /** - * Set chorus parameters. + * Set chorus parameters. It should be turned on with fluid_synth_set_chorus_on(). + * Keep in mind, that the needed CPU time is proportional to 'nr'. * @param synth FluidSynth instance * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) @@ -3817,13 +5177,58 @@ fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ -void -fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, - double speed, double depth_ms, int type) +int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type) +{ + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_ALL, nr, level, speed, + depth_ms, type); +} + +/** + * Set the chorus voice count. See fluid_synth_set_chorus() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr) +{ + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_NR, nr, 0, 0, 0, 0); +} + +/** + * Set the chorus level. See fluid_synth_set_chorus() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0, 0, 0); +} + +/** + * Set the chorus speed. See fluid_synth_set_chorus() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed) +{ + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_SPEED, 0, 0, speed, 0, 0); +} + +/** + * Set the chorus depth. See fluid_synth_set_chorus() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms) +{ + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_DEPTH, 0, 0, 0, depth_ms, 0); +} + +/** + * Set the chorus type. See fluid_synth_set_chorus() for further info. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) { - fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed, - depth_ms, type); + return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type); } /** @@ -3837,40 +5242,70 @@ fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, +fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, double speed, double depth_ms, int type) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + int ret; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); + + /* Synth shadow values are set here so that they will be returned if queried */ + fluid_synth_api_enter(synth); + + ret = fluid_synth_set_chorus_full_LOCAL(synth, set, nr, level, speed, depth_ms, type); - if (!(set & FLUID_CHORUS_SET_ALL)) - set = FLUID_CHORUS_SET_ALL; + FLUID_API_RETURN(ret); +} + +static int +fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int nr, double level, + double speed, double depth_ms, int type) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; - /* Synth shadow values are set here so that they will be returned if queried */ - fluid_synth_api_enter(synth); + if(set & FLUID_CHORUS_SET_NR) + { + synth->chorus_nr = nr; + } - if (set & FLUID_CHORUS_SET_NR) - fluid_atomic_int_set (&synth->chorus_nr, nr); + if(set & FLUID_CHORUS_SET_LEVEL) + { + synth->chorus_level = level; + } - if (set & FLUID_CHORUS_SET_LEVEL) - fluid_atomic_float_set (&synth->chorus_level, level); + if(set & FLUID_CHORUS_SET_SPEED) + { + synth->chorus_speed = speed; + } - if (set & FLUID_CHORUS_SET_SPEED) - fluid_atomic_float_set (&synth->chorus_speed, speed); + if(set & FLUID_CHORUS_SET_DEPTH) + { + synth->chorus_depth = depth_ms; + } - if (set & FLUID_CHORUS_SET_DEPTH) - fluid_atomic_float_set (&synth->chorus_depth, depth_ms); + if(set & FLUID_CHORUS_SET_TYPE) + { + synth->chorus_type = type; + } - if (set & FLUID_CHORUS_SET_TYPE) - fluid_atomic_int_set (&synth->chorus_type, type); - - fluid_rvoice_eventhandler_push5(synth->eventhandler, - fluid_rvoice_mixer_set_chorus_params, - synth->eventhandler->mixer, set, - nr, level, speed, depth_ms, type); + param[0].i = set; + param[1].i = nr; + param[2].real = level; + param[3].real = speed; + param[4].real = depth_ms; + param[5].i = type; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_chorus_params, + synth->eventhandler->mixer, + param); - FLUID_API_RETURN(FLUID_OK); + return (ret); } /** @@ -3879,14 +5314,14 @@ fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, * @return Chorus voice count (0-99) */ int -fluid_synth_get_chorus_nr(fluid_synth_t* synth) +fluid_synth_get_chorus_nr(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_int_get (&synth->chorus_nr); - FLUID_API_RETURN(result); + result = synth->chorus_nr; + FLUID_API_RETURN(result); } /** @@ -3895,14 +5330,14 @@ fluid_synth_get_chorus_nr(fluid_synth_t* synth) * @return Chorus level value (0.0-10.0) */ double -fluid_synth_get_chorus_level(fluid_synth_t* synth) +fluid_synth_get_chorus_level(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->chorus_level); - FLUID_API_RETURN(result); + result = synth->chorus_level; + FLUID_API_RETURN(result); } /** @@ -3911,14 +5346,14 @@ fluid_synth_get_chorus_level(fluid_synth_t* synth) * @return Chorus speed in Hz (0.29-5.0) */ double -fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) +fluid_synth_get_chorus_speed(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->chorus_speed); - FLUID_API_RETURN(result); + result = synth->chorus_speed; + FLUID_API_RETURN(result); } /** @@ -3927,14 +5362,14 @@ fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) * @return Chorus depth */ double -fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) +fluid_synth_get_chorus_depth(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_float_get (&synth->chorus_depth); - FLUID_API_RETURN(result); + result = synth->chorus_depth; + FLUID_API_RETURN(result); } /** @@ -3943,14 +5378,14 @@ fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) * @return Chorus waveform type (#fluid_chorus_mod) */ int -fluid_synth_get_chorus_type(fluid_synth_t* synth) +fluid_synth_get_chorus_type(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_synth_api_enter(synth); + double result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); - result = fluid_atomic_int_get (&synth->chorus_type); - FLUID_API_RETURN(result); + result = synth->chorus_type; + FLUID_API_RETURN(result); } /* @@ -3962,28 +5397,42 @@ fluid_synth_get_chorus_type(fluid_synth_t* synth) * several voice processes, for example a stereo sample. Don't * release those... */ -static void -fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, - int key) +void +fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, + int key) { - int i; - fluid_voice_t* voice; + int i; + fluid_voice_t *voice; + + /* storeid is a parameter for fluid_voice_init() */ + synth->storeid = synth->noteid++; - synth->storeid = synth->noteid++; + /* for "monophonic playing" key is the previous sustained note + if it exists (0 to 127) or INVALID_NOTE otherwise */ + if(key == INVALID_NOTE) + { + return; + } + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; - 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)) { - /* Id of voices that was sustained by sostenuto */ - if(_HELD_BY_SOSTENUTO(voice)) - synth->storeid = voice->id; - /* Force the voice into release stage (pedaling is ignored) */ - fluid_voice_release(voice); + if(fluid_voice_is_playing(voice) + && (fluid_voice_get_channel(voice) == chan) + && (fluid_voice_get_key(voice) == key) + && (fluid_voice_get_id(voice) != synth->noteid)) + { + /* Id of voices that was sustained by sostenuto */ + if(fluid_voice_is_sostenuto(voice)) + { + synth->storeid = fluid_voice_get_id(voice); + } + + /* Force the voice into release stage (pedaling is ignored) */ + fluid_voice_release(voice); + } } - } } /** @@ -3991,29 +5440,36 @@ fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, * @param synth FluidSynth instance * @param chan MIDI channel to set interpolation method on or -1 for all channels * @param interp_method Interpolation method (#fluid_interp) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int -fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method) +fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method) { - int i; - - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - if (chan < -1 || chan >= synth->midi_channels) - FLUID_API_RETURN(FLUID_FAILED); + int i; - if (synth->channel[0] == NULL) { - FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!"); - FLUID_API_RETURN(FLUID_FAILED); - } + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan < -1 || chan >= synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(synth->channel[0] == NULL) + { + FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); + FLUID_API_RETURN(FLUID_FAILED); + } - for (i = 0; i < synth->midi_channels; i++) { - if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) - fluid_channel_set_interp_method(synth->channel[i], interp_method); - } + for(i = 0; i < synth->midi_channels; i++) + { + if(chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) + { + fluid_channel_set_interp_method(synth->channel[i], interp_method); + } + } - FLUID_API_RETURN(FLUID_OK); + FLUID_API_RETURN(FLUID_OK); }; /** @@ -4022,14 +5478,14 @@ fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method) * @return Count of MIDI channels */ int -fluid_synth_count_midi_channels(fluid_synth_t* synth) +fluid_synth_count_midi_channels(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); - result = synth->midi_channels; - FLUID_API_RETURN(result); + result = synth->midi_channels; + FLUID_API_RETURN(result); } /** @@ -4038,14 +5494,14 @@ fluid_synth_count_midi_channels(fluid_synth_t* synth) * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc) */ int -fluid_synth_count_audio_channels(fluid_synth_t* synth) +fluid_synth_count_audio_channels(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); - result = synth->audio_channels; - FLUID_API_RETURN(result); + result = synth->audio_channels; + FLUID_API_RETURN(result); } /** @@ -4056,14 +5512,14 @@ fluid_synth_count_audio_channels(fluid_synth_t* synth) * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc) */ int -fluid_synth_count_audio_groups(fluid_synth_t* synth) +fluid_synth_count_audio_groups(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); - result = synth->audio_groups; - FLUID_API_RETURN(result); + result = synth->audio_groups; + FLUID_API_RETURN(result); } /** @@ -4072,14 +5528,30 @@ fluid_synth_count_audio_groups(fluid_synth_t* synth) * @return Count of allocated effects channels */ int -fluid_synth_count_effects_channels(fluid_synth_t* synth) +fluid_synth_count_effects_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->effects_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated effects units. + * @param synth FluidSynth instance + * @return Count of allocated effects units + */ +int +fluid_synth_count_effects_groups(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); - result = synth->effects_channels; - FLUID_API_RETURN(result); + result = synth->effects_groups; + FLUID_API_RETURN(result); } /** @@ -4088,138 +5560,139 @@ fluid_synth_count_effects_channels(fluid_synth_t* synth) * @return Estimated CPU load value in percent (0-100) */ double -fluid_synth_get_cpu_load(fluid_synth_t* synth) +fluid_synth_get_cpu_load(fluid_synth_t *synth) { - fluid_return_val_if_fail (synth != NULL, 0); - return fluid_atomic_float_get (&synth->cpu_load); + fluid_return_val_if_fail(synth != NULL, 0); + return fluid_atomic_float_get(&synth->cpu_load); } /* Get tuning for a given bank:program */ static fluid_tuning_t * -fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) +fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog) { - if ((synth->tuning == NULL) || - (synth->tuning[bank] == NULL) || - (synth->tuning[bank][prog] == NULL)) - return NULL; + if((synth->tuning == NULL) || + (synth->tuning[bank] == NULL) || + (synth->tuning[bank][prog] == NULL)) + { + return NULL; + } - return synth->tuning[bank][prog]; + return synth->tuning[bank][prog]; } /* Replace tuning on a given bank:program (need not already exist). * Synth mutex should already be locked by caller. */ static int -fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, fluid_tuning_t *tuning, - int bank, int prog, int apply) +fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, fluid_tuning_t *tuning, + int bank, int prog, int apply) { - fluid_tuning_t *old_tuning; -// fluid_event_queue_t *queue; -// fluid_event_queue_elem_t *event; + fluid_tuning_t *old_tuning; + + if(synth->tuning == NULL) + { + synth->tuning = FLUID_ARRAY(fluid_tuning_t **, 128); + + if(synth->tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } - if (synth->tuning == NULL) { - synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); - if (synth->tuning == NULL) { - FLUID_LOG(FLUID_PANIC, "Out of memory"); - return FLUID_FAILED; + FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t **)); } - 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 FLUID_FAILED; + 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 FLUID_FAILED; + } + + FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t *)); } - FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*)); - } - old_tuning = synth->tuning[bank][prog]; - synth->tuning[bank][prog] = tuning; + old_tuning = synth->tuning[bank][prog]; + synth->tuning[bank][prog] = tuning; - if (old_tuning) { - if (!fluid_tuning_unref (old_tuning, 1)) /* -- unref old tuning */ - { /* Replace old tuning if present */ - fluid_synth_replace_tuning_LOCAL (synth, old_tuning, tuning, apply, FALSE); + if(old_tuning) + { + if(!fluid_tuning_unref(old_tuning, 1)) /* -- unref old tuning */ + { + /* Replace old tuning if present */ + fluid_synth_replace_tuning_LOCAL(synth, old_tuning, tuning, apply, FALSE); + } } - } - return FLUID_OK; + return FLUID_OK; } /* Replace a tuning with a new one in all MIDI channels. new_tuning can be * NULL, in which case channels are reset to default equal tempered scale. */ static void -fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, fluid_tuning_t *old_tuning, - fluid_tuning_t *new_tuning, int apply, int unref_new) +fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, int apply, int unref_new) { -// fluid_event_queue_elem_t *event; - fluid_channel_t *channel; - int old_tuning_unref = 0; - int i; - - for (i = 0; i < synth->midi_channels; i++) - { - channel = synth->channel[i]; + fluid_channel_t *channel; + int old_tuning_unref = 0; + int i; - if (fluid_channel_get_tuning (channel) == old_tuning) + for(i = 0; i < synth->midi_channels; i++) { - old_tuning_unref++; - if (new_tuning) fluid_tuning_ref (new_tuning); /* ++ ref new tuning for channel */ - fluid_channel_set_tuning (channel, new_tuning); + channel = synth->channel[i]; + + if(fluid_channel_get_tuning(channel) == old_tuning) + { + old_tuning_unref++; + + if(new_tuning) + { + fluid_tuning_ref(new_tuning); /* ++ ref new tuning for channel */ + } - if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); + fluid_channel_set_tuning(channel, new_tuning); + + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + } + } + + /* Send unref old tuning event if any unrefs */ + if(old_tuning && old_tuning_unref) + { + fluid_tuning_unref(old_tuning, old_tuning_unref); } - } - /* Send unref old tuning event if any unrefs */ - if (old_tuning && old_tuning_unref) - fluid_tuning_unref (old_tuning, old_tuning_unref); - if (!unref_new || !new_tuning) return; + if(!unref_new || !new_tuning) + { + return; + } - fluid_tuning_unref (new_tuning, 1); + fluid_tuning_unref(new_tuning, 1); } /* Update voice tunings in realtime */ static void -fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *channel) +fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, fluid_channel_t *channel) { - fluid_voice_t *voice; - int i; - - for (i = 0; i < synth->polyphony; i++) - { - voice = synth->voice[i]; + fluid_voice_t *voice; + int i; - if (_ON (voice) && (voice->channel == channel)) + for(i = 0; i < synth->polyphony; i++) { - fluid_voice_calculate_gen_pitch (voice); - fluid_voice_update_param (voice, GEN_PITCH); - } - } -} + voice = synth->voice[i]; -/** - * Set the tuning of the entire MIDI note scale. - * @param synth FluidSynth instance - * @param bank Tuning bank number (0-127), not related to MIDI instrument bank - * @param prog Tuning preset number (0-127), not related to MIDI instrument program - * @param name Label name for this tuning - * @param pitch Array of pitch values (length of 128, each value is number of - * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). - * Pass NULL to create a well-tempered (normal) scale. - * @return FLUID_OK on success, FLUID_FAILED otherwise - * - * NOTE: Tuning is not applied in realtime to existing notes of the replaced - * tuning (if any), use fluid_synth_activate_key_tuning() instead to specify - * this behavior. - */ -int -fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch) -{ - return fluid_synth_activate_key_tuning (synth, bank, prog, name, pitch, FALSE); + if(fluid_voice_is_on(voice) && (voice->channel == channel)) + { + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + } + } } /** @@ -4230,58 +5703,48 @@ fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, * @param name Label name for this tuning * @param pitch Array of pitch values (length of 128, each value is number of * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). - * Pass NULL to create a well-tempered (normal) scale. + * Pass NULL to create a equal tempered (normal) scale. * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int -fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply) +fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) { - fluid_tuning_t* tuning; - int retval = FLUID_OK; + fluid_tuning_t *tuning; + int retval = FLUID_OK; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); - fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); + fluid_synth_api_enter(synth); - tuning = new_fluid_tuning (name, bank, prog); + tuning = new_fluid_tuning(name, bank, prog); - if (tuning) - { - if (pitch) fluid_tuning_set_all (tuning, pitch); - retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply); - if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); - } - else retval = FLUID_FAILED; - FLUID_API_RETURN(retval); -} + if(tuning) + { + if(pitch) + { + fluid_tuning_set_all(tuning, pitch); + } -/** - * Apply an octave tuning to every octave in the MIDI note scale. - * @param synth FluidSynth instance - * @param bank Tuning bank number (0-127), not related to MIDI instrument bank - * @param prog Tuning preset number (0-127), not related to MIDI instrument program - * @param name Label name for this tuning - * @param pitch Array of pitch values (length of 12 for each note of an octave - * starting at note C, values are number of offset cents to add to the normal - * tuning amount) - * @return FLUID_OK on success, FLUID_FAILED otherwise - * - * NOTE: Tuning is not applied in realtime to existing notes of the replaced - * tuning (if any), use fluid_synth_activate_octave_tuning() instead to specify - * this behavior. - */ -int -fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch) -{ - return fluid_synth_activate_octave_tuning (synth, bank, prog, name, pitch, FALSE); + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); } /** @@ -4295,34 +5758,41 @@ fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, * tuning amount) * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int -fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, - const char* name, const double* pitch, int apply) +fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) { - fluid_tuning_t* tuning; - int retval = FLUID_OK; + fluid_tuning_t *tuning; + int retval = FLUID_OK; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); - fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); - fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - tuning = new_fluid_tuning (name, bank, prog); + fluid_synth_api_enter(synth); + tuning = new_fluid_tuning(name, bank, prog); + + if(tuning) + { + fluid_tuning_set_octave(tuning, pitch); + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); - if (tuning) - { - fluid_tuning_set_octave (tuning, pitch); - retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply); - if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); - } - else retval = FLUID_FAILED; + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } - FLUID_API_RETURN(retval); + FLUID_API_RETURN(retval); } /** @@ -4336,68 +5806,60 @@ fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, * cents from MIDI note 0) * @param apply TRUE to apply tuning change in realtime to existing notes using * the specified tuning, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * - * NOTE: Prior to version 1.1.0 it was an error to specify a tuning that didn't - * already exist. Starting with 1.1.0, the default equal tempered scale will be + * @note Prior to version 1.1.0 it was an error to specify a tuning that didn't + * already exist. Starting with 1.1.0, the default equal tempered scale will be * used as a basis, if no tuning exists for the given bank and prog. */ int -fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, - int len, const int *key, const double* pitch, int apply) +fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *key, const double *pitch, int apply) { - fluid_tuning_t* old_tuning, *new_tuning; - int retval = FLUID_OK; - int i; + fluid_tuning_t *old_tuning, *new_tuning; + int retval = FLUID_OK; + int i; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); - fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); - fluid_return_val_if_fail (len > 0, FLUID_FAILED); - fluid_return_val_if_fail (key != NULL, FLUID_FAILED); - fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); - - fluid_synth_api_enter(synth); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(key != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); - old_tuning = fluid_synth_get_tuning (synth, bank, prog); + fluid_synth_api_enter(synth); - if (old_tuning) - new_tuning = fluid_tuning_duplicate (old_tuning); - else new_tuning = new_fluid_tuning ("Unnamed", bank, prog); + old_tuning = fluid_synth_get_tuning(synth, bank, prog); - if (new_tuning) - { - for (i = 0; i < len; i++) - fluid_tuning_set_pitch (new_tuning, key[i], pitch[i]); + if(old_tuning) + { + new_tuning = fluid_tuning_duplicate(old_tuning); + } + else + { + new_tuning = new_fluid_tuning("Unnamed", bank, prog); + } - retval = fluid_synth_replace_tuning_LOCK (synth, new_tuning, bank, prog, apply); - if (retval == FLUID_FAILED) fluid_tuning_unref (new_tuning, 1); - } - else retval = FLUID_FAILED; + if(new_tuning) + { + for(i = 0; i < len; i++) + { + fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]); + } - FLUID_API_RETURN(retval); -} + retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply); -/** - * Select a tuning scale on a MIDI channel. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param bank Tuning bank number (0-127), not related to MIDI instrument bank - * @param prog Tuning preset number (0-127), not related to MIDI instrument program - * @return FLUID_OK on success, FLUID_FAILED otherwise - * - * NOTE: This function does NOT activate tuning in realtime, use - * fluid_synth_activate_tuning() instead to specify whether tuning change - * should cause existing notes to update. - * - * NOTE: Prior to version 1.1.0 it was an error to select a tuning that didn't - * already exist. Starting with 1.1.0, a default equal tempered scale will be - * created, if no tuning exists for the given bank and prog. - */ -int -fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) -{ - return fluid_synth_activate_tuning (synth, chan, bank, prog, FALSE); + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(new_tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); } /** @@ -4407,90 +5869,84 @@ fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param apply TRUE to apply tuning change to active notes, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 * - * NOTE: A default equal tempered scale will be created, if no tuning exists + * @note A default equal tempered scale will be created, if no tuning exists * on the given bank and prog. */ int -fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, +fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, int apply) { - //fluid_event_queue_elem_t *event; - //fluid_event_queue_t *queue; - fluid_tuning_t* tuning; - int retval = FLUID_OK; + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); - //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); - fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); - fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + tuning = fluid_synth_get_tuning(synth, bank, prog); - tuning = fluid_synth_get_tuning (synth, bank, prog); + /* If no tuning exists, create a new default tuning. We do this, so that + * it can be replaced later, if any changes are made. */ + if(!tuning) + { + tuning = new_fluid_tuning("Unnamed", bank, prog); - /* If no tuning exists, create a new default tuning. We do this, so that - * it can be replaced later, if any changes are made. */ - if (!tuning) - { - tuning = new_fluid_tuning ("Unnamed", bank, prog); - if (tuning) fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, FALSE); - } + if(tuning) + { + fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE); + } + } - if (tuning) fluid_tuning_ref (tuning); /* ++ ref for outside of lock */ + if(tuning) + { + fluid_tuning_ref(tuning); /* ++ ref for outside of lock */ + } - if (!tuning) - FLUID_API_RETURN(FLUID_FAILED); + if(!tuning) + { + FLUID_API_RETURN(FLUID_FAILED); + } - fluid_tuning_ref (tuning); /* ++ ref new tuning for following function */ - retval = fluid_synth_set_tuning_LOCAL (synth, chan, tuning, apply); + fluid_tuning_ref(tuning); /* ++ ref new tuning for following function */ + retval = fluid_synth_set_tuning_LOCAL(synth, chan, tuning, apply); - fluid_tuning_unref (tuning, 1); /* -- unref for outside of lock */ + fluid_tuning_unref(tuning, 1); /* -- unref for outside of lock */ - FLUID_API_RETURN(retval); + FLUID_API_RETURN(retval); } /* Local synthesis thread set tuning function (takes over tuning reference) */ static int -fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, - fluid_tuning_t *tuning, int apply) +fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply) { - fluid_tuning_t *old_tuning; - fluid_channel_t *channel; - - channel = synth->channel[chan]; + fluid_tuning_t *old_tuning; + fluid_channel_t *channel; - old_tuning = fluid_channel_get_tuning (channel); - fluid_channel_set_tuning (channel, tuning); /* !! Takes over callers reference */ + channel = synth->channel[chan]; - if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); + old_tuning = fluid_channel_get_tuning(channel); + fluid_channel_set_tuning(channel, tuning); /* !! Takes over callers reference */ - /* Send unref old tuning event */ - if (old_tuning) - { - fluid_tuning_unref (old_tuning, 1); - } + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + /* Send unref old tuning event */ + if(old_tuning) + { + fluid_tuning_unref(old_tuning, 1); + } - return FLUID_OK; -} -/** - * Clear tuning scale on a MIDI channel (set it to the default well-tempered scale). - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @return FLUID_OK on success, FLUID_FAILED otherwise - * - * NOTE: This function does NOT activate tuning change in realtime, use - * fluid_synth_deactivate_tuning() instead to specify whether tuning change - * should cause existing notes to update. - */ -int -fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) -{ - return fluid_synth_deactivate_tuning (synth, chan, FALSE); + return FLUID_OK; } /** @@ -4498,19 +5954,19 @@ fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param apply TRUE to apply tuning change to active notes, FALSE otherwise - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int -fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply) +fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply) { - int retval = FLUID_OK; + int retval = FLUID_OK; - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - retval = fluid_synth_set_tuning_LOCAL (synth, chan, NULL, apply); + retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply); - FLUID_API_RETURN(retval); + FLUID_API_RETURN(retval); } /** @@ -4518,12 +5974,12 @@ fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply) * @param synth FluidSynth instance */ void -fluid_synth_tuning_iteration_start(fluid_synth_t* synth) +fluid_synth_tuning_iteration_start(fluid_synth_t *synth) { - fluid_return_if_fail (synth != NULL); - fluid_synth_api_enter(synth); - fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER (0)); - fluid_synth_api_exit(synth); + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER(0)); + fluid_synth_api_exit(synth); } /** @@ -4534,526 +5990,1039 @@ fluid_synth_tuning_iteration_start(fluid_synth_t* synth) * @return 1 if tuning iteration advanced, 0 if no more tunings */ int -fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) +fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog) +{ + void *pval; + int b = 0, p = 0; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_return_val_if_fail(bank != NULL, 0); + fluid_return_val_if_fail(prog != NULL, 0); + fluid_synth_api_enter(synth); + + /* Current tuning iteration stored as: bank << 8 | program */ + pval = fluid_private_get(synth->tuning_iter); + p = FLUID_POINTER_TO_INT(pval); + b = (p >> 8) & 0xFF; + p &= 0xFF; + + if(!synth->tuning) + { + FLUID_API_RETURN(0); + } + + for(; b < 128; b++, p = 0) + { + if(synth->tuning[b] == NULL) + { + continue; + } + + for(; p < 128; p++) + { + if(synth->tuning[b][p] == NULL) + { + continue; + } + + *bank = b; + *prog = p; + + if(p < 127) + { + fluid_private_set(synth->tuning_iter, + FLUID_INT_TO_POINTER(b << 8 | (p + 1))); + } + else + { + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8)); + } + + FLUID_API_RETURN(1); + } + } + + FLUID_API_RETURN(0); +} + +/** + * Get the entire note tuning for a given MIDI bank and program. + * @param synth FluidSynth instance + * @param bank MIDI bank number of tuning + * @param prog MIDI program number of tuning + * @param name Location to store tuning name or NULL to ignore + * @param len Maximum number of chars to store to 'name' (including NULL byte) + * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) + * @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise + */ +int +fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch) +{ + fluid_tuning_t *tuning; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + tuning = fluid_synth_get_tuning(synth, bank, prog); + + if(tuning) + { + if(name) + { + FLUID_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)); + } + } + + FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED); +} + +/** + * Get settings assigned to a synth. + * @param synth FluidSynth instance + * @return FluidSynth settings which are assigned to the synth + */ +fluid_settings_t * +fluid_synth_get_settings(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, NULL); + + return synth->settings; +} + +/** + * Set a SoundFont generator (effect) value on a MIDI channel in real-time. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset or absolute generator value to assign to the MIDI channel + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This function allows for setting all effect parameters in real time on a + * MIDI channel. Setting absolute to non-zero will cause the value to override + * any generator values set in the instruments played on the MIDI channel. + * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont + * generator parameters and valid ranges. + */ +int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value) +{ + return fluid_synth_set_gen2(synth, chan, param, value, FALSE, FALSE); +} + +/** + * Set a SoundFont generator (effect) value on a MIDI channel in real-time. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset or absolute generator value to assign to the MIDI channel + * @param absolute FALSE to assign a relative value, TRUE to assign an absolute value + * @param normalized FALSE if value is specified in the native units of the generator, + * TRUE to take the value as a 0.0-1.0 range and apply it to the valid + * generator effect range (scaled and shifted as necessary). + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This function allows for setting all effect parameters in real time on a + * MIDI channel. Setting absolute to non-zero will cause the value to override + * any generator values set in the instruments played on the MIDI channel. + * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont + * generator parameters and valid ranges. + */ +int +fluid_synth_set_gen2(fluid_synth_t *synth, int chan, int param, + float value, int absolute, int normalized) +{ + float v; + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + v = normalized ? fluid_gen_scale(param, value) : value; + + fluid_synth_set_gen_LOCAL(synth, chan, param, v, absolute); + + FLUID_API_RETURN(FLUID_OK); +} + +/* Synthesis thread local set gen function */ +static void +fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value, + int absolute) +{ + fluid_voice_t *voice; + int i; + + fluid_channel_set_gen(synth->channel[chan], param, value, absolute); + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_set_param(voice, param, value, absolute); + } + } +} + +/** + * Get generator value assigned to a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @return Current generator value assigned to MIDI channel + */ +float +fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param) +{ + float result; + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + result = fluid_channel_get_gen(synth->channel[chan], param); + FLUID_API_RETURN(result); +} + +/** + * Handle MIDI event from MIDI router, used as a callback function. + * @param data FluidSynth instance + * @param event MIDI event to handle + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +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 CHANNEL_PRESSURE: + return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); + + case KEY_PRESSURE: + return fluid_synth_key_pressure(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_value(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); + + case MIDI_SYSEX: + return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE); + + case MIDI_TEXT: + case MIDI_LYRIC: + case MIDI_SET_TEMPO: + return FLUID_OK; + } + + return FLUID_FAILED; +} + +/** + * Create and start voices using a preset and a MIDI note on event. + * @param synth FluidSynth instance + * @param id Voice group ID to use (can be used with fluid_synth_stop()). + * @param preset Preset to synthesize + * @param audio_chan Unused currently, set to 0 + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity number (1-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +int +fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset, + int audio_chan, int chan, int key, int vel) +{ + int result; + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + synth->storeid = id; + result = fluid_preset_noteon(preset, synth, chan, key, vel); + FLUID_API_RETURN(result); +} + +/** + * Stop notes for a given note event voice ID. + * @param synth FluidSynth instance + * @param id Voice note event ID + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned + * if no matching voice note event ID was found. Versions after 1.1.0 only + * return #FLUID_FAILED if an error occurs. + */ +int +fluid_synth_stop(fluid_synth_t *synth, unsigned int id) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_stop_LOCAL(synth, id); + result = FLUID_OK; + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_stop */ +static void +fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id)) + { + fluid_voice_noteoff(voice); + } + } +} + +/** + * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract + * \c offset from any bank number when assigning instruments. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param offset Bank offset value to apply to all instruments + * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise + */ +int +fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfont_id) + { + sfont->bankofs = offset; + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Get bank offset of a loaded SoundFont. + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @return SoundFont bank offset value + */ +int +fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id) { - void *pval; - int b = 0, p = 0; + fluid_sfont_t *sfont; + fluid_list_t *list; + int offset = 0; - fluid_return_val_if_fail (synth != NULL, 0); - fluid_return_val_if_fail (bank != NULL, 0); - fluid_return_val_if_fail (prog != NULL, 0); - fluid_synth_api_enter(synth); - - /* Current tuning iteration stored as: bank << 8 | program */ - pval = fluid_private_get (synth->tuning_iter); - p = FLUID_POINTER_TO_INT (pval); - b = (p >> 8) & 0xFF; - p &= 0xFF; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); - if (!synth->tuning) - { - FLUID_API_RETURN(0); - } + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); - for (; b < 128; b++, p = 0) - { - if (synth->tuning[b] == NULL) continue; + if(fluid_sfont_get_id(sfont) == sfont_id) + { + offset = sfont->bankofs; + break; + } + } - for (; p < 128; p++) + if(!list) { - if (synth->tuning[b][p] == NULL) continue; + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(0); + } - *bank = b; - *prog = p; + FLUID_API_RETURN(offset); +} - if (p < 127) fluid_private_set (synth->tuning_iter, - FLUID_INT_TO_POINTER (b << 8 | (p + 1))); - else fluid_private_set (synth->tuning_iter, - FLUID_INT_TO_POINTER ((b + 1) << 8)); +void +fluid_synth_api_enter(fluid_synth_t *synth) +{ + if(synth->use_mutex) + { + fluid_rec_mutex_lock(synth->mutex); + } - FLUID_API_RETURN(1); + if(!synth->public_api_count) + { + fluid_synth_check_finished_voices(synth); } - } - FLUID_API_RETURN(0); + synth->public_api_count++; } -/** - * Get the entire note tuning for a given MIDI bank and program. - * @param synth FluidSynth instance - * @param bank MIDI bank number of tuning - * @param prog MIDI program number of tuning - * @param name Location to store tuning name or NULL to ignore - * @param len Maximum number of chars to store to 'name' (including NULL byte) - * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) - * @return FLUID_OK if matching tuning was found, FLUID_FAILED otherwise - */ -int -fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch) +void fluid_synth_api_exit(fluid_synth_t *synth) { - fluid_tuning_t* tuning; - - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - tuning = fluid_synth_get_tuning (synth, bank, prog); + synth->public_api_count--; - if (tuning) - { - if (name) + if(!synth->public_api_count) { - snprintf (name, len - 1, "%s", fluid_tuning_get_name (tuning)); - name[len - 1] = 0; /* make sure the string is null terminated */ + fluid_rvoice_eventhandler_flush(synth->eventhandler); } - if (pitch) - FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double)); - } + if(synth->use_mutex) + { + fluid_rec_mutex_unlock(synth->mutex); + } - FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED); } /** - * Get settings assigned to a synth. + * Set midi channel type * @param synth FluidSynth instance - * @return FluidSynth settings which are assigned to the synth + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param type MIDI channel type (#fluid_midi_channel_type) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 */ -fluid_settings_t * -fluid_synth_get_settings(fluid_synth_t* synth) +int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) { - fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - return synth->settings; + synth->channel[chan]->channel_type = type; + + FLUID_API_RETURN(FLUID_OK); } +#ifdef LADSPA /** - * Convenience function to set a string setting of a synth. + * Return the LADSPA effects instance used by FluidSynth + * * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param str Value to assign to the setting - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active */ -int -fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str) +fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, NULL); - return fluid_settings_setstr(synth->settings, name, str); + return synth->ladspa_fx; } +#endif /** - * Convenience function to duplicate a string setting of a synth. + * Configure a general-purpose IIR biquad filter. + * + * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. + * By default this filter is off (#FLUID_IIR_DISABLED). + * * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param str Location to store a pointer to the newly allocated string value - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @param type Type of the IIR filter to use (see #fluid_iir_filter_type) + * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags) * - * The returned string is owned by the caller and should be freed with free() - * when finished with it. + * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED */ -int -fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str) +int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); - fluid_return_val_if_fail (str != NULL, FLUID_FAILED); - - return fluid_settings_dupstr(synth->settings, name, str); -} + int i; + fluid_voice_t *voice; -/** - * Convenience function to set a floating point setting of a synth. - * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param val Value to assign to the setting - * @return FLUID_OK on success, FLUID_FAILED otherwise - */ -int -fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val) -{ - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED); - return fluid_settings_setnum(synth->settings, name, val); -} + fluid_synth_api_enter(synth); -/** - * Convenience function to get a floating point setting of a synth. - * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param val Location to store the current value of the setting - * @return FLUID_OK on success, FLUID_FAILED otherwise - */ -int -fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val) -{ - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + synth->custom_filter_type = type; + synth->custom_filter_flags = flags; - return fluid_settings_getnum(synth->settings, name, val); -} + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; -/** - * Convenience function to set an integer setting of a synth. - * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param val Value to assign to the setting - * @return FLUID_OK on success, FLUID_FAILED otherwise - */ -int -fluid_synth_setint(fluid_synth_t* synth, const char* name, int val) -{ - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_voice_set_custom_filter(voice, type, flags); + } - return fluid_settings_setint(synth->settings, name, val); + FLUID_API_RETURN(FLUID_OK); } /** - * Convenience function to get an integer setting of a synth. + * Set the important channels for voice overflow priority calculation. + * * @param synth FluidSynth instance - * @param name Name of setting parameter - * @param val Location to store the current value of the setting - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @param channels comma-separated list of channel numbers + * @return #FLUID_OK on success, otherwise #FLUID_FAILED */ -int -fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val) +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels) { - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + int i; + int retval = FLUID_FAILED; + int *values = NULL; + int num_values; + fluid_overflow_prio_t *scores; - return fluid_settings_getint(synth->settings, name, val); -} + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); -/** - * Set a SoundFont generator (effect) value on a MIDI channel in real-time. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param param SoundFont generator ID (#fluid_gen_type) - * @param value Offset generator value to assign to the MIDI channel - * @return FLUID_OK on success, FLUID_FAILED otherwise - * - * Parameter numbers and ranges are described in the SoundFont 2.01 - * specification PDF, paragraph 8.1.3, page 48. See #fluid_gen_type. - */ -int -fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) -{ - fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + scores = &synth->overflow; - fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE); + if(scores->num_important_channels < synth->midi_channels) + { + scores->important_channels = FLUID_REALLOC(scores->important_channels, + sizeof(*scores->important_channels) * synth->midi_channels); - FLUID_API_RETURN(FLUID_OK); -} + if(scores->important_channels == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } -/* Synthesis thread local set gen function */ -static void -fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value, - int absolute) -{ - fluid_voice_t* voice; - int i; + scores->num_important_channels = synth->midi_channels; + } - fluid_channel_set_gen (synth->channel[chan], param, value, absolute); + FLUID_MEMSET(scores->important_channels, FALSE, + sizeof(*scores->important_channels) * scores->num_important_channels); - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if(channels != NULL) + { + values = FLUID_ARRAY(int, synth->midi_channels); - if (voice->chan == chan) - fluid_voice_set_param (voice, param, value, absolute); - } -} + if(values == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } -/** - * Set a SoundFont generator (effect) value on a MIDI channel in real-time. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param param SoundFont generator ID (#fluid_gen_type) - * @param value Offset or absolute generator value to assign to the MIDI channel - * @param absolute 0 to assign a relative value, non-zero to assign an absolute value - * @param normalized 0 if value is specified in the native units of the generator, - * non-zero to take the value as a 0.0-1.0 range and apply it to the valid - * generator effect range (scaled and shifted as necessary). - * @return FLUID_OK on success, FLUID_FAILED otherwise - * @since 1.1.0 - * - * This function allows for setting all effect parameters in real time on a - * MIDI channel. Setting absolute to non-zero will cause the value to override - * any generator values set in the instruments played on the MIDI channel. - * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont - * generator parameters and valid ranges. - */ -int -fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, - float value, int absolute, int normalized) -{ - float v; - fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /* Every channel given in the comma-separated list of channel numbers + * is set to TRUE, i.e. flagging it as "important". Channel numbers are + * 1-based. */ + num_values = fluid_settings_split_csv(channels, values, synth->midi_channels); - v = normalized ? fluid_gen_scale(param, value) : value; + for(i = 0; i < num_values; i++) + { + if(values[i] > 0 && values[i] <= synth->midi_channels) + { + scores->important_channels[values[i] - 1] = TRUE; + } + } + } - fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute); + retval = FLUID_OK; - FLUID_API_RETURN(FLUID_OK); +exit: + FLUID_FREE(values); + return retval; } -/** - * Get generator value assigned to a MIDI channel. - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param param SoundFont generator ID (#fluid_gen_type) - * @return Current generator value assigned to MIDI channel +/* + * Handler for synth.overflow.important-channels setting. */ -float -fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value) { - float result; - fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); + fluid_synth_t *synth = (fluid_synth_t *)data; - result = fluid_channel_get_gen(synth->channel[chan], param); - FLUID_API_RETURN(result); + fluid_synth_api_enter(synth); + fluid_synth_set_important_channels(synth, value); + fluid_synth_api_exit(synth); } + +/** API legato mode *********************************************************/ + /** - * Assign a MIDI router to a synth. - * @param synth FluidSynth instance - * @param router MIDI router to assign to the synth + * Sets the legato mode of a channel. * - * NOTE: This should only be done once and prior to using the synth. + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is invalid. */ -void -fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router) +int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode) { - fluid_return_if_fail (synth != NULL); - fluid_synth_api_enter(synth); - - synth->midi_router = router; - fluid_synth_api_exit(synth); -}; + /* checks parameters first */ + fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->legatomode = legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} /** - * Handle MIDI event from MIDI router, used as a callback function. - * @param data FluidSynth instance - * @param event MIDI event to handle - * @return FLUID_OK on success, FLUID_FAILED otherwise + * Gets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is NULL. */ -int -fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) +int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode) { - 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 CHANNEL_PRESSURE: - return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); + /* checks parameters first */ + fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * legatomode = synth->channel[chan]->legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} - case PITCH_BEND: - return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event)); +/** API portamento mode *********************************************************/ - case MIDI_SYSTEM_RESET: - return fluid_synth_system_reset(synth); - case MIDI_SYSEX: - return fluid_synth_sysex (synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE); - } - return FLUID_FAILED; +/** + * Sets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is invalid. + */ +int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, + int portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->portamentomode = portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); } /** - * Create and start voices using a preset and a MIDI note on event. - * @param synth FluidSynth instance - * @param id Voice group ID to use (can be used with fluid_synth_stop()). - * @param preset Preset to synthesize - * @param audio_chan Unused currently, set to 0 - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param key MIDI note number (0-127) - * @param vel MIDI velocity number (1-127) - * @return FLUID_OK on success, FLUID_FAILED otherwise + * Gets the portamento mode of a channel. * - * NOTE: Should only be called from within synthesis thread, which includes - * SoundFont loader preset noteon method. + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is NULL. + */ +int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, + int *portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * portamentomode = synth->channel[chan]->portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API breath mode *********************************************************/ + +/** + * Sets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. */ -int -fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, - int audio_chan, int chan, int key, int vel) +int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode) { - int result; - fluid_return_val_if_fail (preset != NULL, FLUID_FAILED); - fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); - fluid_return_val_if_fail (vel >= 1 && vel <= 127, FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - synth->storeid = id; - result = fluid_preset_noteon (preset, synth, chan, key, vel); - FLUID_API_RETURN(result); + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + fluid_channel_set_breath_info(synth->channel[chan], breathmode); + /**/ + FLUID_API_RETURN(FLUID_OK); } /** - * Stop notes for a given note event voice ID. - * @param synth FluidSynth instance - * @param id Voice note event ID - * @return FLUID_OK on success, FLUID_FAILED otherwise + * Gets the breath mode of a channel. * - * NOTE: In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned - * if no matching voice note event ID was found. Versions after 1.1.0 only - * return #FLUID_FAILED if an error occurs. + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a breathmode is NULL. */ -int -fluid_synth_stop(fluid_synth_t* synth, unsigned int id) +int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode) { - int result; - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - fluid_synth_stop_LOCAL (synth, id); - result = FLUID_OK; - FLUID_API_RETURN(result); + /* checks parameters first */ + fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * breathmode = fluid_channel_get_breath_info(synth->channel[chan]); + /**/ + FLUID_API_RETURN(FLUID_OK); } -/* Local synthesis thread variant of fluid_synth_stop */ +/** API Poly/mono mode ******************************************************/ + +/* + * Resets a basic channel group of MIDI channels. + * @param synth the synth instance. + * @param chan the beginning channel of the group. + * @param nbr_chan the number of channel in the group. +*/ static void -fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id) +fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan) { - fluid_voice_t* voice; - int i; - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + int i; - if (_ON(voice) && (fluid_voice_get_id (voice) == id)) - fluid_voice_noteoff(voice); - } + for(i = chan; i < chan + nbr_chan; i++) + { + fluid_channel_reset_basic_channel_info(synth->channel[i]); + synth->channel[i]->mode_val = 0; + } } /** - * Offset the bank numbers of a loaded SoundFont. - * @param synth FluidSynth instance - * @param sfont_id ID of a loaded SoundFont - * @param offset Bank offset value to apply to all instruments + * Disables and unassigns all channels from a basic channel group. + * + * @param synth The synth instance. + * @param chan The basic channel of the group to reset or -1 to reset all channels. + * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset()) + * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY. + * All other channels belong to this basic channel group. Make sure to call this function before + * setting any custom basic channel setup. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a chan isn't a basic channel. */ -int -fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) +int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan) { - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; - - fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_synth_api_enter(synth); - - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + int nbr_chan; - if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) + /* checks parameters first */ + if(chan < 0) { - sfont_info->bankofs = offset; - break; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + /* The range is all MIDI channels from 0 to MIDI channel count -1 */ + chan = 0; /* beginning chan */ + nbr_chan = synth->midi_channels; /* MIDI Channels number */ } - } + else + { + FLUID_API_ENTRY_CHAN(FLUID_FAILED); - if (!list) - { - FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); - FLUID_API_RETURN(FLUID_FAILED); - } + /* checks if chan is a basic channel */ + if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)) + { + FLUID_API_RETURN(FLUID_FAILED); + } - FLUID_API_RETURN(FLUID_OK); + /* The range is all MIDI channels in the group from chan */ + nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */ + } + + /* resets the range of MIDI channels */ + fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan); + FLUID_API_RETURN(FLUID_OK); } /** - * Get bank offset of a loaded SoundFont. - * @param synth FluidSynth instance - * @param sfont_id ID of a loaded SoundFont - * @return SoundFont bank offset value + * Checks if a new basic channel group overlaps the next basic channel group. + * + * On success the function returns the possible number of channel for this + * new basic channel group. + * The function fails if the new group overlaps the next basic channel group. + * + * @param see fluid_synth_set_basic_channel. + * @return + * - On success, the effective number of channels for this new basic channel group, + * #FLUID_FAILED otherwise. + * - #FLUID_FAILED + * - \a val has a number of channels overlapping next basic channel group or been + * above MIDI channel count. */ -int -fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) +static int +fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val) { - fluid_sfont_info_t *sfont_info; - fluid_list_t *list; - int offset = 0; + int i, n_chan = synth->midi_channels; /* MIDI Channels count */ + int real_val = val; /* real number of channels in the group */ + + /* adjusts val range */ + if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY) + { + real_val = 1; /* mode poly omnioff implies a group of only one channel.*/ + } + else if(val == 0) + { + /* mode poly omnion (0), mono omnion (1), mono omni off (3) */ + /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/ + real_val = n_chan - basicchan; + } + /* checks if val range is above MIDI channel count */ + else if(basicchan + val > n_chan) + { + return FLUID_FAILED; + } - fluid_return_val_if_fail (synth != NULL, 0); - fluid_synth_api_enter(synth); + /* checks if this basic channel group overlaps next basic channel group */ + for(i = basicchan + 1; i < basicchan + real_val; i++) + { + if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC) + { + /* A value of 0 for val means all possible channels from basicchan to + to the next basic channel -1 (if any). + When i reachs the next basic channel group, real_val will be + limited if it is possible */ + if(val == 0) + { + /* limitation of real_val */ + real_val = i - basicchan; + break; + } + + /* overlap with the next basic channel group */ + return FLUID_FAILED; + } + } - for (list = synth->sfont_info; list; list = fluid_list_next(list)) { - sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + return real_val; +} - if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) +/** + * Sets a new basic channel group only. The function doesn't allow to change an + * existing basic channel. + * + * The function fails if any channel overlaps any existing basic channel group. + * To make room if necessary, basic channel groups can be cleared using + * fluid_synth_reset_basic_channel(). + * + * @param synth the synth instance. + * @param chan the basic Channel number (0 to MIDI channel count-1). + * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes). + * @param val number of channels in the group. + * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY, + * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value + * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any) + * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY + * as this mode implies a group of only one channel. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a mode is invalid. + * - \a val has a number of channels overlapping another basic channel group or been + * above MIDI channel count. + * - When the function fails, any existing basic channels aren't modified. + */ +int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val) +{ + /* check parameters */ + fluid_return_val_if_fail(mode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /**/ + if(val > 0 && chan + val > synth->midi_channels) { - offset = sfont_info->bankofs; - break; + FLUID_API_RETURN(FLUID_FAILED); } - } - if (!list) - { - FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); - FLUID_API_RETURN(0); - } + /* Checks if there is an overlap with the next basic channel */ + val = fluid_synth_check_next_basic_channel(synth, chan, mode, val); - FLUID_API_RETURN(offset); -} + if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) + { + /* overlap with the next or previous channel group */ + FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan); + FLUID_API_RETURN(FLUID_FAILED); + } -void -fluid_synth_api_enter(fluid_synth_t* synth) -{ - if (synth->use_mutex) { - fluid_rec_mutex_lock(synth->mutex); - } - if (!synth->public_api_count) { - fluid_synth_check_finished_voices(synth); - } - synth->public_api_count++; + /* sets a new basic channel group */ + fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val); + /**/ + FLUID_API_RETURN(FLUID_OK); } -void fluid_synth_api_exit(fluid_synth_t* synth) +/* + * Local version of fluid_synth_set_basic_channel(), called internally: + * - by fluid_synth_set_basic_channel() to set a new basic channel group. + * - during creation new_fluid_synth() or on CC reset to set a default basic channel group. + * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group. + * + * @param see fluid_synth_set_basic_channel() +*/ +static void +fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val) { - synth->public_api_count--; - if (!synth->public_api_count) { - fluid_rvoice_eventhandler_flush(synth->eventhandler); - } + int i; - if (synth->use_mutex) { - fluid_rec_mutex_unlock(synth->mutex); - } - -} + /* sets the basic channel group */ + for(i = basicchan; i < basicchan + val; i++) + { + int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ + int new_val; + /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */ + fluid_synth_all_notes_off_LOCAL(synth, i); + + if(i == basicchan) + { + new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */ + new_val = val; /* number of channels in the group */ + } + else + { + new_val = 0; /* val is 0 for other channel than basic channel */ + } + /* Channel is enabled */ + new_mode |= FLUID_CHANNEL_ENABLED; + /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */ + fluid_channel_set_basic_channel_info(synth->channel[i], new_mode); + synth->channel[i]->mode_val = new_val; + } +} /** - * Set midi channel type - * @param synth FluidSynth instance - * @param chan MIDI channel number (0 to MIDI channel count - 1) - * @param type CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM - * @return FLUID_OK on success, FLUID_FAILED otherwise - * @since 1.1.4 + * Searchs a previous basic channel starting from chan. + * + * @param synth the synth instance. + * @param chan starting index of the search (including chan). + * @return index of the basic channel if found , FLUID_FAILED otherwise. */ -int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type) +static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan) { - fluid_return_val_if_fail ((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED); - FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - synth->channel[chan]->channel_type = type; + for(; chan >= 0; chan--) + { + /* searchs previous basic channel */ + if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) + { + /* chan is the previous basic channel */ + return chan; + } + } - FLUID_API_RETURN(FLUID_OK); + return FLUID_FAILED; } +/** + * Returns poly mono mode information of any MIDI channel. + * + * @param synth the synth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled. + * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled. + * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled. + * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL + * the corresponding information isn't returned. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_out, + int *val_out) +{ + int basic_chan = FLUID_FAILED; + int mode = FLUID_FAILED; + int val = FLUID_FAILED; + + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) && + /* chan is enabled , we search the basic channel chan belongs to */ + (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED) + { + mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK; + val = synth->channel[basic_chan]->mode_val; + } + + /* returns the informations if they are requested */ + if(basic_chan_out) + { + * basic_chan_out = basic_chan; + } + + if(mode_out) + { + * mode_out = mode; + } + + if(val_out) + { + * val_out = val; + } + + FLUID_API_RETURN(FLUID_OK); +} diff --git a/libs/fluidsynth/src/fluid_synth.h b/libs/fluidsynth/src/fluid_synth.h index 019a8e0d55..96dc545746 100644 --- a/libs/fluidsynth/src/fluid_synth.h +++ b/libs/fluidsynth/src/fluid_synth.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -28,18 +28,11 @@ * INCLUDES */ -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include "fluidsynth_priv.h" -#include "fluid_event_queue.h" +#include "fluid_sys.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" #include "fluid_rvoice_event.h" /*************************************************************** @@ -51,23 +44,21 @@ #define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */ -#if defined(WITH_FLOAT) -#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT -#else -#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE -#endif +#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */ +#define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */ +#define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */ +#define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */ +#define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */ +#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */ +#define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */ +#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */ +#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */ /*************************************************************** * * ENUM */ -/*enum fluid_loop { - FLUID_UNLOOPED = 0, - FLUID_LOOP_DURING_RELEASE = 1, - FLUID_NOTUSED = 2, - FLUID_LOOP_UNTIL_RELEASE = 3 -};*/ /** * Bank Select MIDI message styles. Default style is GS. @@ -82,26 +73,15 @@ enum fluid_midi_bank_select enum fluid_synth_status { - FLUID_SYNTH_CLEAN, - FLUID_SYNTH_PLAYING, - FLUID_SYNTH_QUIET, - FLUID_SYNTH_STOPPED + FLUID_SYNTH_CLEAN, + FLUID_SYNTH_PLAYING, + FLUID_SYNTH_QUIET, + FLUID_SYNTH_STOPPED }; #define SYNTH_REVERB_CHANNEL 0 #define SYNTH_CHORUS_CHANNEL 1 -/** - * Structure used for sfont_info field in #fluid_synth_t for each loaded - * SoundFont with the SoundFont instance and additional fields. - */ -typedef struct _fluid_sfont_info_t { - fluid_sfont_t *sfont; /**< Loaded SoundFont */ - fluid_synth_t *synth; /**< Parent synth */ - int refcount; /**< SoundFont reference count (0 if no presets referencing it) */ - int bankofs; /**< Bank offset */ -} fluid_sfont_info_t; - /* * fluid_synth_t * @@ -113,125 +93,141 @@ typedef struct _fluid_sfont_info_t { * ticks_since_start - atomic, set by rendering thread only * cpu_load - atomic, set by rendering thread only * cur, curmax, dither_index - used by rendering thread only - * LADSPA_FxUnit - same instance copied in rendering thread. Synchronising handled internally (I think...?). + * ladspa_fx - same instance copied in rendering thread. Synchronising handled internally. * */ struct _fluid_synth_t { - fluid_rec_mutex_t mutex; /**< Lock for public API */ - int use_mutex; /**< Use mutex for all public API functions? */ - int public_api_count; /**< How many times the mutex is currently locked */ - - fluid_settings_t* settings; /**< the synthesizer settings */ - int device_id; /**< Device ID used for SYSEX messages */ - int polyphony; /**< Maximum polyphony */ - int with_reverb; /**< Should the synth use the built-in reverb unit? */ - int with_chorus; /**< Should the synth use the built-in chorus unit? */ - int verbose; /**< Turn verbose mode on? */ - int 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 bank_select; /**< the style of Bank Select MIDI messages */ - int audio_channels; /**< the number of audio channels (1 channel=left+right) */ - int audio_groups; /**< the number of (stereo) 'sub'groups from the synth. + fluid_rec_mutex_t mutex; /**< Lock for public API */ + int use_mutex; /**< Use mutex for all public API functions? */ + int public_api_count; /**< How many times the mutex is currently locked */ + + fluid_settings_t *settings; /**< the synthesizer settings */ + int device_id; /**< Device ID used for SYSEX messages */ + int polyphony; /**< Maximum polyphony */ + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int verbose; /**< Turn verbose mode on? */ + double sample_rate; /**< The sample rate */ + int midi_channels; /**< the number of MIDI channels (>= 16) */ + int bank_select; /**< the style of Bank Select MIDI messages */ + 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) */ - int state; /**< the synthesizer state */ - unsigned int ticks_since_start; /**< the number of audio samples since the start */ - unsigned int start; /**< the start in msec, as returned by system clock */ - fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */ - - fluid_list_t *loaders; /**< the SoundFont loaders */ - fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ - fluid_hashtable_t *sfont_hash; /**< Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */ - unsigned int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ - - float gain; /**< master gain */ - fluid_channel_t** channel; /**< the channels */ - int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ - fluid_voice_t** voice; /**< the synthesis voices */ - int active_voice_count; /**< count of active voices */ - unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ - unsigned int storeid; - fluid_rvoice_eventhandler_t* eventhandler; - - float reverb_roomsize; /**< Shadow of reverb roomsize */ - float reverb_damping; /**< Shadow of reverb damping */ - float reverb_width; /**< Shadow of reverb width */ - float reverb_level; /**< Shadow of reverb level */ - - int chorus_nr; /**< Shadow of chorus number */ - float chorus_level; /**< Shadow of chorus level */ - float chorus_speed; /**< Shadow of chorus speed */ - float chorus_depth; /**< Shadow of chorus depth */ - int chorus_type; /**< Shadow of chorus type */ - - int cur; /**< the current sample in the audio buffers to be output */ - int curmax; /**< current amount of samples present in the audio buffers */ - int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ - - char outbuf[256]; /**< buffer for message output */ - float cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ - - fluid_tuning_t*** tuning; /**< 128 banks of 128 programs for the tunings */ - fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ - - fluid_midi_router_t* midi_router; /**< The midi router. Could be done nicer. */ - fluid_sample_timer_t* sample_timers; /**< List of timers triggered before a block is processed */ - unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ - - int cores; /**< Number of CPU cores (1 by default) */ + int effects_channels; /**< the number of effects channels (>= 2) */ + int effects_groups; /**< the number of effects units (>= 1) */ + int state; /**< the synthesizer state */ + fluid_atomic_uint_t ticks_since_start; /**< the number of audio samples since the start */ + unsigned int start; /**< the start in msec, as returned by system clock */ + fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */ + + fluid_list_t *loaders; /**< the SoundFont loaders */ + fluid_list_t *sfont; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ + int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ + + float gain; /**< master gain */ + fluid_channel_t **channel; /**< the channels */ + int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ + fluid_voice_t **voice; /**< the synthesis voices */ + int active_voice_count; /**< count of active voices */ + unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ + unsigned int storeid; + int fromkey_portamento; /**< fromkey portamento */ + fluid_rvoice_eventhandler_t *eventhandler; + + double reverb_roomsize; /**< Shadow of reverb roomsize */ + double reverb_damping; /**< Shadow of reverb damping */ + double reverb_width; /**< Shadow of reverb width */ + double reverb_level; /**< Shadow of reverb level */ + + int chorus_nr; /**< Shadow of chorus number */ + double chorus_level; /**< Shadow of chorus level */ + double chorus_speed; /**< Shadow of chorus speed */ + double chorus_depth; /**< Shadow of chorus depth */ + int chorus_type; /**< Shadow of chorus type */ + + int cur; /**< the current sample in the audio buffers to be output */ + int curmax; /**< current amount of samples present in the audio buffers */ + int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ + + fluid_atomic_float_t cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ + + fluid_tuning_t ***tuning; /**< 128 banks of 128 programs for the tunings */ + fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ + + fluid_sample_timer_t *sample_timers; /**< List of timers triggered before a block is processed */ + unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ + + int cores; /**< Number of CPU cores (1 by default) */ + + fluid_mod_t *default_mod; /**< the (dynamic) list of default modulators */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */ + fluid_ladspa_fx_t *ladspa_fx; /**< Effects unit for LADSPA support */ #endif + enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */ + enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */ }; -int fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str); -int fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str); -int fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val); -int fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val); -int fluid_synth_setint(fluid_synth_t* synth, const char* name, int val); -int fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val); - -fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, - unsigned int banknum, - unsigned int prognum); -void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont); - - -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_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); - -void fluid_synth_print_voice(fluid_synth_t* synth); - -void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr); - -int fluid_synth_reset_reverb(fluid_synth_t* synth); -int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); -int fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, +/** + * Type definition of the synthesizer's audio callback function. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param out1 Array to store left channel of audio to + * @param loff Offset index in 'out1' for first sample + * @param lincr Increment between samples stored to 'out1' + * @param out2 Array to store right channel of audio to + * @param roff Offset index in 'out2' for first sample + * @param rincr Increment between samples stored to 'out2' + */ +typedef int (*fluid_audio_callback_t)(fluid_synth_t *synth, int len, + void *out1, int loff, int lincr, + void *out2, int roff, int rincr); + +fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, + int banknum, + int prognum); +void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont); + +void fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); + +int fluid_synth_reset_reverb(fluid_synth_t *synth); +int fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num); +int fluid_synth_set_reverb_full(fluid_synth_t *synth, int set, double roomsize, double damping, double width, double level); -int fluid_synth_reset_chorus(fluid_synth_t* synth); -int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, +int fluid_synth_reset_chorus(fluid_synth_t *synth); +int fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, double speed, double depth_ms, int type); -fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data); -int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer); +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data); +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer); -void fluid_synth_api_enter(fluid_synth_t* synth); -void fluid_synth_api_exit(fluid_synth_t* synth); -void fluid_synth_process_event_queue(fluid_synth_t* synth); +void fluid_synth_process_event_queue(fluid_synth_t *synth); +int fluid_synth_set_gen2(fluid_synth_t *synth, int chan, + int param, float value, + int absolute, int normalized); /* * misc */ +void fluid_synth_settings(fluid_settings_t *settings); + + +/* extern declared in fluid_synth_monopoly.c */ + +int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key); +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel); +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono); -void fluid_synth_settings(fluid_settings_t* settings); +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range); +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key); #endif /* _FLUID_SYNTH_H */ diff --git a/libs/fluidsynth/src/fluid_synth_monopoly.c b/libs/fluidsynth/src/fluid_synth_monopoly.c new file mode 100644 index 0000000000..b7828af5b7 --- /dev/null +++ b/libs/fluidsynth/src/fluid_synth_monopoly.c @@ -0,0 +1,727 @@ +/* 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 + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_chan.h" +#include "fluid_defsfont.h" + + +/****************************************************************************** + The legato detector is composed as this, + variables: + - monolist: monophonic list variable. + - prev_note: to store the most recent note before adding on noteon or before + removing on noteoff. + - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on + legato or staccato playing. + functions: + - fluid_channel_add_monolist(), for inserting a new note. + - fluid_channel_search_monolist(), for searching the position of a note + into the list. + - fluid_channel_remove_monolist(), for removing a note from the list. + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The list allows an easy automatic detection of a legato passage when it is + played on a MIDI keyboard input device. + It is useful also when the input device is an ewi (electronic wind instrument) + or evi (electronic valve instrument) and these instruments are unable to send + MIDI CC legato on/off. + + The list memorizes the notes in playing order. + - (a) On noteOn n2, if a previous note n1 exists, there is a legato + detection with n1 (with or without portamento from n1 to n2 See note below). + - (b) On noteOff of the running note n2, if a previous note n1 exists, + there is a legato detection from n2 to n1, allowing fast trills playing + (with or without portamento from n2 to n1. See note below). + + Notes in the list are inserted to the end of the list that works like a + circular buffer.The features are: + + 1) It is always possible to play an infinite legato passage in + direct order (n1_On,n2_On,n3_On,....). + + 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in + fast trills playing as the list memorizes 10 most recent notes. + + 3) Playing an infinite lagato passage in ascendant or descendant order, + without playing trills is always possible using the usual way like this: + First we begin with an ascendant passage, + n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then + we continue with a descendant passage + (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on + + Each MIDI channel have a legato detector. + + Note: + Portamento is a feature independant of the legato detector. So + portamento isn't part of the lagato detector. However portamento + (when enabled) is triggered at noteOn (like legato). Like in legato + situation it is usual to have a portamento from a note 'fromkey' to another + note 'tokey'. Portamento fromkey note choice is determined at noteOn by + fluid_synth_get_fromkey_portamento_legato() (see below). + + More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). +******************************************************************************/ + + +/***************************************************************************** + Portamento related functions in Poly or Mono mode +******************************************************************************/ + +/** + * fluid_synth_get_fromkey_portamento_legato returns two informations: + * - fromkey note for portamento. + * - fromkey note for legato. + * +-----> fromkey_portamento + * ______|________ + * portamento modes >------->| | + * | get_fromkey | + * Porta.on/off >------------------------->|_______________| + * (PTC) | + * +-----> fromkey_legato + * + * The functions is intended to be call on noteOn mono + * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato() + * ------- + * 1)The function determines if a portamento must occur on next noteOn. + * The value returned is 'fromkey portamento' which is the pitchstart key + * of a portamento, as function of PTC or (default_fromkey, prev_note) both + * if Portamento On. By order of precedence the result is: + * 1.1) PTC have precedence over Portamento On. + * If CC PTC has been received, its value supersedes and any + * portamento pedal On, default_fromkey,prev_note or portamento mode. + * 1.2) Otherwise ,when Portamento On the function takes the following value: + * - default_fromkey if valid + * - otherwise prev_note(prev_note is the note prior the most recent + * note played). + * Then portamento mode is applied to validate the value choosen. + * Where portamento mode is: + * - each note, a portamento occurs on each note. + * - legato only, portamento only on notes played legato. + * - staccato only, portamento only on notes played staccato. + * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled). + * ------ + * 2)The function determines if a legato playing must occur on next noteOn. + * 'fromkey legato note' is returned as a function of default_fromkey, PTC, + * current mono/poly mode,actual 'staccato/legato' playing state and prev_note. + * By order of precedence the result is: + * 2.1) If valid, default_fromkey have precedence over any others values. + * 2.2) Otherwise if CC PTC has been received its value is returned. + * 2.3) Otherwise fromkey legato is determined from the mono/poly mode, + * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note + * as this: + * - in (poly/Mono) staccato , INVALID_NOTE is returned. + * - in poly legato , actually we don't want playing legato. So + * INVALID_NOTE is returned. + * - in mono legato , prev_note is returned. + * + * On input + * @param chan fluid_channel_t. + * @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato' + * note (see description above). + * + * @return + * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. + * If valid,it means that portamento is enabled . + * + * 2)The 'fromkey legato' note is returned. + * + * Notes about usage: + * The function is intended to be called when the following event occurs: + * - On noteOn (Poly or Mono) after insertion in the monophonic list. + * - On noteOff(mono legato playing). In this case, default_fromkey must be valid. + * + * Typical calling usage: + * - In poly, default_fromkey must be INVALID_NOTE. + * - In mono staccato playing,default_fromkey must be INVALID_NOTE. + * - In mono legato playing,default_fromkey must be valid. + */ +static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, + int default_fromkey) +{ + unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL); + + if(fluid_channel_is_valid_note(ptc)) + { + /* CC PTC has been received */ + fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */ + chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */ + + /* returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + default_fromkey = ptc; + } + } + else + { + /* determines and returns fromkey portamento */ + unsigned char fromkey_portamento = INVALID_NOTE; + + if(fluid_channel_portamento(chan)) + { + /* Portamento when Portamento pedal is On */ + /* 'fromkey portamento'is determined from the portamento mode + and the most recent note played (prev_note)*/ + enum fluid_channel_portamento_mode portamentomode = chan->portamentomode; + + if(fluid_channel_is_valid_note(default_fromkey)) + { + fromkey_portamento = default_fromkey; /* on each note */ + } + else + { + fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */ + } + + if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY) + { + /* Mode portamento:legato only */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + fromkey_portamento = INVALID_NOTE; + } + } + else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY) + { + /* Mode portamento:staccato only */ + if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + fromkey_portamento = INVALID_NOTE; + } + } + + /* else Mode portamento: on each note (staccato/legato) */ + } + + /* Returns fromkey portamento */ + chan->synth->fromkey_portamento = fromkey_portamento; + + /* Determines and returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + /* in staccato (poly/Mono) returns INVALID_NOTE */ + /* In mono mode legato playing returns the note prior most + recent note played */ + if(fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */ + } + + /* In poly mode legato playing, actually we don't want playing legato. + So returns INVALID_NOTE */ + } + } + + return default_fromkey; /* Returns legato fromkey */ +} + +/***************************************************************************** + noteon - noteoff functions in Mono mode +******************************************************************************/ +/* + * noteon - noteoff on a channel in "monophonic playing". + * + * A channel needs to be played monophonic if this channel has been set in + * monophonic mode by basic channel API.(see fluid_synth_polymono.c). + * A channel needs also to be played monophonic if it has been set in + * polyphonic mode and legato pedal is On during the playing. + * When a channel is in "monophonic playing" state, only one note at a time can be + * played in a staccato or legato manner (with or without portamento). + * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--*-> preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without) + * LOCAL |________________| O /|\ | (portamento) + * /|\ set_onenote | | fromkey | + * | | | portamento| + * noteOn poly >---*------------------* | | + * | | | + * | _____ |________ | + * portamento modes >--- | ->| | | + * | | get_fromkey | | + * Porta.on/off >--------------------- | ->|_______________| | + * (PTC) | | | + * | fromkey | fromkey | + * | legato | portamento| + * | _____\|/_______ | + * *-->| noteon |--/ + * | | monopoly | + * | | legato |----> voices + * legato modes >------- | ->|_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * noteOff poly >---*----------------- | ---------+ + * | clear | | + * _\|/_____________ | | + * | legato detector | O | + * noteoff_mono->|(search_monolist)|-O-- _____\|/_______ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off +------------------------------------------------------------------------------*/ +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, + char Mono); + +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, + int fromkey, int tokey, int vel); + +/** + * Plays a noteon event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing". + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--->preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| + * LOCAL |________________| O + * | + * | + * | + * | + * | + * | + * | + * | + * | + * | _______________ + * | | noteon | + * +-->| monopoly | + * | legato |---> voices + * |_______________| triggering + * + * The function uses the legato detector (see above) to determine if the note must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, + int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Adds the note into the monophonic list */ + fluid_channel_add_monolist(channel, key, vel, 0); + + /* in Breath Sync mode, the noteon triggering is postponed + until the musician starts blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato/staccato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* legato playing */ + /* legato from prev_note to key */ + /* the voices from prev_note key number are to be used to play key number */ + /* fromkey must be valid */ + return fluid_synth_noteon_monopoly_legato(synth, chan, + fluid_channel_prev_note(channel), key, vel); + } + else + { + /* staccato playing */ + return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); + } + } + else + { + return FLUID_OK; + } +} + +/** + * Plays a noteoff event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing" + * + * _______________ + * | noteon | + * +-->| monopoly | + * | | legato |----> voices + * | |_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * | + * | + * | + * | + * _________________ | + * | legato detector | O + * noteoff_mono->|(search_monolist)|-O-- _______________ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * |_______________| + * + * The function uses the legato detector (see above) to determine if the noteoff must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + int i, i_prev; + fluid_channel_t *channel = synth->channel[chan]; + /* searching the note in the monophonic list */ + i = fluid_channel_search_monolist(channel, key, &i_prev); + + if(i >= 0) + { + /* the note is in the monophonic list */ + /* Removes the note from the monophonic list */ + fluid_channel_remove_monolist(channel, i, &i_prev); + + /* in Breath Sync mode, the noteoff triggering is done + if the musician is blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* the list contains others notes */ + if(i_prev >= 0) + { + /* legato playing detection on noteoff */ + /* legato from key to i_prev key */ + /* the voices from key number are to be used to + play i_prev key number. */ + status = fluid_synth_noteon_monopoly_legato(synth, chan, + key, channel->monolist[i_prev].note, + channel->monolist[i_prev].vel); + } + /* else the note doesn't need to be played off */ + else + { + status = FLUID_OK; + } + } + else + { + /* the monophonic list is empty */ + /* plays the monophonic note noteoff and eventually held + by sustain/sostenuto */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); + } + } + else + { + status = FLUID_OK; + } + } + else + { + /* the note is not found in the list so the note was + played On when the channel was in polyphonic playing */ + /* plays the noteoff as for polyphonic */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + return status; +} + +/*---------------------------------------------------------------------------- + staccato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note in staccato manner. + * Please see the description above about "monophonic playing". + * _______________ + * | noteon | + * noteon_mono >------------------------>| mono_staccato |----> preset_noteon + * |_______________| (with or without) + * LOCAL /|\ (portamento) + * | fromkey + * | portamento + * | + * | + * ______|________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento + * (PTC) + * + * We are in staccato situation (where no previous note have been depressed). + * Before the note been passed to fluid_preset_noteon(), the function must determine + * the from_key_portamento parameter used by fluid_preset_noteon(). + * + * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control + * (PTC). + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int +fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Before playing a new note, if a previous monophonic note is currently + sustained it needs to be released */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained); + /* Get possible 'fromkey portamento' */ + fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE); + /* The note needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, key, vel); +} + +/** + * Plays noteoff for a polyphonic or monophonic note + * Please see the description above about "monophonic playing". + * + * + * noteOff poly >---------------------------------+ + * | + * | + * | + * noteoff_mono _____\|/_______ + * LOCAL >------------------------->| noteoff | + * | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off + * + * The function has the same behaviour when the noteoff is poly of mono, except + * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the + * key is memorized. This is neccessary when the next mono note will be played + * staccato, as any current mono note currently sustained will need to be released + * (see fluid_synth_noteon_mono_staccato()). + * Note also that for a monophonic legato passage, the function is called only when + * the last noteoff of the passage occurs. That means that if sustain or sostenuto + * is depressed, only the last note of a legato passage will be sustained. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param Mono, 1 noteoff on monophonic note. + * 0 noteoff on polyphonic note. + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: On return, on monophonic, possible sustained note is memorized in + * key_mono_sustained. Memorization is done here on noteOff. + */ +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, + char Mono) +{ + int status = FLUID_FAILED; + fluid_voice_t *voice; + int i; + fluid_channel_t *channel = synth->channel[chan]; + + /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */ + if(Mono) + { + channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */ + } + + /* noteoff for all voices with same chan and same key */ + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == 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%d", + fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, + fluid_voice_get_id(voice), + (fluid_curtime() - synth->start) / 1000.0f, + used_voices); + } /* if verbose */ + + fluid_voice_noteoff(voice); + + /* noteoff on monophonic note */ + /* Key memorization if the note is sustained */ + if(Mono && + (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))) + { + channel->key_mono_sustained = key; + } + + status = FLUID_OK; + } /* if voice on */ + } /* for all voices */ + + return status; +} + +/*---------------------------------------------------------------------------- + legato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note played legato. + * Please see the description above about "monophonic playing". + * + * + * _______________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento | + * (PTC) | +-->preset_noteon + * fromkey | fromkey | (with or without) + * legato | portamento| (portamento) + * _____\|/_______ | + * | noteon |--+ + * noteon_mono >------------------------>| monopoly | + * LOCAL | legato |----->voices + * |_______________| triggering + * /|\ (with or without) + * | (portamento) + * legato modes >-----------------+ + * + * We are in legato situation (where a previous note has been depressed). + * The function must determine the from_key_portamento and from_key_legato parameters + * used respectively by fluid_preset_noteon() function and voices triggering functions. + * + * from_key_portamento and from_key_legato are returned by + * fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API), CC portamento On/Off, and CC portamento control + * (PTC). + * Then, depending of the legato modes (see legato mode API), the function will call + * the appropriate triggering functions. + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param fromkey MIDI note number (0-127). + * @param tokey MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: The voices with key 'fromkey' are to be used to play key 'tokey'. + * The function is able to play legato through Preset Zone(s) (PZ) and + * Instrument Zone(s) (IZ) as far as possible. + * When key tokey is outside the current Instrument Zone, Preset Zone, + * current 'fromkey' voices are released. If necessary new voices + * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). + * More informations in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). + */ +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, + int fromkey, int tokey, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + enum fluid_channel_legato_mode legatomode = channel->legatomode; + fluid_voice_t *voice; + int i ; + /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */ + fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey); + + if(fluid_channel_is_valid_note(fromkey)) + { + for(i = 0; i < synth->polyphony; i++) + { + /* searching fromkey voices: only those who don't have 'note off' */ + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == fromkey) + { + fluid_zone_range_t *zone_range = voice->zone_range; + + /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise + checks if tokey is inside the range of the running voice */ + if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel)) + { + switch(legatomode) + { + case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */ + fluid_voice_release(voice); /* normal release */ + break; + + case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */ + /* Skip in attack section */ + fluid_voice_update_multi_retrigger_attack(voice, tokey, vel); + + /* Starts portamento if enabled */ + if(fluid_channel_is_valid_note(synth->fromkey_portamento)) + { + /* Sends portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, + synth->fromkey_portamento, + tokey); + } + + /* The voice is now used to play tokey in legato manner */ + /* Marks this Instrument Zone to be ignored during next + fluid_preset_noteon() */ + zone_range->ignore = TRUE; + break; + + default: /* Invalid mode: this should never happen */ + FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d", + legatomode); + return FLUID_FAILED; + } + } + else + { + /* tokey note is outside the voice range, so the voice is released */ + fluid_voice_release(voice); + } + } + } + } + + /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in + this case it needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel); +} diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c index 328f2556d6..c9662f7787 100644 --- a/libs/fluidsynth/src/fluid_sys.c +++ b/libs/fluidsynth/src/fluid_sys.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -32,7 +32,13 @@ /* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket. * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */ -#define WIN32_SOCKET_FLAG 0x40000000 +#ifdef _WIN32 +#define FLUID_SOCKET_FLAG 0x40000000 +#else +#define FLUID_SOCKET_FLAG 0x00000000 +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 +#endif /* SCHED_FIFO priority for high priority timer threads */ #define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10 @@ -40,73 +46,47 @@ typedef struct { - fluid_thread_func_t func; - void *data; - int prio_level; + fluid_thread_func_t func; + void *data; + int prio_level; } fluid_thread_info_t; struct _fluid_timer_t { - long msec; - fluid_timer_callback_t callback; - void *data; - fluid_thread_t *thread; - int cont; - int auto_destroy; + long msec; + fluid_timer_callback_t callback; + void *data; + fluid_thread_t *thread; + int cont; + int auto_destroy; }; struct _fluid_server_socket_t { - fluid_socket_t socket; - fluid_thread_t *thread; - int cont; - fluid_server_func_t func; - void *data; + fluid_socket_t socket; + fluid_thread_t *thread; + int cont; + fluid_server_func_t func; + void *data; }; -static int fluid_istream_gets(fluid_istream_t in, char* buf, int len); +static int fluid_istream_gets(fluid_istream_t in, char *buf, int len); 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(); -} - - -unsigned int fluid_debug_flags = 0; - -#if DEBUG -/* - * fluid_debug - */ -int fluid_debug(int level, char * fmt, ...) +static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] = { - 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); + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function +}; +static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL }; - fun = fluid_log_function[FLUID_DBG]; - if (fun != NULL) { - (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]); - } - } - return 0; -} -#endif +static const char fluid_libname[] = "fluidsynth"; /** * Installs a new log function for a specified log level. @@ -116,16 +96,18 @@ int fluid_debug(int level, char * fmt, ...) * @return The previously installed function. */ fluid_log_function_t -fluid_set_log_function(int level, fluid_log_function_t fun, void* data) +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_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; } /** @@ -135,75 +117,46 @@ fluid_set_log_function(int level, fluid_log_function_t fun, void* data) * @param data User supplied data (not used) */ void -fluid_default_log_function(int level, char* message, void* data) +fluid_default_log_function(int level, const char *message, void *data) { - FILE* out; + FILE *out; #if defined(WIN32) - out = stdout; + 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); + out = stderr; #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) { + switch(level) + { + case FLUID_PANIC: + FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); + break; - fluid_log_initialized = 1; + case FLUID_ERR: + FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); + break; - if (fluid_log_function[FLUID_PANIC] == NULL) { - fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL); - } + case FLUID_WARN: + FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); + break; - if (fluid_log_function[FLUID_ERR] == NULL) { - fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL); - } + case FLUID_INFO: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; - if (fluid_log_function[FLUID_WARN] == NULL) { - fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL); - } + case FLUID_DBG: +#if DEBUG + FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); +#endif + break; - if (fluid_log_function[FLUID_INFO] == NULL) { - fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL); + default: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; } - if (fluid_log_function[FLUID_DBG] == NULL) { - fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); - } - } + fflush(out); } /** @@ -214,22 +167,26 @@ fluid_log_config(void) * @return Always returns #FLUID_FAILED */ int -fluid_log(int level, const char* fmt, ...) +fluid_log(int level, const char *fmt, ...) { - fluid_log_function_t fun = NULL; + fluid_log_function_t fun = NULL; - va_list args; - va_start (args, fmt); - vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); - va_end (args); + va_list args; + va_start(args, fmt); + FLUID_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]); + 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; + + return FLUID_FAILED; } /** @@ -245,70 +202,77 @@ fluid_log(int level, const char* fmt, ...) * @param delim String of delimiter chars. * @return Pointer to the next token or NULL if no more tokens. */ -char *fluid_strtok (char **str, char *delim) +char *fluid_strtok(char **str, const char *delim) { - char *s, *d, *token; - char c; + char *s, *token; + const char *d; + char c; - if (str == NULL || delim == NULL || !*delim) - { - FLUID_LOG(FLUID_ERR, "Null pointer"); - return NULL; - } + if(str == NULL || delim == NULL || !*delim) + { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return NULL; + } - s = *str; - if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */ + s = *str; - /* skip delimiter chars at beginning of token */ - do - { - c = *s; - if (!c) /* end of source string? */ + if(!s) { - *str = NULL; - return NULL; + return NULL; /* str points to a NULL pointer? (tokenize already ended) */ } - for (d = delim; *d; d++) /* is source char a token char? */ + /* skip delimiter chars at beginning of token */ + do { - if (c == *d) /* token char match? */ - { - s++; /* advance to next source char */ - break; - } + c = *s; + + if(!c) /* end of source string? */ + { + *str = NULL; + return NULL; + } + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + s++; /* advance to next source char */ + break; + } + } } - } while (*d); /* while token char match */ + while(*d); /* while token char match */ - token = s; /* start of token found */ + token = s; /* start of token found */ - /* search for next token char or end of source string */ - for (s = s+1; *s; s++) - { - c = *s; - - for (d = delim; *d; d++) /* is source char a token char? */ + /* search for next token char or end of source string */ + for(s = s + 1; *s; s++) { - if (c == *d) /* token char match? */ - { - *s = '\0'; /* overwrite token char with zero byte to terminate token */ - *str = s+1; /* update str to point to beginning of next token */ - return token; - } + c = *s; + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + *s = '\0'; /* overwrite token char with zero byte to terminate token */ + *str = s + 1; /* update str to point to beginning of next token */ + return token; + } + } } - } - /* we get here only if source string ended */ - *str = NULL; - return token; + /* we get here only if source string ended */ + *str = NULL; + return token; } /* * fluid_error */ -char* +char * fluid_error() { - return fluid_errbuf; + return fluid_errbuf; } /** @@ -322,19 +286,23 @@ fluid_error() int fluid_is_midifile(const char *filename) { - FILE* fp = fopen(filename, "rb"); - char id[4]; + 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; + } - 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; + return FLUID_STRNCMP(id, "MThd", 4) == 0; } /** @@ -342,25 +310,43 @@ fluid_is_midifile(const char *filename) * @param filename Path to the file to check * @return TRUE if it could be a SoundFont, FALSE otherwise * - * The current implementation only checks for the "RIFF" header in the file. - * It is useful only to distinguish between SoundFont and MIDI files. + * @note The current implementation only checks for the "RIFF" and "sfbk" headers in + * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files. */ int fluid_is_soundfont(const char *filename) { - FILE* fp = fopen(filename, "rb"); - char id[4]; + FILE *fp = fopen(filename, "rb"); + char riff_id[4], sfbk_id[4]; + + if(fp == NULL) + { + return 0; + } + + if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) || + (fseek(fp, 4, SEEK_CUR) != 0) || + (fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id))) + { + goto error_rec; + } - if (fp == NULL) { - return 0; - } - if (fread((void*) id, 1, 4, fp) != 4) { + fclose(fp); + return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) && + (FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0); + +error_rec: fclose(fp); return 0; - } - fclose(fp); +} - return strncmp(id, "RIFF", 4) == 0; +/** + * Suspend the execution of the current thread for the specified amount of time. + * @param milliseconds to wait. + */ +void fluid_msleep(unsigned int msecs) +{ + g_usleep(msecs * 1000); } /** @@ -369,78 +355,121 @@ fluid_is_soundfont(const char *filename) */ unsigned int fluid_curtime(void) { - static glong initial_seconds = 0; - GTimeVal timeval; + static glong initial_seconds = 0; + GTimeVal timeval; - if (initial_seconds == 0) { - g_get_current_time (&timeval); - initial_seconds = timeval.tv_sec; - } + if(initial_seconds == 0) + { + g_get_current_time(&timeval); + initial_seconds = timeval.tv_sec; + } - g_get_current_time (&timeval); + g_get_current_time(&timeval); - return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0); + return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0); } /** * Get time in microseconds to be used in relative timing operations. - * @return Unix time in microseconds. + * @return time in microseconds. + * Note: When used for profiling we need high precision clock given + * by g_get_monotonic_time()if available (glib version >= 2.53.3). + * If glib version is too old and in the case of Windows the function + * uses high precision performance counter instead of g_getmonotic_time(). */ double -fluid_utime (void) +fluid_utime(void) { - GTimeVal timeval; + double utime; + +#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28 + /* use high precision monotonic clock if available (g_monotonic_time(). + * For Winfdows, if this clock is actually implemented as low prec. clock + * (i.e. in case glib is too old), high precision performance counter are + * used instead. + * see: https://bugzilla.gnome.org/show_bug.cgi?id=783340 + */ +#if defined(WITH_PROFILING) && defined(WIN32) &&\ + /* glib < 2.53.3 */\ + (GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3)) + /* use high precision performance counter. */ + static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */ + LARGE_INTEGER perf_cpt; + + if(! freq_cache.QuadPart) + { + QueryPerformanceFrequency(&freq_cache); /* Frequency value */ + } - g_get_current_time (&timeval); + QueryPerformanceCounter(&perf_cpt); /* Counter value */ + utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */ +#else + utime = g_get_monotonic_time(); +#endif +#else + /* fallback to less precise clock */ + GTimeVal timeval; + g_get_current_time(&timeval); + utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec); +#endif - return (timeval.tv_sec * 1000000.0 + timeval.tv_usec); + return utime; } + #if defined(WIN32) /* Windoze specific stuff */ void -fluid_thread_self_set_prio (int prio_level) +fluid_thread_self_set_prio(int prio_level) { - if (prio_level > 0) - SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + if(prio_level > 0) + { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + } } #elif defined(__OS2__) /* OS/2 specific stuff */ void -fluid_thread_self_set_prio (int prio_level) +fluid_thread_self_set_prio(int prio_level) { - if (prio_level > 0) - DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); + if(prio_level > 0) + { + DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); + } } #else /* POSIX stuff.. Nice POSIX.. Good POSIX. */ void -fluid_thread_self_set_prio (int prio_level) +fluid_thread_self_set_prio(int prio_level) { - struct sched_param priority; + struct sched_param priority; - if (prio_level > 0) - { + if(prio_level > 0) + { - memset(&priority, 0, sizeof(priority)); - priority.sched_priority = prio_level; + memset(&priority, 0, sizeof(priority)); + priority.sched_priority = prio_level; + + if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0) + { + return; + } - if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) == 0) { - return; - } #ifdef DBUS_SUPPORT -/* Try to gain high priority via rtkit */ - - if (fluid_rtkit_make_realtime(0, prio_level) == 0) { - return; - } + /* Try to gain high priority via rtkit */ + + if(fluid_rtkit_make_realtime(0, prio_level) == 0) + { + return; + } + #endif - FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); - } + FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); + } } #ifdef FPE_CHECK @@ -480,34 +509,34 @@ fluid_thread_self_set_prio (int prio_level) * Checks, if the floating point unit has produced an exception, print a message * if so and clear the exception. */ -unsigned int fluid_check_fpe_i386(char* explanation) +unsigned int fluid_check_fpe_i386(char *explanation) { - unsigned int s; + unsigned int s; - _FPU_GET_SW(s); - _FPU_CLR_SW(); + _FPU_GET_SW(s); + _FPU_CLR_SW(); - s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; + s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; - if (s) - { - 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 " : ""); - } + if(s) + { + 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; + return s; } /* Purpose: * Clear floating point exception. */ -void fluid_clear_fpe_i386 (void) +void fluid_clear_fpe_i386(void) { - _FPU_CLR_SW(); + _FPU_CLR_SW(); } #endif // ifdef FPE_CHECK @@ -523,47 +552,409 @@ void fluid_clear_fpe_i386 (void) */ #if WITH_PROFILING +/* Profiling interface beetween profiling command shell and audio rendering API + (FluidProfile_0004.pdf- 3.2.2). + Macros are in defined in fluid_sys.h. +*/ -fluid_profile_data_t fluid_profile_data[] = +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profling -->| Data |<--macros -->| <--> rendering + shell | API | | | API + +*/ +/* default parameters for shell command "prof_start" in fluid_sys.c */ +unsigned short fluid_profile_notes = 0; /* number of generated notes */ +/* preset bank:0 prog:16 (organ) */ +unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK; +unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG; + +/* print mode */ +unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT; +/* number of measures */ +unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF; +/* measure duration in ms */ +unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION; +/* lock between multiple-shell */ +fluid_atomic_int_t fluid_profile_lock = 0; +/**/ + +/*---------------------------------------------- + Profiling Data +-----------------------------------------------*/ +unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */ +unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */ +fluid_profile_data_t fluid_profile_data[] = /* Data duration */ { - { FLUID_PROF_WRITE, "fluid_synth_write_* ", 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} + {"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0} }; +/*---------------------------------------------- + Internal profiling API +-----------------------------------------------*/ +/* logging profiling data (used on synthesizer instance deletion) */ void fluid_profiling_print(void) { - int i; + int i; - printf("fluid_profiling_print\n"); + printf("fluid_profiling_print\n"); - FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); + 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); + for(i = 0; i < FLUID_PROFILE_NBR; 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); + } } - } } +/* Macro that returns cpu load in percent (%) + * @dur: duration (micro second). + * @sample_rate: sample_rate used in audio driver (Hz). + * @n_amples: number of samples collected during 'dur' duration. +*/ +#define fluid_profile_load(dur,sample_rate,n_samples) \ + (dur * sample_rate / n_samples / 10000.0) + + +/* prints cpu loads only +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out) +{ + unsigned int n_voices; /* voices number */ + static const char max_voices_not_available[] = " not available"; + const char *pmax_voices; + char max_voices_available[20]; + + /* First computes data to be printed */ + double total, voices, reverb, chorus, all_voices, voice; + /* voices number */ + n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices / + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0; + + /* total load (%) */ + total = fluid_profile_data[FLUID_PROF_WRITE].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate, + fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0; + + /* reverb load (%) */ + reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0; + + /* chorus load (%) */ + chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0; + + /* total voices load: total - reverb - chorus (%) */ + voices = total - reverb - chorus; + + /* One voice load (%): all_voices / n_voices. */ + all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0; + + voice = n_voices ? all_voices / n_voices : 0; + + /* estimated maximum voices number */ + if(voice > 0) + { + FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available), + "%17d", (unsigned int)((100.0 - reverb - chorus) / voice)); + pmax_voices = max_voices_available; + } + else + { + pmax_voices = max_voices_not_available; + } -#endif /* WITH_PROFILING */ + /* Now prints data */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n"); + fluid_ostream_printf(out, + " -------|---------|---------|----------|---------|---------|-------------------\n"); + fluid_ostream_printf(out, + "%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices, + reverb, chorus, voice, pmax_voices); +} + +/* +* prints profiling data (used by profile shell command: prof_start). +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* When print mode is 1, the function prints all the informations (see below). +* When print mode is 0, the fonction prints only the cpu loads. +* +* ------------------------------------------------------------------------------ +* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) +* ------------------------------------------------------------------------------ +* Code under profiling |Voices| Duration (microsecond) | Load(%) +* | nbr| min| avg| max| +* ---------------------------|------|--------------------------------|---------- +* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544 +* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100 +* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084 +* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163 +* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839 +* synth_one_block:reverb --->| no profiling available +* synth_one_block:chorus --->| no profiling available +* voice:note --------------->| no profiling available +* voice:release ------------>| no profiling available +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out) +{ + int i; + + if(fluid_profile_print) + { + /* print all details: Duration(microsecond) and cpu loads(%) */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Code under profiling |Voices| Duration (microsecond) | Load(%%)\n"); + fluid_ostream_printf(out, + " | nbr| min| avg| max|\n"); + fluid_ostream_printf(out, + " ---------------------------|------|--------------------------------|----------\n"); + + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + unsigned int count = fluid_profile_data[i].count; + + if(count > 0) + { + /* data are available */ + + if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS) + { + double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate, + fluid_profile_data[i].n_samples); + fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, /* voices number */ + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max, /* maximum duration */ + load); /* cpu load */ + } + else + { + /* note and release duration */ + fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max); /* maximum duration */ + } + } + else + { + /* data aren't available */ + fluid_ostream_printf(out, + " %s| no profiling available\n", fluid_profile_data[i].description); + } + } + } + + /* prints cpu loads only */ + fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */ +} + +/* + Returns true if the user cancels the current profiling measurement. + Actually this is implemented using the key. To add this functionality: + 1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h. + 2) Adds the necessary code inside fluid_profile_is_cancel(). + + When FLUID_PROFILE_CANCEL is not defined, the function return FALSE. +*/ +int fluid_profile_is_cancel_req(void) +{ +#ifdef FLUID_PROFILE_CANCEL + +#if defined(WIN32) /* Windows specific stuff */ + /* Profile cancellation is supported for Windows */ + /* returns TRUE if key is depressed */ + return(GetAsyncKeyState(VK_RETURN) & 0x1); + +#elif defined(__OS2__) /* OS/2 specific stuff */ + /* Profile cancellation isn't yet supported for OS2 */ + /* For OS2, replaces the following line with the function that returns + true when the keyboard key is depressed */ + return FALSE; /* default value */ + +#else /* POSIX stuff */ + /* Profile cancellation is supported for Linux */ + /* returns true is is depressed */ + { + /* Here select() is used to poll the standard input to see if an input + is ready. As the standard input is usually buffered, the user + needs to depress to set the input to a "ready" state. + */ + struct timeval tv; + fd_set fds; /* just one fds need to be polled */ + tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */ + tv.tv_usec = 0; + FD_ZERO(&fds); /* reset fds */ + FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */ + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */ + return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */ + } +#endif /* OS stuff */ + +#else /* FLUID_PROFILE_CANCEL not defined */ + return FALSE; /* default value */ +#endif /* FLUID_PROFILE_CANCEL */ +} +/** +* Returns status used in shell command "prof_start". +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @return status +* - PROFILE_READY profiling data are ready. +* - PROFILE_RUNNING, profiling data are still under acquisition. +* - PROFILE_CANCELED, acquisition has been cancelled by the user. +* - PROFILE_STOP, no acquisition in progress. +* +* When status is PROFILE_RUNNING, the caller can do passive waiting, or other +* work before recalling the function later. +*/ +int fluid_profile_get_status(void) +{ + /* Checks if user has requested to cancel the current measurement */ + /* Cancellation must have precedence over other status */ + if(fluid_profile_is_cancel_req()) + { + fluid_profile_start_stop(0, 0); /* stops the measurement */ + return PROFILE_CANCELED; + } + + switch(fluid_profile_status) + { + case PROFILE_READY: + return PROFILE_READY; /* profiling data are ready */ + + case PROFILE_START: + return PROFILE_RUNNING;/* profiling data are under acquisition */ + + default: + return PROFILE_STOP; + } +} +/** +* Starts or stops profiling measurement. +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param end_tick end position of the measure (in ticks). +* - If end_tick is greater then 0, the function starts a measure if a measure +* isn't running. If a measure is already running, the function does nothing +* and returns. +* - If end_tick is 0, the function stops a measure. +* @param clear_data, +* - If clear_data is 0, the function clears fluid_profile_data before starting +* a measure, otherwise, the data from the started measure will be accumulated +* within fluid_profile_data. +*/ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data) +{ + if(end_ticks) /* This is a "start" request */ + { + /* Checks if a measure is already running */ + if(fluid_profile_status != PROFILE_START) + { + short i; + fluid_profile_end_ticks = end_ticks; + + /* Clears profile data */ + if(clear_data == 0) + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + fluid_profile_data[i].min = 1e10;/* min sets to max value */ + fluid_profile_data[i].max = 0; /* maximum sets to min value */ + fluid_profile_data[i].total = 0; /* total duration microsecond */ + fluid_profile_data[i].count = 0; /* data count */ + fluid_profile_data[i].n_voices = 0; /* voices number */ + fluid_profile_data[i].n_samples = 0;/* audio samples number */ + } + + fluid_profile_status = PROFILE_START; /* starts profiling */ + } + + /* else do nothing when profiling is already started */ + } + else /* This is a "stop" request */ + { + /* forces the current running profile (if any) to stop */ + fluid_profile_status = PROFILE_STOP; /* stops profiling */ + } +} + +#endif /* WITH_PROFILING */ /*************************************************************** * @@ -576,25 +967,29 @@ void fluid_profiling_print(void) /* Rather than inline this one, we just declare it as a function, to prevent * GCC warning about inline failure. */ fluid_cond_t * -new_fluid_cond (void) +new_fluid_cond(void) { - if (!g_thread_supported ()) g_thread_init (NULL); - return g_cond_new (); + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_cond_new(); } #endif static gpointer -fluid_thread_high_prio (gpointer data) +fluid_thread_high_prio(gpointer data) { - fluid_thread_info_t *info = data; + fluid_thread_info_t *info = data; - fluid_thread_self_set_prio (info->prio_level); + fluid_thread_self_set_prio(info->prio_level); - info->func (info->data); - FLUID_FREE (info); + info->func(info->data); + FLUID_FREE(info); - return NULL; + return NULL; } /** @@ -607,59 +1002,78 @@ fluid_thread_high_prio (gpointer data) * @return New thread pointer or NULL on error */ fluid_thread_t * -new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach) +new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach) { - GThread *thread; - fluid_thread_info_t *info; - GError *err = NULL; + GThread *thread; + fluid_thread_info_t *info; + GError *err = NULL; - g_return_val_if_fail (func != NULL, NULL); + g_return_val_if_fail(func != NULL, NULL); #if OLD_GLIB_THREAD_API - /* Make sure g_thread_init has been called. - * FIXME - Probably not a good idea in a shared library, - * but what can we do *and* remain backwards compatible? */ - if (!g_thread_supported ()) g_thread_init (NULL); -#endif - - if (prio_level > 0) - { - info = FLUID_NEW (fluid_thread_info_t); - if (!info) + /* Make sure g_thread_init has been called. + * FIXME - Probably not a good idea in a shared library, + * but what can we do *and* remain backwards compatible? */ + if(!g_thread_supported()) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; + g_thread_init(NULL); } - info->func = func; - info->data = data; - info->prio_level = prio_level; +#endif + + if(prio_level > 0) + { + info = FLUID_NEW(fluid_thread_info_t); + + if(!info) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + info->func = func; + info->data = data; + info->prio_level = prio_level; #if NEW_GLIB_THREAD_API - thread = g_thread_try_new (name, fluid_thread_high_prio, info, &err); + thread = g_thread_try_new(name, fluid_thread_high_prio, info, &err); #else - thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err); + thread = g_thread_create(fluid_thread_high_prio, info, detach == FALSE, &err); #endif - } + } + #if NEW_GLIB_THREAD_API - else thread = g_thread_try_new (name, (GThreadFunc)func, data, &err); + else + { + thread = g_thread_try_new(name, (GThreadFunc)func, data, &err); + } + #else - else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err); + else + { + thread = g_thread_create((GThreadFunc)func, data, detach == FALSE, &err); + } + #endif - if (!thread) - { - FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s", - fluid_gerror_message (err)); - g_clear_error (&err); - return NULL; - } + if(!thread) + { + FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s", + fluid_gerror_message(err)); + g_clear_error(&err); + return NULL; + } #if NEW_GLIB_THREAD_API - if (detach) g_thread_unref (thread); // Release thread reference, if caller wants to detach + + if(detach) + { + g_thread_unref(thread); // Release thread reference, if caller wants to detach + } + #endif - return thread; + return thread; } /** @@ -667,9 +1081,9 @@ new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int pr * @param thread Thread to free */ void -delete_fluid_thread(fluid_thread_t* thread) +delete_fluid_thread(fluid_thread_t *thread) { - /* Threads free themselves when they quit, nothing to do */ + /* Threads free themselves when they quit, nothing to do */ } /** @@ -678,115 +1092,142 @@ delete_fluid_thread(fluid_thread_t* thread) * @return FLUID_OK */ int -fluid_thread_join(fluid_thread_t* thread) +fluid_thread_join(fluid_thread_t *thread) { - g_thread_join (thread); + g_thread_join(thread); - return FLUID_OK; + return FLUID_OK; } -static void -fluid_timer_run (void *data) +static fluid_thread_return_t +fluid_timer_run(void *data) { - fluid_timer_t *timer; - int count = 0; - int cont; - long start; - long delay; + fluid_timer_t *timer; + int count = 0; + int cont; + long start; + long delay; + + timer = (fluid_timer_t *)data; + + /* keep track of the start time for absolute positioning */ + start = fluid_curtime(); - timer = (fluid_timer_t *)data; + while(timer->cont) + { + cont = (*timer->callback)(timer->data, fluid_curtime() - start); - /* keep track of the start time for absolute positioning */ - start = fluid_curtime (); + count++; - while (timer->cont) - { - cont = (*timer->callback)(timer->data, fluid_curtime() - start); + if(!cont) + { + break; + } - count++; - if (!cont) break; + /* 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); - /* 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) g_usleep (delay * 1000); - } + if(delay > 0) + { + fluid_msleep(delay); + } + } - FLUID_LOG (FLUID_DBG, "Timer thread finished"); + FLUID_LOG(FLUID_DBG, "Timer thread finished"); - if (timer->auto_destroy) - FLUID_FREE (timer); + if(timer->auto_destroy) + { + FLUID_FREE(timer); + } - return; + return FLUID_THREAD_RETURN_VALUE; } -fluid_timer_t* -new_fluid_timer (int msec, fluid_timer_callback_t callback, void* data, - int new_thread, int auto_destroy, int high_priority) +fluid_timer_t * +new_fluid_timer(int msec, fluid_timer_callback_t callback, void *data, + int new_thread, int auto_destroy, int high_priority) { - fluid_timer_t *timer; + fluid_timer_t *timer; - timer = FLUID_NEW (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 = TRUE ; - timer->thread = NULL; - timer->auto_destroy = auto_destroy; - - if (new_thread) - { - timer->thread = new_fluid_thread ("timer", fluid_timer_run, timer, high_priority - ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE); - if (!timer->thread) + if(timer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->cont = TRUE ; + timer->thread = NULL; + timer->auto_destroy = auto_destroy; + + if(new_thread) + { + timer->thread = new_fluid_thread("timer", fluid_timer_run, timer, high_priority + ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE); + + if(!timer->thread) + { + FLUID_FREE(timer); + return NULL; + } + } + else { - FLUID_FREE (timer); - return NULL; + fluid_timer_run(timer); /* Run directly, instead of as a separate thread */ + + if(auto_destroy) + { + /* do NOT return freed memory */ + return NULL; + } } - } - else fluid_timer_run (timer); /* Run directly, instead of as a separate thread */ - return timer; + return timer; } -int -delete_fluid_timer (fluid_timer_t *timer) +void +delete_fluid_timer(fluid_timer_t *timer) { - int auto_destroy = timer->auto_destroy; + int auto_destroy; + fluid_return_if_fail(timer != NULL); - timer->cont = 0; - fluid_timer_join (timer); + auto_destroy = timer->auto_destroy; - /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */ + timer->cont = 0; + fluid_timer_join(timer); - if (!auto_destroy) FLUID_FREE (timer); + /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */ - return FLUID_OK; + if(!auto_destroy) + { + FLUID_FREE(timer); + } } int -fluid_timer_join (fluid_timer_t *timer) +fluid_timer_join(fluid_timer_t *timer) { - int auto_destroy; + int auto_destroy; - if (timer->thread) - { - auto_destroy = timer->auto_destroy; - fluid_thread_join (timer->thread); + if(timer->thread) + { + auto_destroy = timer->auto_destroy; + fluid_thread_join(timer->thread); - if (!auto_destroy) timer->thread = NULL; - } + if(!auto_destroy) + { + timer->thread = NULL; + } + } - return FLUID_OK; + return FLUID_OK; } @@ -801,9 +1242,9 @@ fluid_timer_join (fluid_timer_t *timer) * @return Standard in stream. */ fluid_istream_t -fluid_get_stdin (void) +fluid_get_stdin(void) { - return STDIN_FILENO; + return STDIN_FILENO; } /** @@ -811,9 +1252,9 @@ fluid_get_stdin (void) * @return Standard out stream. */ fluid_ostream_t -fluid_get_stdout (void) +fluid_get_stdout(void) { - return STDOUT_FILENO; + return STDOUT_FILENO; } /** @@ -821,31 +1262,34 @@ fluid_get_stdout (void) * @return 0 if end-of-stream, -1 if error, non zero otherwise */ int -fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt, - char* buf, int len) +fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, + char *buf, int len) { #if WITH_READLINE - if (in == fluid_get_stdin ()) - { - char *line; - line = readline (prompt); + if(in == fluid_get_stdin()) + { + char *line; - if (line == NULL) - return -1; + line = readline(prompt); - snprintf(buf, len, "%s", line); - buf[len - 1] = 0; + if(line == NULL) + { + return -1; + } - free(line); - return 1; - } - else + FLUID_SNPRINTF(buf, len, "%s", line); + buf[len - 1] = 0; + + free(line); + return 1; + } + else #endif - { - fluid_ostream_printf (out, "%s", prompt); - return fluid_istream_gets (in, buf, len); - } + { + fluid_ostream_printf(out, "%s", prompt); + return fluid_istream_gets(in, buf, len); + } } /** @@ -856,49 +1300,67 @@ fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt, * @return 1 if a line was read, 0 on end of stream, -1 on error */ static int -fluid_istream_gets (fluid_istream_t in, char* buf, int len) +fluid_istream_gets(fluid_istream_t in, char *buf, int len) { - char c; - int n; + char c; + int n; - buf[len - 1] = 0; + buf[len - 1] = 0; - while (--len > 0) - { + while(--len > 0) + { #ifndef WIN32 - n = read(in, &c, 1); - if (n == -1) return -1; + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + #else - /* Handle read differently depending on if its a socket or file descriptor */ - if (!(in & WIN32_SOCKET_FLAG)) - { - n = read(in, &c, 1); - if (n == -1) return -1; - } - else - { - n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0); - if (n == SOCKET_ERROR) return -1; - } -#endif - if (n == 0) - { - *buf++ = 0; - return 0; - } + /* Handle read differently depending on if its a socket or file descriptor */ + if(!(in & FLUID_SOCKET_FLAG)) + { + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + } + else + { + n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0); + + if(n == SOCKET_ERROR) + { + return -1; + } + } - if ((c == '\n')) - { - *buf++ = 0; - return 1; - } +#endif - /* Store all characters excluding CR */ - if (c != '\r') *buf++ = c; - } + if(n == 0) + { + *buf = 0; + return 0; + } + + if(c == '\n') + { + *buf = 0; + return 1; + } + + /* Store all characters excluding CR */ + if(c != '\r') + { + *buf++ = c; + } + } - return -1; + return -1; } /** @@ -909,390 +1371,297 @@ fluid_istream_gets (fluid_istream_t in, char* buf, int len) * @return Number of bytes written or -1 on error */ int -fluid_ostream_printf (fluid_ostream_t out, char* format, ...) +fluid_ostream_printf(fluid_ostream_t out, const char *format, ...) { - char buf[4096]; - va_list args; - int len; + char buf[4096]; + va_list args; + int len; - va_start (args, format); - len = vsnprintf (buf, 4095, format, args); - va_end (args); + va_start(args, format); + len = FLUID_VSNPRINTF(buf, 4095, format, args); + va_end(args); - if (len == 0) - { - return 0; - } + if(len == 0) + { + return 0; + } - if (len < 0) - { - printf("fluid_ostream_printf: buffer overflow"); - return -1; - } + if(len < 0) + { + printf("fluid_ostream_printf: buffer overflow"); + return -1; + } - buf[4095] = 0; + buf[4095] = 0; #ifndef WIN32 - return write (out, buf, strlen (buf)); + return write(out, buf, FLUID_STRLEN(buf)); #else - { - int retval; + { + int retval; - /* Handle write differently depending on if its a socket or file descriptor */ - if (!(out & WIN32_SOCKET_FLAG)) - return write(out, buf, strlen (buf)); + /* Handle write differently depending on if its a socket or file descriptor */ + if(!(out & FLUID_SOCKET_FLAG)) + { + return write(out, buf, FLUID_STRLEN(buf)); + } - /* Socket */ - retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0); + /* Socket */ + retval = send(out & ~FLUID_SOCKET_FLAG, buf, FLUID_STRLEN(buf), 0); - return retval != SOCKET_ERROR ? retval : -1; - } + return retval != SOCKET_ERROR ? retval : -1; + } #endif } -#if 0 // Ardour says: no, thanks +#ifdef NETWORK_SUPPORT + int fluid_server_socket_join(fluid_server_socket_t *server_socket) { - return fluid_thread_join (server_socket->thread); + return fluid_thread_join(server_socket->thread); } +static int fluid_socket_init(void) +{ +#ifdef _WIN32 + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); -#ifndef WIN32 // Not win32? - -#define SOCKET_ERROR -1 + if(res != 0) + { + FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res); + return FLUID_FAILED; + } -fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock) -{ - return sock; -} +#endif -fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock) -{ - return sock; + return FLUID_OK; } -void fluid_socket_close(fluid_socket_t sock) +static void fluid_socket_cleanup(void) { - if (sock != INVALID_SOCKET) - close (sock); +#ifdef _WIN32 + WSACleanup(); +#endif } -static void -fluid_server_socket_run (void *data) +static int fluid_socket_get_error(void) { - fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; - fluid_socket_t client_socket; -#ifdef IPV6 - struct sockaddr_in6 addr; - char straddr[INET6_ADDRSTRLEN]; +#ifdef _WIN32 + return (int)WSAGetLastError(); #else - struct sockaddr_in addr; - char straddr[INET_ADDRSTRLEN]; + return errno; #endif - socklen_t addrlen = sizeof (addr); - int retval; - FLUID_MEMSET((char *)&addr, 0, 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_istream_t fluid_socket_get_istream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} - FLUID_LOG (FLUID_DBG, "New client connection"); +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} - if (client_socket == INVALID_SOCKET) +void fluid_socket_close(fluid_socket_t sock) +{ + if(sock != INVALID_SOCKET) { - if (server_socket->cont) - FLUID_LOG(FLUID_ERR, "Failed to accept connection"); - - server_socket->cont = 0; - return; - } else { -#ifdef IPV6 - inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); +#ifdef _WIN32 + closesocket(sock); + #else - inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); + close(sock); #endif - retval = server_socket->func (server_socket->data, client_socket, - straddr); - - if (retval != 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) +static fluid_thread_return_t fluid_server_socket_run(void *data) { - fluid_server_socket_t* server_socket; -#ifdef IPV6 - struct sockaddr_in6 addr; + fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; + fluid_socket_t client_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; #else - struct sockaddr_in addr; + struct sockaddr_in addr; #endif - fluid_socket_t sock; - - g_return_val_if_fail (func != NULL, NULL); -#ifdef IPV6 - sock = socket(AF_INET6, 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_in6)); - addr.sin6_family = AF_INET6; - addr.sin6_addr = in6addr_any; - addr.sin6_port = htons(port); +#ifdef HAVE_INETNTOP +#ifdef IPV6_SUPPORT + char straddr[INET6_ADDRSTRLEN]; #else + char straddr[INET_ADDRSTRLEN]; +#endif /* IPV6_SUPPORT */ +#endif /* HAVE_INETNTOP */ - 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); -#endif - if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == 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; - } + socklen_t addrlen = sizeof(addr); + int r; + FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); - server_socket->socket = sock; - server_socket->func = func; - server_socket->data = data; - server_socket->cont = 1; - - server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, - 0, FALSE); - 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; -} - - -#else // Win32 is "special" + 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: %ld", fluid_socket_get_error()); + } + + server_socket->cont = 0; + return FLUID_THREAD_RETURN_VALUE; + } + else + { +#ifdef HAVE_INETNTOP + +#ifdef IPV6_SUPPORT + inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); +#else + inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); +#endif -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN + r = server_socket->func(server_socket->data, client_socket, + straddr); +#else + r = server_socket->func(server_socket->data, client_socket, + inet_ntoa(addr.sin_addr)); #endif -fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock) -{ - return sock | WIN32_SOCKET_FLAG; -} + if(r != 0) + { + fluid_socket_close(client_socket); + } + } + } -fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock) -{ - return sock | WIN32_SOCKET_FLAG; -} + FLUID_LOG(FLUID_DBG, "Server closing"); -void fluid_socket_close (fluid_socket_t sock) -{ - if (sock != INVALID_SOCKET) - closesocket (sock); + return FLUID_THREAD_RETURN_VALUE; } -static void fluid_server_socket_run (void *data) +fluid_server_socket_t * +new_fluid_server_socket(int port, fluid_server_func_t func, void *data) { - fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; - fluid_socket_t client_socket; -#ifdef IPV6 - struct sockaddr_in6 addr; - char straddr[INET6_ADDRSTRLEN]; + fluid_server_socket_t *server_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; #else - struct sockaddr_in addr; - char straddr[INET_ADDRSTRLEN]; + struct sockaddr_in addr; #endif - socklen_t addrlen = sizeof (addr); - int r; - FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); - FLUID_LOG(FLUID_DBG, "Server listening for connections"); + fluid_socket_t sock; - while (server_socket->cont) - { - client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen); + fluid_return_val_if_fail(func != NULL, NULL); - FLUID_LOG (FLUID_DBG, "New client connection"); - - if (client_socket == INVALID_SOCKET) + if(fluid_socket_init() != FLUID_OK) { - if (server_socket->cont) - FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ()); - - server_socket->cont = 0; - return; + return NULL; } - else + +#ifdef IPV6_SUPPORT + sock = socket(AF_INET6, SOCK_STREAM, 0); + + if(sock == INVALID_SOCKET) { -#ifdef IPV6 - inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); -#else - inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); -#endif - r = server_socket->func (server_socket->data, client_socket, - straddr); - if (r != 0) - fluid_socket_close (client_socket); + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; } - } - 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; -#ifdef IPV6 - struct sockaddr_in6 addr; + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons((uint16_t)port); + addr.sin6_addr = in6addr_any; #else - struct sockaddr_in addr; -#endif - - fluid_socket_t sock; - WSADATA wsaData; - int retval; - g_return_val_if_fail (func != NULL, NULL); + sock = socket(AF_INET, SOCK_STREAM, 0); - // Win32 requires initialization of winsock - retval = WSAStartup (MAKEWORD (2,2), &wsaData); + if(sock == INVALID_SOCKET) + { + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; + } - if (retval != 0) - { - FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval); - return NULL; - } -#ifdef IPV6 - sock = socket (AF_INET6, SOCK_STREAM, 0); - if (sock == INVALID_SOCKET) - { - FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ()); - WSACleanup (); - return NULL; - } - addr.sin6_family = AF_INET6; - addr.sin6_port = htons (port); - addr.sin6_addr = in6addr_any; -#else + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); +#endif - sock = socket (AF_INET, SOCK_STREAM, 0); + if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %ld", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } - if (sock == INVALID_SOCKET) - { - FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ()); - WSACleanup (); - return NULL; - } + if(listen(sock, SOMAXCONN) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %ld", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } - addr.sin_family = AF_INET; - addr.sin_port = htons (port); - addr.sin_addr.s_addr = htonl (INADDR_ANY); -#endif - retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr)); + server_socket = FLUID_NEW(fluid_server_socket_t); - if (retval == SOCKET_ERROR) - { - FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ()); - fluid_socket_close (sock); - WSACleanup (); - return NULL; - } + if(server_socket == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } - if (listen (sock, SOMAXCONN) == SOCKET_ERROR) - { - FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ()); - fluid_socket_close (sock); - WSACleanup (); - return NULL; - } + server_socket->socket = sock; + server_socket->func = func; + server_socket->data = data; + server_socket->cont = 1; - server_socket = FLUID_NEW (fluid_server_socket_t); + server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, + 0, FALSE); - if (server_socket == NULL) - { - FLUID_LOG (FLUID_ERR, "Out of memory"); - fluid_socket_close (sock); - WSACleanup (); - return NULL; - } - - server_socket->socket = sock; - server_socket->func = func; - server_socket->data = data; - server_socket->cont = 1; - - server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, - 0, FALSE); - if (server_socket->thread == NULL) - { - FLUID_FREE (server_socket); - fluid_socket_close (sock); - WSACleanup (); - return NULL; - } + if(server_socket->thread == NULL) + { + FLUID_FREE(server_socket); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } - return server_socket; + return server_socket; } -int delete_fluid_server_socket(fluid_server_socket_t *server_socket) +void delete_fluid_server_socket(fluid_server_socket_t *server_socket) { - server_socket->cont = 0; + fluid_return_if_fail(server_socket != NULL); - if (server_socket->socket != INVALID_SOCKET) - fluid_socket_close (server_socket->socket); + server_socket->cont = 0; - if (server_socket->thread) - delete_fluid_thread (server_socket->thread); + if(server_socket->socket != INVALID_SOCKET) + { + fluid_socket_close(server_socket->socket); + } - FLUID_FREE (server_socket); + if(server_socket->thread) + { + fluid_thread_join(server_socket->thread); + delete_fluid_thread(server_socket->thread); + } - WSACleanup (); // Should be called the same number of times as WSAStartup + FLUID_FREE(server_socket); - return FLUID_OK; + // Should be called the same number of times as fluid_socket_init() + fluid_socket_cleanup(); } -#endif -#endif +#endif // NETWORK_SUPPORT diff --git a/libs/fluidsynth/src/fluid_sys.h b/libs/fluidsynth/src/fluid_sys.h index 4953515692..d95f6159f2 100644 --- a/libs/fluidsynth/src/fluid_sys.h +++ b/libs/fluidsynth/src/fluid_sys.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -36,9 +36,13 @@ #ifndef _FLUID_SYS_H #define _FLUID_SYS_H -#include #include "fluidsynth_priv.h" +#ifdef LADSPA +#include +#endif + +#include /** * Macro used for safely accessing a message from a GError and using a default @@ -48,50 +52,46 @@ */ #define fluid_gerror_message(err) ((err) ? err->message : "No error details") - -void fluid_sys_config(void); -void fluid_log_config(void); -void fluid_time_config(void); - - /* Misc */ +#if defined(__INTEL_COMPILER) +#define FLUID_RESTRICT restrict +#elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define FLUID_RESTRICT __restrict__ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#define FLUID_RESTRICT __restrict +#else +#warning "Dont know how this compiler handles restrict pointers, refuse to use them." +#define FLUID_RESTRICT +#endif -#define fluid_return_val_if_fail g_return_val_if_fail -#define fluid_return_if_fail g_return_if_fail #define FLUID_INLINE inline #define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT #define FLUID_UINT_TO_POINTER GUINT_TO_POINTER #define FLUID_POINTER_TO_INT GPOINTER_TO_INT #define FLUID_INT_TO_POINTER GINT_TO_POINTER #define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0])) +#define FLUID_MEMBER_SIZE(struct, member) ( sizeof (((struct *)0)->member) ) #define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) -/* - * Utility functions - */ -char *fluid_strtok (char **str, char *delim); +#define FLUID_LE32TOH(x) GINT32_FROM_LE(x) +#define FLUID_LE16TOH(x) GINT16_FROM_LE(x) -/** +#define fluid_return_if_fail(cond) \ +if(cond) \ + ; \ +else \ + return - 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 -}; +#define fluid_return_val_if_fail(cond, val) \ + fluid_return_if_fail(cond) (val) -int fluid_debug(int level, char * fmt, ...); -#else -#define fluid_debug -#endif +/* + * Utility functions + */ +char *fluid_strtok(char **str, const char *delim); #if defined(__OS2__) @@ -112,17 +112,17 @@ double fluid_utime(void); /* 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 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, +fluid_timer_t *new_fluid_timer(int msec, fluid_timer_callback_t callback, + void *data, int new_thread, int auto_destroy, int high_priority); -int delete_fluid_timer(fluid_timer_t* timer); -int fluid_timer_join(fluid_timer_t* timer); -int fluid_timer_stop(fluid_timer_t* timer); +void delete_fluid_timer(fluid_timer_t *timer); +int fluid_timer_join(fluid_timer_t *timer); +int fluid_timer_stop(fluid_timer_t *timer); // Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) #define NEW_GLIB_THREAD_API (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 32)) @@ -155,19 +155,20 @@ typedef GMutex fluid_cond_mutex_t; #define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) static FLUID_INLINE fluid_cond_mutex_t * -new_fluid_cond_mutex (void) +new_fluid_cond_mutex(void) { - GMutex *mutex; - mutex = g_new (GMutex, 1); - g_mutex_init (mutex); - return (mutex); + GMutex *mutex; + mutex = g_new(GMutex, 1); + g_mutex_init(mutex); + return (mutex); } static FLUID_INLINE void -delete_fluid_cond_mutex (fluid_cond_mutex_t *m) +delete_fluid_cond_mutex(fluid_cond_mutex_t *m) { - g_mutex_clear (m); - g_free (m); + fluid_return_if_fail(m != NULL); + g_mutex_clear(m); + g_free(m); } /* Thread condition signaling */ @@ -177,19 +178,20 @@ typedef GCond fluid_cond_t; #define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) static FLUID_INLINE fluid_cond_t * -new_fluid_cond (void) +new_fluid_cond(void) { - GCond *cond; - cond = g_new (GCond, 1); - g_cond_init (cond); - return (cond); + GCond *cond; + cond = g_new(GCond, 1); + g_cond_init(cond); + return (cond); } static FLUID_INLINE void -delete_fluid_cond (fluid_cond_t *cond) +delete_fluid_cond(fluid_cond_t *cond) { - g_cond_clear (cond); - g_free (cond); + fluid_return_if_fail(cond != NULL); + g_cond_clear(cond); + g_free(cond); } /* Thread private data */ @@ -211,10 +213,10 @@ typedef GStaticMutex fluid_mutex_t; #define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) #define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) -#define fluid_mutex_init(_m) G_STMT_START { \ +#define fluid_mutex_init(_m) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_mutex_init (&(_m)); \ -} G_STMT_END; +} while(0) /* Recursive lock capable mutex */ typedef GStaticRecMutex fluid_rec_mutex_t; @@ -222,10 +224,10 @@ typedef GStaticRecMutex fluid_rec_mutex_t; #define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) #define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) -#define fluid_rec_mutex_init(_m) G_STMT_START { \ +#define fluid_rec_mutex_init(_m) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_rec_mutex_init (&(_m)); \ -} G_STMT_END; +} while(0) /* Dynamically allocated mutex suitable for fluid_cond_t use */ typedef GMutex fluid_cond_mutex_t; @@ -234,15 +236,19 @@ typedef GMutex fluid_cond_mutex_t; #define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) static FLUID_INLINE fluid_cond_mutex_t * -new_fluid_cond_mutex (void) +new_fluid_cond_mutex(void) { - if (!g_thread_supported ()) g_thread_init (NULL); - return g_mutex_new (); + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_mutex_new(); } /* Thread condition signaling */ typedef GCond fluid_cond_t; -fluid_cond_t *new_fluid_cond (void); +fluid_cond_t *new_fluid_cond(void); #define delete_fluid_cond(cond) g_cond_free(cond) #define fluid_cond_signal(cond) g_cond_signal(cond) #define fluid_cond_broadcast(cond) g_cond_broadcast(cond) @@ -254,10 +260,10 @@ typedef GStaticPrivate fluid_private_t; #define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL) #define fluid_private_free(_priv) g_static_private_free(&(_priv)) -#define fluid_private_init(_priv) G_STMT_START { \ +#define fluid_private_init(_priv) do { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_private_init (&(_priv)); \ -} G_STMT_END; +} while(0) #endif @@ -265,7 +271,6 @@ typedef GStaticPrivate fluid_private_t; /* Atomic operations */ #define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi) -#define fluid_atomic_int_add(_pi, _val) g_atomic_int_add(_pi, _val) #define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi) #define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val) #define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi) @@ -275,9 +280,13 @@ typedef GStaticPrivate fluid_private_t; #if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30) #define fluid_atomic_int_exchange_and_add(_pi, _add) \ g_atomic_int_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_add(_pi, _add) #else #define fluid_atomic_int_exchange_and_add(_pi, _add) \ g_atomic_int_exchange_and_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) #endif #define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) @@ -288,115 +297,276 @@ typedef GStaticPrivate fluid_private_t; static FLUID_INLINE void fluid_atomic_float_set(volatile float *fptr, float val) { - sint32 ival; - memcpy (&ival, &val, 4); - fluid_atomic_int_set ((volatile int *)fptr, ival); + int32_t ival; + memcpy(&ival, &val, 4); + fluid_atomic_int_set((volatile int *)fptr, ival); } static FLUID_INLINE float fluid_atomic_float_get(volatile float *fptr) { - sint32 ival; - float fval; - ival = fluid_atomic_int_get ((volatile int *)fptr); - memcpy (&fval, &ival, 4); - return fval; + int32_t ival; + float fval; + ival = fluid_atomic_int_get((volatile int *)fptr); + memcpy(&fval, &ival, 4); + return fval; } /* Threads */ +/* other thread implementations might change this for their needs */ +typedef void *fluid_thread_return_t; +/* static return value for thread functions which requires a return value */ +#define FLUID_THREAD_RETURN_VALUE (NULL) + typedef GThread fluid_thread_t; -typedef void (*fluid_thread_func_t)(void* data); +typedef fluid_thread_return_t (*fluid_thread_func_t)(void *data); #define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ #define fluid_thread_id_t GThread * /* Data type for a thread ID */ #define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ -fluid_thread_t* new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, +fluid_thread_t *new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach); -void delete_fluid_thread(fluid_thread_t* thread); -void fluid_thread_self_set_prio (int prio_level); -int fluid_thread_join(fluid_thread_t* thread); +void delete_fluid_thread(fluid_thread_t *thread); +void fluid_thread_self_set_prio(int prio_level); +int fluid_thread_join(fluid_thread_t *thread); + +/* Dynamic Module Loading, currently only used by LADSPA subsystem */ +#ifdef LADSPA + +typedef GModule fluid_module_t; + +#define fluid_module_open(_name) g_module_open((_name), G_MODULE_BIND_LOCAL) +#define fluid_module_close(_mod) g_module_close(_mod) +#define fluid_module_error() g_module_error() +#define fluid_module_name(_mod) g_module_name(_mod) +#define fluid_module_symbol(_mod, _name, _ptr) g_module_symbol((_mod), (_name), (_ptr)) + +#endif /* LADSPA */ /* Sockets and I/O */ -fluid_istream_t fluid_get_stdin (void); -fluid_ostream_t fluid_get_stdout (void); -int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len); -int fluid_ostream_printf (fluid_ostream_t out, char* format, ...); +fluid_istream_t fluid_get_stdin(void); +fluid_ostream_t fluid_get_stdout(void); +int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, char *buf, int len); +int fluid_ostream_printf(fluid_ostream_t out, const char *format, ...); /* 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); +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); +fluid_server_socket_t *new_fluid_server_socket(int port, fluid_server_func_t func, void *data); +void delete_fluid_server_socket(fluid_server_socket_t *sock); +int fluid_server_socket_join(fluid_server_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); +/* File access */ +#if !GLIB_CHECK_VERSION(2, 26, 0) + /* GStatBuf has not been introduced yet, manually typedef to what they had at that time: + * https://github.com/GNOME/glib/blob/e7763678b56e3be073cc55d707a6e92fc2055ee0/glib/gstdio.h#L98-L115 + */ + #if defined(WIN32) || HAVE_WINDOWS_H // somehow reliably mock G_OS_WIN32?? + #if defined (_MSC_VER) && !defined(_WIN64) + typedef struct _stat32 fluid_stat_buf_t; + #else + typedef struct _stat fluid_stat_buf_t; + #endif + #else + /* posix, OS/2, etc. */ + typedef struct stat fluid_stat_buf_t; + #endif +#else +typedef GStatBuf fluid_stat_buf_t; +#endif +#define fluid_stat(_filename, _statbuf) g_stat((_filename), (_statbuf)) /* Profiling */ +#if WITH_PROFILING +/** profiling interface beetween Profiling command shell and Audio + rendering API (FluidProfile_0004.pdf- 3.2.2) +*/ +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profling -->| Data |<--macros -->| <--> rendering + shell | API | | | API -/** - * 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, - 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 -}; +*/ +/* default parameters for shell command "prof_start" in fluid_sys.c */ +#define FLUID_PROFILE_DEFAULT_BANK 0 /* default bank */ +#define FLUID_PROFILE_DEFAULT_PROG 16 /* default prog (organ) */ +#define FLUID_PROFILE_FIRST_KEY 12 /* first key generated */ +#define FLUID_PROFILE_LAST_KEY 108 /* last key generated */ +#define FLUID_PROFILE_DEFAULT_VEL 64 /* default note velocity */ +#define FLUID_PROFILE_VOICE_ATTEN -0.04f /* gain attenuation per voice (dB) */ -#if WITH_PROFILING -void fluid_profiling_print(void); +#define FLUID_PROFILE_DEFAULT_PRINT 0 /* default print mode */ +#define FLUID_PROFILE_DEFAULT_N_PROF 1 /* default number of measures */ +#define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms) */ + + +extern unsigned short fluid_profile_notes; /* number of generated notes */ +extern unsigned char fluid_profile_bank; /* bank,prog preset used by */ +extern unsigned char fluid_profile_prog; /* generated notes */ +extern unsigned char fluid_profile_print; /* print mode */ + +extern unsigned short fluid_profile_n_prof;/* number of measures */ +extern unsigned short fluid_profile_dur; /* measure duration in ms */ +extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */ +/**/ + +/*---------------------------------------------- + Internal profiling API (in fluid_sys.c) +-----------------------------------------------*/ +/* Starts a profiling measure used in shell command "prof_start" */ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data); + +/* Returns status used in shell command "prof_start" */ +int fluid_profile_get_status(void); + +/* Prints profiling data used in shell command "prof_start" */ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out); + +/* Returns True if profiling cancellation has been requested */ +int fluid_profile_is_cancel_req(void); + +/* For OS that implement key for profile cancellation: + 1) Adds #define FLUID_PROFILE_CANCEL + 2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c +*/ +#if defined(WIN32) /* Profile cancellation is supported for Windows */ +#define FLUID_PROFILE_CANCEL + +#elif defined(__OS2__) /* OS/2 specific stuff */ +/* Profile cancellation isn't yet supported for OS2 */ +#else /* POSIX stuff */ +#define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */ +#include /* STDIN_FILENO */ +#include /* select() */ +#endif /* posix */ -/** Profiling data. Keep track of min/avg/max values to execute a +/* logging profiling data (used on synthesizer instance deletion) */ +void fluid_profiling_print(void); + +/*---------------------------------------------- + Profiling Data (in fluid_sys.c) +-----------------------------------------------*/ +/** Profiling data. Keep track of min/avg/max values to profile a piece of code. */ -typedef struct _fluid_profile_data_t { - int num; - char* description; - double min, max, total; - unsigned int count; +typedef struct _fluid_profile_data_t +{ + const char *description; /* name of the piece of code under profiling */ + double min, max, total; /* duration (microsecond) */ + unsigned int count; /* total count */ + unsigned int n_voices; /* voices number */ + unsigned int n_samples; /* audio samples number */ } fluid_profile_data_t; -extern fluid_profile_data_t fluid_profile_data[]; +enum +{ + /* commands/status (profiling interface) */ + PROFILE_STOP, /* command to stop a profiling measure */ + PROFILE_START, /* command to start a profile measure */ + PROFILE_READY, /* status to signal that a profiling measure has finished + and ready to be printed */ + /*- State returned by fluid_profile_get_status() -*/ + /* between profiling commands and internal profiling API */ + PROFILE_RUNNING, /* a profiling measure is running */ + PROFILE_CANCELED,/* a profiling measure has been canceled */ +}; + +/* Data interface */ +extern unsigned char fluid_profile_status ; /* command and status */ +extern unsigned int fluid_profile_end_ticks; /* ending position (in ticks) */ +extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */ -/** Macro to obtain a time refence used for the profiling */ +/*---------------------------------------------- + Probes macros +-----------------------------------------------*/ +/** Macro to obtain a time reference used for the profiling */ #define fluid_profile_ref() fluid_utime() /** Macro to create a variable and assign the current reference time for profiling. * So we don't get unused variable warnings when profiling is disabled. */ #define fluid_profile_ref_var(name) double name = 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; \ +/** + * Profile identifier 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, + 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_PROFILE_NBR /* number of profile probes */ +}; +/** Those macros are used to calculate the min/avg/max. Needs a profile number, a + time reference, the voices and samples number. */ + +/* local macro : acquiere data */ +#define fluid_profile_data(_num, _ref, voices, samples)\ +{\ + 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++;\ + fluid_profile_data[_num].n_voices += voices;\ + fluid_profile_data[_num].n_samples += samples;\ + _ref = _now;\ } +/** Macro to collect data, called from inner functions inside audio + rendering API */ +#define fluid_profile(_num, _ref, voices, samples)\ +{\ + if ( fluid_profile_status == PROFILE_START)\ + { /* acquires data */\ + fluid_profile_data(_num, _ref, voices, samples)\ + }\ +} + +/** Macro to collect data, called from audio rendering API (fluid_write_xxxx()). + This macro control profiling ending position (in ticks). +*/ +#define fluid_profile_write(_num, _ref, voices, samples)\ +{\ + if (fluid_profile_status == PROFILE_START)\ + {\ + /* acquires data first: must be done before checking that profile is + finished to ensure at least one valid data sample. + */\ + fluid_profile_data(_num, _ref, voices, samples)\ + if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\ + {\ + /* profiling is finished */\ + fluid_profile_status = PROFILE_READY;\ + }\ + }\ +} #else @@ -404,11 +574,9 @@ extern fluid_profile_data_t fluid_profile_data[]; #define fluid_profiling_print() #define fluid_profile_ref() 0 #define fluid_profile_ref_var(name) -#define fluid_profile(_num,_ref) - -#endif - - +#define fluid_profile(_num,_ref,voices, samples) +#define fluid_profile_write(_num,_ref, voices, samples) +#endif /* WITH_PROFILING */ /** @@ -442,7 +610,32 @@ extern fluid_profile_data_t fluid_profile_data[]; #define fluid_clear_fpe() #endif -unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe); +unsigned int fluid_check_fpe_i386(char *explanation_in_case_of_fpe); void fluid_clear_fpe_i386(void); +/* System control */ +void fluid_msleep(unsigned int msecs); + +/** + * Advances the given \c ptr to the next \c alignment byte boundary. + * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow. + * + * @note \c alignment must be a power of two + * @return Returned pointer is guarenteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. + */ +static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment) +{ + uintptr_t ptr_int = (uintptr_t)ptr; + unsigned int offset = ptr_int & (alignment - 1); + unsigned int add = (alignment - offset) & (alignment - 1); // advance the pointer to the next alignment boundary + ptr_int += add; + + /* assert alignment is power of two */ + FLUID_ASSERT(!(alignment == 0) && !(alignment & (alignment - 1))); + + return (void *)ptr_int; +} + +#define FLUID_DEFAULT_ALIGNMENT (64U) + #endif /* _FLUID_SYS_H */ diff --git a/libs/fluidsynth/src/fluid_tuning.c b/libs/fluidsynth/src/fluid_tuning.c index 8977ed6728..ee083116a2 100644 --- a/libs/fluidsynth/src/fluid_tuning.c +++ b/libs/fluidsynth/src/fluid_tuning.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,151 +24,168 @@ #include "fluid_sys.h" -fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog) +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog) { - fluid_tuning_t* tuning; - int i; + 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 = FLUID_NEW(fluid_tuning_t); - tuning->name = NULL; + if(tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } - if (name != NULL) { - tuning->name = FLUID_STRDUP(name); - } + FLUID_MEMSET(tuning, 0, sizeof(fluid_tuning_t)); - tuning->bank = bank; - tuning->prog = prog; + if(fluid_tuning_set_name(tuning, name) != FLUID_OK) + { + delete_fluid_tuning(tuning); + return NULL; + } - for (i = 0; i < 128; i++) { - tuning->pitch[i] = i * 100.0; - } + tuning->bank = bank; + tuning->prog = prog; - tuning->refcount = 1; /* Start with a refcount of 1 */ + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = i * 100.0; + } + + fluid_atomic_int_set(&tuning->refcount, 1); /* Start with a refcount of 1 */ - return tuning; + return tuning; } /* Duplicate a tuning */ fluid_tuning_t * -fluid_tuning_duplicate (fluid_tuning_t *tuning) +fluid_tuning_duplicate(fluid_tuning_t *tuning) { - fluid_tuning_t *new_tuning; - int i; + fluid_tuning_t *new_tuning; + int i; - new_tuning = FLUID_NEW (fluid_tuning_t); + new_tuning = FLUID_NEW(fluid_tuning_t); - if (!new_tuning) { - FLUID_LOG (FLUID_PANIC, "Out of memory"); - return NULL; - } + if(!new_tuning) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } - if (tuning->name) - { - new_tuning->name = FLUID_STRDUP (tuning->name); + FLUID_MEMSET(new_tuning, 0, sizeof(fluid_tuning_t)); - if (!new_tuning->name) + if(fluid_tuning_set_name(new_tuning, tuning->name) != FLUID_OK) { - FLUID_FREE (new_tuning); - FLUID_LOG (FLUID_PANIC, "Out of memory"); - return NULL; + delete_fluid_tuning(new_tuning); + return NULL; } - } - else new_tuning->name = NULL; - new_tuning->bank = tuning->bank; - new_tuning->prog = tuning->prog; + new_tuning->bank = tuning->bank; + new_tuning->prog = tuning->prog; - for (i = 0; i < 128; i++) - new_tuning->pitch[i] = tuning->pitch[i]; + for(i = 0; i < 128; i++) + { + new_tuning->pitch[i] = tuning->pitch[i]; + } - new_tuning->refcount = 1; /* Start with a refcount of 1 */ + fluid_atomic_int_set(&new_tuning->refcount, 1); /* Start with a refcount of 1 */ - return new_tuning; + return new_tuning; } void -delete_fluid_tuning (fluid_tuning_t *tuning) +delete_fluid_tuning(fluid_tuning_t *tuning) { - if (tuning->name) FLUID_FREE (tuning->name); - FLUID_FREE (tuning); + fluid_return_if_fail(tuning != NULL); + + FLUID_FREE(tuning->name); + FLUID_FREE(tuning); } /* Add a reference to a tuning object */ void -fluid_tuning_ref (fluid_tuning_t *tuning) +fluid_tuning_ref(fluid_tuning_t *tuning) { - fluid_return_if_fail (tuning != NULL); + fluid_return_if_fail(tuning != NULL); - fluid_atomic_int_inc (&tuning->refcount); + fluid_atomic_int_inc(&tuning->refcount); } /* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */ int -fluid_tuning_unref (fluid_tuning_t *tuning, int count) +fluid_tuning_unref(fluid_tuning_t *tuning, int count) { - fluid_return_val_if_fail (tuning != NULL, FALSE); - - /* Add and compare are separate, but that is OK, since refcount will only - * reach 0 when there are no references and therefore no possibility of - * another thread adding a reference in between */ - fluid_atomic_int_add (&tuning->refcount, -count); - - /* Delete when refcount reaches 0 */ - if (!fluid_atomic_int_get (&tuning->refcount)) - { - delete_fluid_tuning (tuning); - return TRUE; - } - else return FALSE; -} + fluid_return_val_if_fail(tuning != NULL, FALSE); -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); - } + /* Add and compare are separate, but that is OK, since refcount will only + * reach 0 when there are no references and therefore no possibility of + * another thread adding a reference in between */ + fluid_atomic_int_add(&tuning->refcount, -count); + + /* Delete when refcount reaches 0 */ + if(!fluid_atomic_int_get(&tuning->refcount)) + { + delete_fluid_tuning(tuning); + return TRUE; + } + else + { + return FALSE; + } } -char* fluid_tuning_get_name(fluid_tuning_t* tuning) +int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name) { - return tuning->name; + if(tuning->name != NULL) + { + FLUID_FREE(tuning->name); + tuning->name = NULL; + } + + if(name != NULL) + { + tuning->name = FLUID_STRDUP(name); + + if(tuning->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + } + + return FLUID_OK; } -static void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch) +char *fluid_tuning_get_name(fluid_tuning_t *tuning) { - tuning->pitch[key] = pitch; + return tuning->name; } -void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv) +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv) { - int i; + int i; - for (i = 0; i < 128; i++) { - tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; - } + 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, const double* pitch) +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch) { - int i; + int i; - for (i = 0; i < 128; i++) { - tuning->pitch[i] = pitch[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) +void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch) { - if ((key >= 0) && (key < 128)) { - tuning->pitch[key] = pitch; - } + if((key >= 0) && (key < 128)) + { + tuning->pitch[key] = pitch; + } } diff --git a/libs/fluidsynth/src/fluid_tuning.h b/libs/fluidsynth/src/fluid_tuning.h index d974139276..3afe2c65ad 100644 --- a/libs/fluidsynth/src/fluid_tuning.h +++ b/libs/fluidsynth/src/fluid_tuning.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -34,32 +34,33 @@ #include "fluidsynth_priv.h" -struct _fluid_tuning_t { - char* name; - int bank; - int prog; - double pitch[128]; /* the pitch of every key, in cents */ - int refcount; /* Tuning reference count */ +struct _fluid_tuning_t +{ + char *name; + int bank; + int prog; + double pitch[128]; /* the pitch of every key, in cents */ + fluid_atomic_int_t refcount; /* Tuning reference count */ }; -fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog); -void delete_fluid_tuning (fluid_tuning_t *tuning); -fluid_tuning_t *fluid_tuning_duplicate (fluid_tuning_t *tuning); -void fluid_tuning_ref (fluid_tuning_t *tuning); -int fluid_tuning_unref (fluid_tuning_t *tuning, int count); +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog); +void delete_fluid_tuning(fluid_tuning_t *tuning); +fluid_tuning_t *fluid_tuning_duplicate(fluid_tuning_t *tuning); +void fluid_tuning_ref(fluid_tuning_t *tuning); +int fluid_tuning_unref(fluid_tuning_t *tuning, int count); -void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name); -char* fluid_tuning_get_name(fluid_tuning_t* tuning); +int fluid_tuning_set_name(fluid_tuning_t *tuning, const 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); +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, const double* pitch_deriv); +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv); -void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch); +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch); #define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c index e6efbac899..51c1ebf655 100644 --- a/libs/fluidsynth/src/fluid_voice.c +++ b/libs/fluidsynth/src/fluid_voice.c @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -27,6 +27,7 @@ #include "fluid_sys.h" #include "fluid_sfont.h" #include "fluid_rvoice_event.h" +#include "fluid_defsfont.h" /* 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 */ @@ -36,299 +37,349 @@ /* min vol envelope release (to stop clicks) in SoundFont timecents */ #define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ -static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice); -static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, + +static const int32_t INT24_MAX = (1 << (16 + 8 - 1)); + +static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice); +static int calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, int gen_key2base, int is_decay); static fluid_real_t -fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); +fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice); #define UPDATE_RVOICE0(proc) \ do { \ - if (voice->can_access_rvoice) proc(voice->rvoice); \ - else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ - proc, voice->rvoice, 0, 0.0f); \ - } while (0) - -#define UPDATE_RVOICE_PTR(proc, obj) \ - do { \ - if (voice->can_access_rvoice) proc(voice->rvoice, obj); \ - else fluid_rvoice_eventhandler_push_ptr(voice->channel->synth->eventhandler, \ - proc, voice->rvoice, obj); \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \ } while (0) - #define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \ do { \ - if (voice->can_access_rvoice) proc(obj, rarg); \ - else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ - proc, obj, 0, rarg); \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) #define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \ do { \ - if (voice->can_access_rvoice) proc(obj, iarg); \ - else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ - proc, obj, iarg, 0.0f); \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) -#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ +#define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \ do { \ - if (voice->can_access_rvoice) proc(obj, iarg, rarg); \ - else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ - proc, obj, iarg, rarg); \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg1; \ + param[1].i = iarg2; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) -#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \ +#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ do { \ - if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \ - else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \ - proc, obj, iarg, r1, r2, r3, r4, r5); \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + param[1].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ } while (0) -#define UPDATE_RVOICE_VOLENV(section, arg1, arg2, arg3, arg4, arg5) \ - do { \ - fluid_adsr_env_set_data(&voice->volenv, section, arg1, arg2, arg3, arg4, arg5) \ - UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, arg1, arg2, arg3, arg4, arg5) \ - } while(0) - -#define UPDATE_RVOICE_MODENV(section, arg1, arg2, arg3, arg4, arg5) \ - UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, arg1, arg2, arg3, arg4, arg5) - #define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1) #define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1) -#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1) -#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg) -#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg) -#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) -#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) +#define UPDATE_RVOICE_BUFFERS_AMP(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg) +#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) +#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) -static inline void -fluid_voice_update_volenv(fluid_voice_t* voice, - fluid_adsr_env_section_t section, +static FLUID_INLINE void +fluid_voice_update_volenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max) { - fluid_adsr_env_set_data(&voice->volenv, section, count, coeff, increment, - min, max); - UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, - &voice->rvoice->envlfo.volenv, section, count, - coeff, increment, min, max); + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.volenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param); + } } -static inline void -fluid_voice_update_modenv(fluid_voice_t* voice, - fluid_adsr_env_section_t section, +static FLUID_INLINE void +fluid_voice_update_modenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max) { - UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, - &voice->rvoice->envlfo.modenv, section, count, - coeff, increment, min, max); + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.modenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param); + } } -static inline void fluid_sample_null_ptr(fluid_sample_t** sample) +static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t **sample) { - if (*sample != NULL) { - fluid_sample_decr_ref(*sample); - *sample = NULL; - } + if(*sample != NULL) + { + fluid_sample_decr_ref(*sample); + *sample = NULL; + } } /* * Swaps the current rvoice with the current overflow_rvoice */ -static void fluid_voice_swap_rvoice(fluid_voice_t* voice) +static void fluid_voice_swap_rvoice(fluid_voice_t *voice) { - fluid_rvoice_t* rtemp = voice->rvoice; - int ctemp = voice->can_access_rvoice; - voice->rvoice = voice->overflow_rvoice; - voice->can_access_rvoice = voice->can_access_overflow_rvoice; - voice->overflow_rvoice = rtemp; - voice->can_access_overflow_rvoice = ctemp; + fluid_rvoice_t *rtemp = voice->rvoice; + int ctemp = voice->can_access_rvoice; + voice->rvoice = voice->overflow_rvoice; + voice->can_access_rvoice = voice->can_access_overflow_rvoice; + voice->overflow_rvoice = rtemp; + voice->can_access_overflow_rvoice = ctemp; } -static void fluid_voice_initialize_rvoice(fluid_voice_t* voice) +static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) { - FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); - - /* 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. - */ - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVSUSTAIN, - 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVFINISHED, - 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVSUSTAIN, - 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED, - 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); + + /* 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. + */ + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + + param[0].i = FLUID_IIR_LOWPASS; + param[1].i = 0; + fluid_iir_filter_init(&voice->rvoice->resonant_filter, param); + + param[0].i = FLUID_IIR_DISABLED; + fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param); + + param[0].real = output_rate; + fluid_rvoice_set_output_rate(voice->rvoice, param); } /* * new_fluid_voice */ -fluid_voice_t* -new_fluid_voice(fluid_real_t output_rate) +fluid_voice_t * +new_fluid_voice(fluid_rvoice_eventhandler_t *handler, 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->rvoice = FLUID_NEW(fluid_rvoice_t); - voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); - if (voice->rvoice == NULL || voice->overflow_rvoice == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE(voice->rvoice); - FLUID_FREE(voice); - return NULL; - } - - voice->status = FLUID_VOICE_CLEAN; - voice->chan = NO_CHANNEL; - voice->key = 0; - voice->vel = 0; - voice->channel = NULL; - voice->sample = NULL; - - /* Initialize both the rvoice and overflow_rvoice */ - voice->can_access_rvoice = 1; - voice->can_access_overflow_rvoice = 1; - fluid_voice_initialize_rvoice(voice); - fluid_voice_swap_rvoice(voice); - fluid_voice_initialize_rvoice(voice); - - fluid_voice_set_output_rate(voice, output_rate); - - return voice; + fluid_voice_t *voice; + voice = FLUID_NEW(fluid_voice_t); + + if(voice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + voice->can_access_rvoice = TRUE; + voice->can_access_overflow_rvoice = TRUE; + + voice->rvoice = FLUID_NEW(fluid_rvoice_t); + voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); + + if(voice->rvoice == NULL || voice->overflow_rvoice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_voice(voice); + return NULL; + } + + voice->status = FLUID_VOICE_CLEAN; + voice->chan = NO_CHANNEL; + voice->key = 0; + voice->vel = 0; + voice->eventhandler = handler; + voice->channel = NULL; + voice->sample = NULL; + voice->output_rate = output_rate; + + /* Initialize both the rvoice and overflow_rvoice */ + fluid_voice_initialize_rvoice(voice, output_rate); + fluid_voice_swap_rvoice(voice); + fluid_voice_initialize_rvoice(voice, output_rate); + + return voice; } /* * delete_fluid_voice */ -int -delete_fluid_voice(fluid_voice_t* voice) +void +delete_fluid_voice(fluid_voice_t *voice) { - if (voice == NULL) { - return FLUID_OK; - } - if (!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) { - /* stop rvoice before deleting voice! */ - return FLUID_FAILED; - } - FLUID_FREE(voice->overflow_rvoice); - FLUID_FREE(voice->rvoice); - FLUID_FREE(voice); - return FLUID_OK; + fluid_return_if_fail(voice != NULL); + + if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) + { + FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id); + } + + FLUID_FREE(voice->overflow_rvoice); + FLUID_FREE(voice->rvoice); + FLUID_FREE(voice); } /* fluid_voice_init * * Initialize the synthesis process + * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange + * of the voice. + * When playing legato (n1,n2) in mono mode, n2 will use n1 voices + * as far as n2 still enters in Keyrange,Velrange of n1. */ 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) +fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + 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. */ - int i; - - if (!voice->can_access_rvoice) { - if (voice->can_access_overflow_rvoice) - fluid_voice_swap_rvoice(voice); - else { - FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); - return FLUID_FAILED; + /* 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. */ + int i; + + if(!voice->can_access_rvoice) + { + if(voice->can_access_overflow_rvoice) + { + fluid_voice_swap_rvoice(voice); + } + else + { + FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); + return FLUID_FAILED; + } } - } - /* We are now guaranteed to have access to the rvoice */ - - if (voice->sample) - fluid_voice_off(voice); - - 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->start_time = start_time; - voice->debug = 0; - voice->has_noteoff = 0; - UPDATE_RVOICE0(fluid_rvoice_reset); - - /* Increment the reference count of the sample to prevent the - unloading of the soundfont while this voice is playing, - once for us and once for the rvoice. */ - fluid_sample_incr_ref(sample); - UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample); - fluid_sample_incr_ref(sample); - voice->sample = sample; - - i = fluid_channel_get_interp_method(channel); - UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); - - /* 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); - UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice)); - - voice->synth_gain = gain; - /* avoid division by zero later*/ - if (voice->synth_gain < 0.0000001){ - voice->synth_gain = 0.0000001; - } - UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); - - /* Set up buffer mapping, should be done more flexible in the future. */ - i = channel->synth->audio_groups; - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL); - i = 2 * (voice->chan % i); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1); - - return FLUID_OK; + + /* We are now guaranteed to have access to the rvoice */ + + if(voice->sample) + { + fluid_voice_off(voice); + } + + voice->zone_range = inst_zone_range; /* Instrument zone range for legato */ + 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->start_time = start_time; + voice->has_noteoff = 0; + UPDATE_RVOICE0(fluid_rvoice_reset); + + /* Increment the reference count of the sample to prevent the + unloading of the soundfont while this voice is playing, + once for us and once for the rvoice. */ + fluid_sample_incr_ref(sample); + fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); + fluid_sample_incr_ref(sample); + voice->sample = sample; + + i = fluid_channel_get_interp_method(channel); + UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); + + /* 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); + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice)); + + voice->synth_gain = gain; + + /* avoid division by zero later*/ + if(voice->synth_gain < 0.0000001) + { + voice->synth_gain = 0.0000001; + } + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); + + /* Set up buffer mapping, should be done more flexible in the future. */ + i = 2 * channel->synth->audio_groups; + i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels; + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL); + + i = 2 * (voice->chan % channel->synth->audio_groups); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1); + + return FLUID_OK; } /** - * Update sample rate. - * NOTE: If the voice is active, it will be turned off. + * Update sample rate. + * @note If the voice is active, it will be turned off. */ -int -fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value) +void +fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value) { - if (_PLAYING(voice)) - fluid_voice_off(voice); - - voice->output_rate = value; - UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); - /* Update the other rvoice as well */ - fluid_voice_swap_rvoice(voice); - UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); - fluid_voice_swap_rvoice(voice); - - return FLUID_FAILED; + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + + voice->output_rate = value; + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value); + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value); } @@ -339,12 +390,15 @@ fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value) * @param val Generator value */ void -fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) +fluid_voice_gen_set(fluid_voice_t *voice, int i, float val) { - voice->gen[i].val = val; - voice->gen[i].flags = GEN_SET; - if (i == GEN_SAMPLEMODE) - UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val); + voice->gen[i].val = val; + voice->gen[i].flags = GEN_SET; + + if(i == GEN_SAMPLEMODE) + { + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val); + } } /** @@ -354,10 +408,10 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) * @param val Value to add to the existing value */ void -fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val) +fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val) { - voice->gen[i].val += val; - voice->gen[i].flags = GEN_SET; + voice->gen[i].val += val; + voice->gen[i].flags = GEN_SET; } /** @@ -367,128 +421,102 @@ fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val) * @return Current generator value */ float -fluid_voice_gen_get(fluid_voice_t* voice, int gen) +fluid_voice_gen_get(fluid_voice_t *voice, int gen) { - return voice->gen[gen].val; + return voice->gen[gen].val; } -fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) +fluid_real_t fluid_voice_gen_value(const 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); - } + /* 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); + } } - -/** - * Synthesize a voice to a buffer. - * - * @param voice Voice to synthesize - * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) - * @return Count of samples written to dsp_buf (can be 0) - * - * Panning, reverb and chorus are processed separately. The dsp interpolation - * routine is in (fluid_dsp_float.c). +/* + * fluid_voice_start */ -int -fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf) +void fluid_voice_start(fluid_voice_t *voice) { - int result; - if (!voice->can_access_rvoice) - return 0; + /* 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.*/ - result = fluid_rvoice_write(voice->rvoice, dsp_buf); + fluid_voice_calculate_runtime_synthesis_parameters(voice); - if (result == -1) - return 0; +#ifdef WITH_PROFILING + voice->ref = fluid_profile_ref(); +#endif - if ((result < FLUID_BUFSIZE) && _PLAYING(voice)) /* Voice finished by itself */ - fluid_voice_off(voice); + voice->status = FLUID_VOICE_ON; - return result; + /* Increment voice count */ + voice->channel->synth->active_voice_count++; } - /** - * Mix voice data to left/right (panning), reverb and chorus buffers. - * @param count Number of samples - * @param dsp_buf Source buffer - * @param voice Voice to mix - * @param left_buf Left audio buffer - * @param right_buf Right audio buffer - * @param reverb_buf Reverb buffer - * @param chorus_buf Chorus buffer + * Calculate the amplitude of a voice. * + * @param gain The gain value in the range [0.0 ; 1.0] + * @return An amplitude used by rvoice_mixer's buffers */ -void -fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, - fluid_real_t* left_buf, fluid_real_t* right_buf, - fluid_real_t* reverb_buf, fluid_real_t* chorus_buf) +static FLUID_INLINE fluid_real_t +fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain) { - fluid_rvoice_buffers_t buffers; - fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf}; - - fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left); - fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right); - fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb); - fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus); - - fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4); - - fluid_check_fpe ("voice_mix"); + /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float + * samples to [0.0;1.0] divide samples by the max. value of an int24 and + * amplify them with the gain */ + return gain * voice->synth_gain / (INT24_MAX * 1.0f); } - - -/* - * fluid_voice_start - */ -void fluid_voice_start(fluid_voice_t* voice) +/* Useful to return the nominal pitch of a key */ +/* The nominal pitch is dependant of voice->root_pitch,tuning, and + GEN_SCALETUNE generator. + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. +*/ +fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) { - /* 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); - - voice->ref = fluid_profile_ref(); - - voice->status = FLUID_VOICE_ON; + fluid_tuning_t *tuning; + fluid_real_t x, pitch; + + /* Now the nominal pitch of the key is returned. + * 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 fluid_voice_gen_value(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)) + { + tuning = fluid_channel_get_tuning(voice->channel); + x = fluid_tuning_get_pitch(tuning, (int)(voice->root_pitch / 100.0f)); + pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch(tuning, key) - x) + x; + } + else + { + pitch = voice->gen[GEN_SCALETUNE].val + * (key - voice->root_pitch / 100.0f) + voice->root_pitch; + } - /* Increment voice count */ - voice->channel->synth->active_voice_count++; + return pitch; } -void -fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) +void +fluid_voice_calculate_gen_pitch(fluid_voice_t *voice) { - fluid_tuning_t* tuning; - fluid_real_t x; - - /* 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)) { - tuning = fluid_channel_get_tuning (voice->channel); - x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f)); - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f * - (fluid_tuning_get_pitch (tuning, voice->key) - x) + x; - } else { - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val - * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch; - } - + voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); } + /* * fluid_voice_calculate_runtime_synthesis_parameters * @@ -500,176 +528,206 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) * example, for the pitch since it is modulated by the controllers in * cents. */ static int -fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) +fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice) { - 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); */ - } - - /* 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). */ - UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, - fluid_voice_get_lower_boundary_for_attenuation(voice)); - return FLUID_OK; + int i; + unsigned int n; + + static int const list_of_generators_to_initialize[] = + { + 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, /* --- */ + GEN_CUSTOM_BALANCE, /* --- */ + GEN_CUSTOM_FILTERFC, /* --- */ + GEN_CUSTOM_FILTERQ /* --- */ + }; + + /* 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); + int dest_gen_index = mod->dest; + fluid_gen_t *dest_gen = &voice->gen[dest_gen_index]; + dest_gen->mod += modval; + /* fluid_dump_modulator(mod); */ + } + + /* 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(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++) + { + fluid_voice_update_param(voice, list_of_generators_to_initialize[n]); + } + + /* Start portamento if enabled */ + { + /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. + When fromkey is set to ValidNote , portamento is started */ + /* Return fromkey portamento */ + int fromkey = voice->channel->synth->fromkey_portamento; + + if(fluid_channel_is_valid_note(fromkey)) + { + /* Send portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice)); + } + } + + /* Make an estimate on how loud this voice can get at any time (attenuation). */ + UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, + fluid_voice_get_lower_boundary_for_attenuation(voice)); + return FLUID_OK; } /* * calculate_hold_decay_buffers */ static int -calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, +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; + /* 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 = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (60.0 - fluid_voice_get_actual_key(voice))); + + /* 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; + 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.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; } - } - /* 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. */ + 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); + /* 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; + return buffers; } /* @@ -684,7 +742,7 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, * * 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 + * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum * of all three. */ /** @@ -696,236 +754,245 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, * Most applications will not need this function. */ void -fluid_voice_update_param(fluid_voice_t* voice, int gen) +fluid_voice_update_param(fluid_voice_t *voice, int gen) { - double q_dB; - fluid_real_t x; - fluid_real_t y; - unsigned int count, z; - // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank. - static const float ALT_ATTENUATION_SCALE = 0.4; - - 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; - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right); - break; - - case GEN_ATTENUATION: - voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) + - (fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn; - - /* 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); - UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation); - break; + unsigned int count, z; + fluid_real_t x = fluid_voice_gen_value(voice, gen); + + switch(gen) + { + + case GEN_PAN: + case GEN_CUSTOM_BALANCE: + /* range checking is done in the fluid_pan and fluid_balance functions */ + voice->pan = fluid_voice_gen_value(voice, GEN_PAN); + voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE); + + /* left amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1))); + + /* right amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0))); + break; + + case GEN_ATTENUATION: + voice->attenuation = x; + + /* 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); + UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation); + 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)); - UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch); - 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; - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb); - 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; - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus); - 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->sample != NULL) { - 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; - x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate)); - } else { - if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 - voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f; - else - voice->root_pitch = 0; - x = fluid_ct2hz(voice->root_pitch); - } - /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ - fluid_voice_calculate_gen_pitch(voice); - UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x); - - 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] - */ - x = _GEN(voice, GEN_FILTERFC); - UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_fres, x); - 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; + case GEN_PITCH: + case GEN_COARSETUNE: + case GEN_FINETUNE: + /* The testing for allowed range is done in 'fluid_ct2hz' */ + voice->pitch = (fluid_voice_gen_value(voice, GEN_PITCH) + + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE) + + fluid_voice_gen_value(voice, GEN_FINETUNE)); + UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch); + break; + + case GEN_REVERBSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->reverb_send = x / 1000.0f; + fluid_clip(voice->reverb_send, 0.0, 1.0); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send)); + break; + + case GEN_CHORUSSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->chorus_send = x / 1000.0f; + fluid_clip(voice->chorus_send, 0.0, 1.0); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send)); + 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->sample != NULL) + { + 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; + } + + x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate)); + } + else + { + if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 + { + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f; + } + else + { + voice->root_pitch = 0; + } + + x = fluid_ct2hz(voice->root_pitch); + } + + /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ + fluid_voice_calculate_gen_pitch(voice); + UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x); + + 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] + */ + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x); + break; + + case GEN_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x); + break; + + /* same as the two above, only for the custom filter */ + case GEN_CUSTOM_FILTERFC: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_CUSTOM_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_MODLFOTOPITCH: + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); + break; + + case GEN_MODLFOTOVOL: + fluid_clip(x, -960.0, 960.0); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); + break; + + case GEN_MODLFOTOFILTERFC: + fluid_clip(x, -12000, 12000); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); + break; + + case GEN_MODLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z); + break; + + case GEN_MODLFOFREQ: + /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x); + 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 + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate; + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); + break; + + case GEN_VIBLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); + break; + + case GEN_VIBLFOTOPITCH: + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); + 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. + */ + + /* 2017-09-02: do not change the voice's key here, otherwise it will + * never be released on a noteoff event + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_KEYNUM); - /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ - fluid_clip(q_dB, 0.0f, 96.0f); + if(x >= 0) + { + voice->key = x; + } - /* 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; - UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB); - - break; - - case GEN_MODLFOTOPITCH: - x = _GEN(voice, GEN_MODLFOTOPITCH); - fluid_clip(x, -12000.0, 12000.0); - UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); - break; - - case GEN_MODLFOTOVOL: - x = _GEN(voice, GEN_MODLFOTOVOL); - fluid_clip(x, -960.0, 960.0); - UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); - break; - - case GEN_MODLFOTOFILTERFC: - x = _GEN(voice, GEN_MODLFOTOFILTERFC); - fluid_clip(x, -12000, 12000); - UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); - break; - - case GEN_MODLFODELAY: - x = _GEN(voice, GEN_MODLFODELAY); - fluid_clip(x, -12000.0f, 5000.0f); - z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); - UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z); - 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); - x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); - UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x); - 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); - x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate; - UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); - break; - - case GEN_VIBLFODELAY: - x = _GEN(voice,GEN_VIBLFODELAY); - fluid_clip(x, -12000.0f, 5000.0f); - z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); - UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); - break; - - case GEN_VIBLFOTOPITCH: - x = _GEN(voice, GEN_VIBLFOTOPITCH); - fluid_clip(x, -12000.0, 12000.0); - UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); - 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; +#endif + 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. + */ + /* 2017-09-02: do not change the voice's velocity here, use + * fluid_voice_get_actual_velocity() to get the value of this generator + * if active. + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_VELOCITY); - 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; + if(x > 0) + { + voice->vel = x; + } - case GEN_MODENVTOPITCH: - x = _GEN(voice, GEN_MODENVTOPITCH); - fluid_clip(x, -12000.0, 12000.0); - UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); - break; +#endif + break; - case GEN_MODENVTOFILTERFC: - x = _GEN(voice,GEN_MODENVTOFILTERFC); + case GEN_MODENVTOPITCH: + fluid_clip(x, -12000.0, 12000.0); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); + break; - /* Range: SF2.01 section 8.1.3 # 1 - * Motivation for range checking: - * Filter is reported to make funny noises now and then - */ - fluid_clip(x, -12000.0, 12000.0); - UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x); - break; + case 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(x, -12000.0, 12000.0); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x); + break; /* sample start and ends points @@ -939,45 +1006,59 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * 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) { - z = (voice->sample->start - + (int) _GEN(voice, GEN_STARTADDROFS) - + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); - UPDATE_RVOICE_I1(fluid_rvoice_set_start, z); - } - 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) { - z = (voice->sample->end - + (int) _GEN(voice, GEN_ENDADDROFS) - + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); - UPDATE_RVOICE_I1(fluid_rvoice_set_end, z); - } - 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) { - z = (voice->sample->loopstart - + (int) _GEN(voice, GEN_STARTLOOPADDROFS) - + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); - UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z); - } - 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) { - z = (voice->sample->loopend - + (int) _GEN(voice, GEN_ENDLOOPADDROFS) - + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); - UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z); - } - break; - - /* Conversion functions differ in range limit */ + case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ + case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ + if(voice->sample != NULL) + { + fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS); + fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS); + + z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_start, z); + } + + 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) + { + fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS); + fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS); + + z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_end, z); + } + + 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) + { + fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS); + fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS); + + z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z); + } + + 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) + { + fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS); + fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS); + + z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z); + } + + 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) @@ -988,90 +1069,84 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * - 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); - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDELAY, - count, 0.0f, 0.0f, -1.0f, 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); - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVATTACK, - count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 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 */ - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVHOLD, - count, 1.0f, 0.0f, -1.0f, 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 */ - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDECAY, - count, 1.0f, count ? -1.0f / count : 0.0f, y, 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); - fluid_voice_update_volenv(voice, FLUID_VOICE_ENVRELEASE, - count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f); - break; + case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + count, 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 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 */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 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 */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ + fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 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); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDELAY, - NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 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); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVATTACK, - count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 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 */ - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVHOLD, - count, 1.0f, 0.0f, -1.0f, 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); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDECAY, - count, 1.0f, count ? -1.0f / count : 0.0f, y, 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); - fluid_voice_update_modenv(voice, FLUID_VOICE_ENVRELEASE, - count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f); - - break; - - } /* switch gen */ + case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ + fluid_clip(x, -12000.0f, 5000.0f); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 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 */ + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 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 */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 2.0f); + + break; + + } /* switch gen */ } /** @@ -1099,43 +1174,48 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) * - For every changed generator, convert its value to the correct * unit of the corresponding DSP parameter */ -int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl) +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); + 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); + } + } + + 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; + + return FLUID_OK; } /** @@ -1143,44 +1223,113 @@ int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl) * ALL_CTRL_OFF MIDI message has been received (CC 121). * */ -int fluid_voice_modulate_all(fluid_voice_t* voice) +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_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); + } + } + + 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); } - fluid_gen_set_mod(&voice->gen[gen], modval); + return FLUID_OK; +} - /* Update the parameter values that are depend on the generator - * 'gen' */ - fluid_voice_update_param(voice, gen); - } +/** legato update functions --------------------------------------------------*/ +/* Updates voice portamento parameters + * + * @voice voice the synthesis voice + * @fromkey the beginning pitch of portamento. + * @tokey the ending pitch of portamento. + * + * The function calculates pitch offset and increment, then these parameters + * are send to the dsp. +*/ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey) + +{ + fluid_channel_t *channel = voice->channel; + + /* calculates pitch offset */ + fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey); + fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey); + fluid_real_t pitchoffset = PitchBeg - PitchEnd; + + /* Calculates increment countinc */ + /* Increment is function of PortamentoTime (ms)*/ + unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate * + 0.001f * + (fluid_real_t)fluid_channel_portamentotime(channel)) / + (fluid_real_t)FLUID_BUFSIZE + 0.5); + + /* Send portamento parameters to the voice dsp */ + UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset); +} + +/*---------------------------------------------------------------*/ +/*legato mode 1: multi_retrigger + * + * Modulates all generators dependent of key,vel. + * Forces the voice envelopes in the attack section (legato mode 1). + * + * @voice voice the synthesis voice + * @tokey the new key to be applied to this voice. + * @vel the new velocity to be applied to this voice. + */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Updates generators dependent of velocity */ + /* Modulates GEN_ATTENUATION (and others ) before calling + fluid_rvoice_multi_retrigger_attack().*/ + fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY); + + /* Updates generator dependent of voice->key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + + /* Updates pitch generator */ + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); - return FLUID_OK; + /* updates adsr generator */ + UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); } +/** end of legato update functions */ /* Force the voice into release stage. Useful anywhere a voice @@ -1190,40 +1339,45 @@ int fluid_voice_modulate_all(fluid_voice_t* voice) fluid_voice_noteoff(). */ void -fluid_voice_release(fluid_voice_t* voice) +fluid_voice_release(fluid_voice_t *voice) { - unsigned int at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); + unsigned int at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); voice->has_noteoff = 1; // voice is marked as noteoff occured } /* * fluid_voice_noteoff + * + * Sending a noteoff event will advance the envelopes to section 5 (release). + * The function is convenient for polyphonic or monophonic note */ -int -fluid_voice_noteoff(fluid_voice_t* voice) +void +fluid_voice_noteoff(fluid_voice_t *voice) { - fluid_channel_t* channel; - - fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref); - - channel = voice->channel; - - /* Sustain a note under Sostenuto pedal */ - if (fluid_channel_sostenuto(channel) && - channel->sostenuto_orderid > voice->id) - { // Sostenuto depressed after note - voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; - } - /* Or sustain a note under Sustain pedal */ - else if (fluid_channel_sustained(channel)) { - voice->status = FLUID_VOICE_SUSTAINED; - } - /* Or force the voice to release stage */ - else - fluid_voice_release(voice); - - return FLUID_OK; + fluid_channel_t *channel; + + fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0); + + channel = voice->channel; + + /* Sustain a note under Sostenuto pedal */ + if(fluid_channel_sostenuto(channel) && + channel->sostenuto_orderid > voice->id) + { + // Sostenuto depressed after note + voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; + } + /* Or sustain a note under Sustain pedal */ + else if(fluid_channel_sustained(channel)) + { + voice->status = FLUID_VOICE_SUSTAINED; + } + /* Or force the voice to release stage */ + else + { + fluid_voice_release(voice); + } } /* @@ -1239,73 +1393,84 @@ fluid_voice_noteoff(fluid_voice_t* voice) */ int -fluid_voice_kill_excl(fluid_voice_t* voice){ +fluid_voice_kill_excl(fluid_voice_t *voice) +{ - unsigned int at_tick; + unsigned int at_tick; - if (!_PLAYING(voice)) { - return FLUID_OK; - } + if(!fluid_voice_is_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); + /* 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); - /* 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 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); + /* Speed up the modulation envelope */ + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_MODENVRELEASE); - at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); - UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); + at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); - return FLUID_OK; + return FLUID_OK; } /* - * Called by fluid_synth when the overflow rvoice can be reclaimed. + * Called by fluid_synth when the overflow rvoice can be reclaimed. */ -void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice) +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) { - voice->can_access_overflow_rvoice = 1; - fluid_sample_null_ptr(&voice->overflow_rvoice->dsp.sample); + voice->can_access_overflow_rvoice = 1; + fluid_voice_sample_unref(&voice->overflow_rvoice->dsp.sample); } - /* * fluid_voice_off * + * Force the voice into finished stage. Useful anywhere a voice + * needs to be cancelled from MIDI API. + */ +void fluid_voice_off(fluid_voice_t *voice) +{ + UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */ +} + +/* + * fluid_voice_stop + * * Purpose: - * Turns off a voice, meaning that it is not processed - * anymore by the DSP loop. + * Turns off a voice, meaning that it is not processed anymore by the + * DSP loop, i.e. contrary part to fluid_voice_start(). */ -int -fluid_voice_off(fluid_voice_t* voice) +void +fluid_voice_stop(fluid_voice_t *voice) { - fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0); - voice->chan = NO_CHANNEL; - UPDATE_RVOICE0(fluid_rvoice_voiceoff); - - if (voice->can_access_rvoice) - fluid_sample_null_ptr(&voice->rvoice->dsp.sample); + voice->chan = NO_CHANNEL; - voice->status = FLUID_VOICE_OFF; - voice->has_noteoff = 1; + if(voice->can_access_rvoice) + { + fluid_voice_sample_unref(&voice->rvoice->dsp.sample); + } - /* Decrement the reference count of the sample. */ - fluid_sample_null_ptr(&voice->sample); + voice->status = FLUID_VOICE_OFF; + voice->has_noteoff = 1; - /* Decrement voice count */ - voice->channel->synth->active_voice_count--; + /* Decrement the reference count of the sample. */ + fluid_voice_sample_unref(&voice->sample); - return FLUID_OK; + /* Decrement voice count */ + voice->channel->synth->active_voice_count--; } /** @@ -1319,57 +1484,70 @@ fluid_voice_off(fluid_voice_t* voice) * exist so don't check. */ void -fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode) +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; - } + 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 != FLUID_MOD_NONE) /* SF2.01 section 8.2.1: Constant value */ + && (mod->src1 != FLUID_MOD_VELOCITY) /* Note-on velocity */ + && (mod->src1 != FLUID_MOD_KEY) /* Note-on key number */ + && (mod->src1 != FLUID_MOD_KEYPRESSURE) /* Poly pressure */ + && (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */ + && (mod->src1 != FLUID_MOD_PITCHWHEEL) /* Pitch wheel */ + && (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */ + { + FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); + return; } - } else if (mode == FLUID_VOICE_OVERWRITE) { + 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; + } + } - /* 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); - } + 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); + } + else + { + FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id); + } } /** @@ -1388,19 +1566,134 @@ fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode) * * Otherwise the voice has finished playing. */ -unsigned int fluid_voice_get_id(fluid_voice_t* voice) +unsigned int fluid_voice_get_id(const fluid_voice_t *voice) { - return voice->id; + return voice->id; } /** - * Check if a voice is still playing. + * Check if a voice is producing sound. This is also true after a voice received a noteoff as it may be playing in release phase. * @param voice Voice instance * @return TRUE if playing, FALSE otherwise */ -int fluid_voice_is_playing(fluid_voice_t* voice) +int fluid_voice_is_playing(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON) + || fluid_voice_is_sustained(voice) + || fluid_voice_is_sostenuto(voice); + +} + +/** + * Check if a voice is ON. A voice is ON, if it has not yet received a noteoff event. + * @param voice Voice instance + * @return TRUE if on, FALSE otherwise + * @since 1.1.7 + */ +int fluid_voice_is_on(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sustain. + * @param voice Voice instance + * @return TRUE if sustained, FALSE otherwise + * @since 1.1.7 + */ +int fluid_voice_is_sustained(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_SUSTAINED); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto. + * @param voice Voice instance + * @return TRUE if sostenuto, FALSE otherwise + * @since 1.1.7 + */ +int fluid_voice_is_sostenuto(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO); +} + +/** + * If the voice is playing, gets the midi channel the voice is playing on. Else the result is undefined. + * @param voice Voice instance + * @return The channel assigned to this voice + * @since 1.1.7 + */ +int fluid_voice_get_channel(const fluid_voice_t *voice) +{ + return voice->chan; +} + +/** + * If the voice is playing, gets the midi key the voice is actually playing at. Else the result is undefined. + * If the voice was started from an instrument which uses a fixed key generator, it returns that. + * Else returns the same as \c fluid_voice_get_key. + * @param voice Voice instance + * @return The midi key this voice is playing at + * @since 1.1.7 + */ +int fluid_voice_get_actual_key(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM); + + if(x >= 0) + { + return (int)x; + } + else + { + return fluid_voice_get_key(voice); + } +} + +/** + * If the voice is playing, gets the midi key from the noteon event, by which the voice was initially turned on with. + * Else the result is undefined. + * @param voice Voice instance + * @return The midi key of the noteon event that originally turned on this voice + * @since 1.1.7 + */ +int fluid_voice_get_key(const fluid_voice_t *voice) +{ + return voice->key; +} + +/** + * If the voice is playing, gets the midi velocity the voice is actually playing at. Else the result is undefined. + * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. + * Else returns the same as \c fluid_voice_get_velocity. + * @param voice Voice instance + * @return The midi velocity this voice is playing at + * @since 1.1.7 + */ +int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY); + + if(x > 0) + { + return (int)x; + } + else + { + return fluid_voice_get_velocity(voice); + } +} + +/** + * If the voice is playing, gets the midi velocity from the noteon event, by which the voice was initially + * turned on with. Else the result is undefined. + * @param voice Voice instance + * @return The midi velocity which originally turned on this voice + * @since 1.1.7 + */ +int fluid_voice_get_velocity(const fluid_voice_t *voice) { - return _PLAYING(voice); + return voice->vel; } /* @@ -1416,85 +1709,105 @@ int fluid_voice_is_playing(fluid_voice_t* voice) * voice->attenuation has to be initialized. */ static fluid_real_t -fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice) +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); - } + 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) + || (mod->src1 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src1 == FLUID_MOD_KEYPRESSURE) + || (mod->src1 == FLUID_MOD_PITCHWHEEL) + || (mod->src2 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src2 == FLUID_MOD_KEYPRESSURE) + || (mod->src2 == FLUID_MOD_PITCHWHEEL))) + { + + fluid_real_t current_val = fluid_mod_get_value(mod, 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; + 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; + /* SF2.01 specs do not allow negative attenuation */ + if(lower_bound < 0) + { + lower_bound = 0; + } + + return lower_bound; } -int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) +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; + 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) +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; - - UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb); - UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus); - - return FLUID_OK; + fluid_real_t left, right, reverb, chorus; + + /* avoid division by zero*/ + if(gain < 0.0000001) + { + gain = 0.0000001; + } + + voice->synth_gain = gain; + left = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)); + right = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)); + reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send); + chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send); + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, chorus); + + return FLUID_OK; } /* - Scan the loop @@ -1514,113 +1827,162 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) * fluid_voice_optimize_sample() on each sample once. */ int -fluid_voice_optimize_sample(fluid_sample_t* s) +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; - } + int32_t peak_max = 0; + int32_t peak_min = 0; + int32_t peak; + fluid_real_t normalized_amplitude_during_loop; + double result; + unsigned int i; + + /* ignore disabled samples */ + if(s->start == s->end) + { + return (FLUID_OK); } - /* 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(!s->amplitude_that_reaches_noise_floor_is_valid) /* Only once */ + { + /* Scan the loop */ + for(i = s->loopstart; i < s->loopend; i++) + { + int32_t val = fluid_rvoice_get_sample(s->data, s->data24, 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) / (INT24_MAX * 1.0f); + 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); + printf("Sample peak detection: factor %f\n", (double)result); #endif - }; - return FLUID_OK; + } + + return FLUID_OK; } -fluid_real_t -fluid_voice_get_overflow_prio(fluid_voice_t* voice, - fluid_overflow_prio_t* score, - unsigned int cur_time) +float +fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time) { - fluid_real_t this_voice_prio = 0; - - /* Are we already overflowing? */ - if (!voice->can_access_overflow_rvoice) { - return OVERFLOW_PRIO_CANNOT_KILL; - } - - /* Is this voice on the drum channel? - * Then it is very important. - * Also skip the released and sustained scores. - */ - if (voice->channel->channel_type == CHANNEL_TYPE_DRUM){ - this_voice_prio += score->percussion; - } - else if (voice->has_noteoff) { - /* Noteoff has */ - this_voice_prio += score->released; - } else if (_SUSTAINED(voice) || _HELD_BY_SOSTENUTO(voice)) { - /* This voice is still active, since the sustain pedal is held down. - * 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. + float this_voice_prio = 0; + int channel; + + /* Are we already overflowing? */ + if(!voice->can_access_overflow_rvoice) + { + return OVERFLOW_PRIO_CANNOT_KILL; + } + + /* Is this voice on the drum channel? + * Then it is very important. + * Also skip the released and sustained scores. */ - this_voice_prio += score->sustained; - } - - /* 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 give newer voices a higher score. */ - if (score->age) { - cur_time -= voice->start_time; - if (cur_time < 1) - cur_time = 1; // Avoid div by zero - this_voice_prio += (score->age * voice->output_rate) / cur_time; - } - - /* take a rough estimate of loudness into account. Louder voices are more important. */ - if (score->volume) { - fluid_real_t a = voice->attenuation; - if (voice->has_noteoff) { - // FIXME: Should take into account where on the envelope we are...? + if(voice->channel->channel_type == CHANNEL_TYPE_DRUM) + { + this_voice_prio += score->percussion; } - if (a < 0.1) - a = 0.1; // Avoid div by zero - this_voice_prio += score->volume / a; + else if(voice->has_noteoff) + { + /* Noteoff has */ + this_voice_prio += score->released; } - - return this_voice_prio; + else if(fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)) + { + /* This voice is still active, since the sustain pedal is held down. + * 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 += score->sustained; + } + + /* 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 give newer voices a higher score. */ + if(score->age) + { + cur_time -= voice->start_time; + + if(cur_time < 1) + { + cur_time = 1; // Avoid div by zero + } + + this_voice_prio += (score->age * voice->output_rate) / cur_time; + } + + /* take a rough estimate of loudness into account. Louder voices are more important. */ + if(score->volume) + { + fluid_real_t a = voice->attenuation; + + if(voice->has_noteoff) + { + // FIXME: Should take into account where on the envelope we are...? + } + + if(a < 0.1) + { + a = 0.1; // Avoid div by zero + } + + this_voice_prio += score->volume / a; + } + + /* Check if this voice is on an important channel. If so, then add the + * score for important channels */ + channel = fluid_voice_get_channel(voice); + + if(channel < score->num_important_channels && score->important_channels[channel]) + { + this_voice_prio += score->important; + } + + return this_voice_prio; +} + + +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags) +{ + UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags); } + diff --git a/libs/fluidsynth/src/fluid_voice.h b/libs/fluidsynth/src/fluid_voice.h index c43fe59738..6038a1a9f3 100644 --- a/libs/fluidsynth/src/fluid_voice.h +++ b/libs/fluidsynth/src/fluid_voice.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -29,28 +29,32 @@ #include "fluid_adsr_env.h" #include "fluid_lfo.h" #include "fluid_rvoice.h" +#include "fluid_rvoice_event.h" #include "fluid_sys.h" #define NO_CHANNEL 0xff typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; -struct _fluid_overflow_prio_t +struct _fluid_overflow_prio_t { - fluid_real_t percussion; /**< Is this voice on the drum channel? Then add this score */ - fluid_real_t released; /**< Is this voice in release stage? Then add this score (usually negative) */ - fluid_real_t sustained; /**< Is this voice sustained? Then add this score (usually negative) */ - fluid_real_t volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ - fluid_real_t age; /**< This score will be divided by the number of seconds the voice has lasted */ + float percussion; /**< Is this voice on the drum channel? Then add this score */ + float released; /**< Is this voice in release stage? Then add this score (usually negative) */ + float sustained; /**< Is this voice sustained? Then add this score (usually negative) */ + float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ + float age; /**< This score will be divided by the number of seconds the voice has lasted */ + float important; /**< This score will be added to all important channels */ + char *important_channels; /**< "important" flags indexed by MIDI channel number */ + int num_important_channels; /**< Number of elements in the important_channels array */ }; enum fluid_voice_status { - FLUID_VOICE_CLEAN, - FLUID_VOICE_ON, - FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */ - FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */ - FLUID_VOICE_OFF + FLUID_VOICE_CLEAN, + FLUID_VOICE_ON, + FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */ + FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */ + FLUID_VOICE_OFF }; @@ -59,170 +63,135 @@ enum fluid_voice_status */ struct _fluid_voice_t { - unsigned int id; /* the id is incremented for every new noteon. + 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 access 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; - fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */ - - int has_noteoff; /* Flag set when noteoff has been sent */ - - /* basic parameters */ - fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ - - unsigned int start_time; - fluid_adsr_env_t volenv; /* Volume envelope (dupe in rvoice) */ - - /* basic parameters */ - fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ - fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ - fluid_real_t root_pitch; - - /* master gain (dupe in rvoice) */ - fluid_real_t synth_gain; - - /* 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; - - /* rvoice control */ - fluid_rvoice_t* rvoice; - fluid_rvoice_t* overflow_rvoice; /* Used temporarily and only in overflow situations */ - int can_access_rvoice; /* False if rvoice is being rendered in separate thread */ - int can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ - - /* for debugging */ - int debug; - double ref; + unsigned char status; + unsigned char chan; /* the channel number, quick access for channel messages */ + unsigned char key; /* the key of the noteon event, quick access for noteoff */ + unsigned char vel; /* the velocity of the noteon event */ + fluid_channel_t *channel; + fluid_rvoice_eventhandler_t *eventhandler; + fluid_zone_range_t *zone_range; /* instrument zone range*/ + fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + + unsigned int start_time; + int mod_count; + fluid_mod_t mod[FLUID_NUM_MOD]; + fluid_gen_t gen[GEN_LAST]; + + /* basic parameters */ + fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ + + /* basic parameters */ + fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ + fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ + fluid_real_t root_pitch; + + /* master gain (dupe in rvoice) */ + fluid_real_t synth_gain; + + /* pan */ + fluid_real_t pan; + + /* balance */ + fluid_real_t balance; + + /* reverb */ + fluid_real_t reverb_send; + + /* chorus */ + fluid_real_t chorus_send; + + /* rvoice control */ + fluid_rvoice_t *rvoice; + fluid_rvoice_t *overflow_rvoice; /* Used temporarily and only in overflow situations */ + char can_access_rvoice; /* False if rvoice is being rendered in separate thread */ + char can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ + char has_noteoff; /* Flag set when noteoff has been sent */ + +#ifdef WITH_PROFILING + /* for debugging */ + double ref; +#endif }; -fluid_voice_t* new_fluid_voice(fluid_real_t output_rate); -int delete_fluid_voice(fluid_voice_t* voice); +fluid_voice_t *new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate); +void delete_fluid_voice(fluid_voice_t *voice); -void fluid_voice_start(fluid_voice_t* voice); -void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice); +void fluid_voice_start(fluid_voice_t *voice); +void fluid_voice_calculate_gen_pitch(fluid_voice_t *voice); -int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf); +int fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + fluid_channel_t *channel, int key, int vel, + unsigned int id, unsigned int time, fluid_real_t gain); -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); +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); +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); +int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain); -int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value); +void fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value); /** 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); - -/** fluid_voice_release - Force the voice into release stage. Usefuf anywhere a voice - needs to be damped even if pedals (sustain sostenuto) are depressed. - See fluid_synth_damp_voices_LOCAL(), fluid_synth_damp_voices_by_sostenuto_LOCAL, - fluid_voice_noteoff(), fluid_synth_stop_LOCAL(). -*/ -void fluid_voice_release(fluid_voice_t* voice); -int fluid_voice_noteoff(fluid_voice_t* voice); -int fluid_voice_off(fluid_voice_t* voice); -void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice); -void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, - fluid_real_t* left_buf, fluid_real_t* right_buf, - fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); - -int fluid_voice_kill_excl(fluid_voice_t* voice); -fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice, - fluid_overflow_prio_t* score, - unsigned int cur_time); +void fluid_voice_update_param(fluid_voice_t *voice, int gen); + +/** legato modes */ +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel); +/* Update portamento parameter */ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey); + + +void fluid_voice_release(fluid_voice_t *voice); +void fluid_voice_noteoff(fluid_voice_t *voice); +void fluid_voice_off(fluid_voice_t *voice); +void fluid_voice_stop(fluid_voice_t *voice); +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice); + +int fluid_voice_kill_excl(fluid_voice_t *voice); +float fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time); #define OVERFLOW_PRIO_CANNOT_KILL 999999. /** * Locks the rvoice for rendering, so it can't be modified directly */ -static FLUID_INLINE fluid_rvoice_t* -fluid_voice_lock_rvoice(fluid_voice_t* voice) +static FLUID_INLINE void +fluid_voice_lock_rvoice(fluid_voice_t *voice) { - voice->can_access_rvoice = 0; - return voice->rvoice; + voice->can_access_rvoice = 0; } /** * Unlocks the rvoice for rendering, so it can be modified directly */ -static FLUID_INLINE void -fluid_voice_unlock_rvoice(fluid_voice_t* voice) +static FLUID_INLINE void +fluid_voice_unlock_rvoice(fluid_voice_t *voice) { - voice->can_access_rvoice = 1; + voice->can_access_rvoice = 1; } - -#define fluid_voice_get_channel(voice) ((voice)->channel) - - -#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) || \ - _SUSTAINED(voice) || \ - _HELD_BY_SOSTENUTO(voice) ) - -/* 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->has_noteoff) -#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED) -#define _HELD_BY_SOSTENUTO(voice) ((voice)->status == FLUID_VOICE_HELD_BY_SOSTENUTO) #define _AVAILABLE(voice) ((voice)->can_access_rvoice && \ (((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) -/* FIXME - This doesn't seem to be used anywhere - JG */ -fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); - -#define fluid_voice_get_loudness(voice) (fluid_adsr_env_get_max_val(&voice->volenv)) - -#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) - -/* defined in fluid_dsp_float.c */ +fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num); +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags); -void fluid_dsp_float_config (void); -int fluid_dsp_float_interpolate_none (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice); -int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice); #endif /* _FLUID_VOICE_H */ diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h index b01618df26..d500f6174e 100644 --- a/libs/fluidsynth/src/fluidsynth_priv.h +++ b/libs/fluidsynth/src/fluidsynth_priv.h @@ -3,16 +3,16 @@ * 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 + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -24,9 +24,7 @@ #include -#if HAVE_CONFIG_H #include "config.h" -#endif #if HAVE_STRING_H #include @@ -100,40 +98,61 @@ #include #endif +#if HAVE_OPENMP +#include +#endif + #if HAVE_IO_H #include #endif -#if HAVE_WINDOWS_H -#include +#if HAVE_SIGNAL_H +#include #endif -/* MinGW32 special defines */ -#ifdef MINGW32 - +/** Integer types */ +#if HAVE_STDINT_H #include -#define snprintf _snprintf -#define vsnprintf _vsnprintf +#else + +/* Assume GLIB types */ +typedef gint8 int8_t; +typedef guint8 uint8_t; +typedef gint16 int16_t; +typedef guint16 uint16_t; +typedef gint32 int32_t; +typedef guint32 uint32_t; +typedef gint64 int64_t; +typedef guint64 uint64_t; + +#endif + +#if defined(WIN32) && HAVE_WINDOWS_H +//#include +//#include /* Provides also socklen_t */ +#include + +/* WIN32 special defines */ #define DSOUND_SUPPORT 1 #define WINMIDI_SUPPORT 1 #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 -#elif defined _MSC_VER - -#define STDIN_FILENO _fileno(stdin) -#define STDOUT_FILENO _fileno(stdout) -#define STDERR_FILENO _fileno(stderr) +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#pragma warning(disable : 4101) +#pragma warning(disable : 4305) +#pragma warning(disable : 4996) +#endif #endif /* Darwin special defines (taken from config_macosx.h) */ #ifdef DARWIN -#define MACINTOSH -#define __Types__ -#define WITHOUT_SERVER 1 +# define MACINTOSH +# define __Types__ #endif @@ -156,7 +175,6 @@ typedef double fluid_real_t; typedef SOCKET fluid_socket_t; #else typedef int fluid_socket_t; -#define INVALID_SOCKET -1 #endif #if defined(SUPPORTS_VLA) @@ -168,15 +186,10 @@ typedef int fluid_socket_t; #endif -/** Integer types */ -//typedef gint8 sint8; -typedef guint8 uint8; -//typedef gint16 sint16; -//typedef guint16 uint16; -typedef gint32 sint32; -typedef guint32 uint32; -//typedef gint64 sint64; -//typedef guint64 uint64; +/** Atomic types */ +typedef int fluid_atomic_int_t; +typedef unsigned int fluid_atomic_uint_t; +typedef float fluid_atomic_float_t; /*************************************************************** @@ -191,6 +204,25 @@ 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; typedef struct _fluid_sample_timer_t fluid_sample_timer_t; +typedef struct _fluid_zone_range_t fluid_zone_range_t; +typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; + +/* Declare rvoice related typedefs here instead of fluid_rvoice.h, as it's needed + * in fluid_lfo.c and fluid_adsr.c as well */ +typedef union _fluid_rvoice_param_t +{ + void *ptr; + int i; + fluid_real_t real; +} fluid_rvoice_param_t; +enum { MAX_EVENT_PARAMS = 6 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ +typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); + +/* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access + * those params that were previously set in fluid_voice.c + */ +#define DECLARE_FLUID_RVOICE_FUNCTION(name) void name(void* obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) + /*************************************************************** * @@ -198,48 +230,84 @@ typedef struct _fluid_sample_timer_t fluid_sample_timer_t; */ #define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ +#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) /**< Number of buffers that can be processed in one rendering run */ #define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ #define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ #define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ #define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */ #define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */ - -#ifndef PI -#define PI 3.141592654 -#endif +#define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ /*************************************************************** * * SYSTEM INTERFACE */ -typedef FILE* fluid_file; +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_ARRAY_ALIGNED(_t,_n,_a) (_t*)malloc((_n)*sizeof(_t) + ((unsigned int)_a - 1u)) +#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u) #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_FTELL(_f) ftell(_f) #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_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n) + +#define FLUID_STRNCPY(_dst,_src,_n) \ +do { strncpy(_dst,_src,_n); \ + (_dst)[(_n)-1]=0; \ +}while(0) + #define FLUID_STRCHR(_s,_c) strchr(_s,_c) #define FLUID_STRRCHR(_s,_c) strrchr(_s,_c) + #ifdef strdup -#define FLUID_STRDUP(s) strdup(s) +#define FLUID_STRDUP(s) strdup(s) #else -#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) +#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) #endif + #define FLUID_SPRINTF sprintf #define FLUID_FPRINTF fprintf +#if (defined(WIN32) && _MSC_VER < 1900) || defined(MINGW32) +/* need to make sure we use a C99 compliant implementation of (v)snprintf(), + * i.e. not microsofts non compliant extension _snprintf() as it doesnt + * reliably null-terminates the buffer + */ +#define FLUID_SNPRINTF g_snprintf +#else +#define FLUID_SNPRINTF snprintf +#endif + +#if (defined(WIN32) && _MSC_VER < 1500) || defined(MINGW32) +#define FLUID_VSNPRINTF g_vsnprintf +#else +#define FLUID_VSNPRINTF vsnprintf +#endif + +#if defined(WIN32) && !defined(MINGW32) +#define FLUID_STRCASECMP _stricmp +#else +#define FLUID_STRCASECMP strcasecmp +#endif + +#if defined(WIN32) && !defined(MINGW32) +#define FLUID_STRNCASECMP _strnicmp +#else +#define FLUID_STRNCASECMP strncasecmp +#endif + + #define fluid_clip(_val, _min, _max) \ { (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } @@ -251,21 +319,38 @@ typedef FILE* fluid_file; #define FLUID_FLUSH() fflush(stdout) #endif +/* People who want to reduce the size of the may do this by entirely + * removing the logging system. This will cause all log messages to + * be discarded at compile time, allowing to save about 80 KiB for + * the compiled binary. + */ +#if 0 +#define FLUID_LOG (void)sizeof +#else #define FLUID_LOG fluid_log +#endif #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530941723212145818 +#endif -#define FLUID_ASSERT(a,b) -#define FLUID_ASSERT_P(a,b) - -char* fluid_error(void); +#ifndef M_LN10 +#define M_LN10 2.3025850929940456840179914546844 +#endif +#ifdef DEBUG +#define FLUID_ASSERT(a) g_assert(a) +#else +#define FLUID_ASSERT(a) +#endif -/* Internationalization */ -#define _(s) s +#define FLUID_LIKELY G_LIKELY +#define FLUID_UNLIKELY G_UNLIKELY +char *fluid_error(void); #endif /* _FLUIDSYNTH_PRIV_H */ diff --git a/libs/fluidsynth/wscript b/libs/fluidsynth/wscript index ebce6efcb4..9b6422c341 100644 --- a/libs/fluidsynth/wscript +++ b/libs/fluidsynth/wscript @@ -5,9 +5,9 @@ import os import sys # Version of this package (even if built as a child) -MAJOR = '1' -MINOR = '1' -MICRO = '6' +MAJOR = '2' +MINOR = '0' +MICRO = '1' LIBFLUIDSYNTH_VERSION = "%s.%s.%s" % (MAJOR, MINOR, MICRO) # Variables for 'waf dist' @@ -58,7 +58,11 @@ def build(bld): 'src/fluid_hash.c', 'src/fluid_list.c', 'src/fluid_ringbuffer.c', + 'src/fluid_samplecache.c', 'src/fluid_settings.c', + 'src/fluid_sffile.c', + 'src/fluid_sfont.c', + 'src/fluid_synth_monopoly.c', 'src/fluid_sys.c' ], cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden' ], -- cgit v1.2.3