Path: blob/master/libs/fluidsynth/src/synth/fluid_synth.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_synth.h"21#include "fluid_sys.h"22#include "fluid_chan.h"23#include "fluid_tuning.h"24#include "fluid_settings.h"25#include "fluid_sfont.h"26#include "fluid_defsfont.h"27#include "fluid_instpatch.h"2829#ifdef TRAP_ON_FPE30#define _GNU_SOURCE31#include <fenv.h>3233/* seems to not be declared in fenv.h */34extern int feenableexcept(int excepts);35#endif3637#define FLUID_API_RETURN(return_value) \38do { fluid_synth_api_exit(synth); \39return return_value; } while (0)4041#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \42do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \43{} \44else \45{ FLUID_API_RETURN(return_value); } \46} while (0)4748#define FLUID_API_ENTRY_CHAN(fail_value) \49fluid_return_val_if_fail (synth != NULL, fail_value); \50fluid_return_val_if_fail (chan >= 0, fail_value); \51fluid_synth_api_enter(synth); \52if (chan >= synth->midi_channels) { \53FLUID_API_RETURN(fail_value); \54} \5556static void fluid_synth_init(void);57static void fluid_synth_api_enter(fluid_synth_t *synth);58static void fluid_synth_api_exit(fluid_synth_t *synth);5960static int fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key,61int vel);62static int fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key);63static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num);64static int fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data,65int len, char *response,66int *response_len, int avail_response,67int *handled, int dryrun);68static int fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data,69int len, char *response,70int *response_len, int avail_response,71int *handled, int dryrun);72static int fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data,73int len, char *response,74int *response_len, int avail_response,75int *handled, int dryrun);76int fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan);77static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan);78static int fluid_synth_system_reset_LOCAL(fluid_synth_t *synth);79static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan,80int is_cc, int ctrl);81static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan);82static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int channum);83static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key);84static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan);85static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan);86static int fluid_synth_set_preset(fluid_synth_t *synth, int chan,87fluid_preset_t *preset);88static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group,89int param, double *value);90static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group,91int param, double *value);9293static fluid_preset_t *94fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum,95int banknum, int prognum);96static fluid_preset_t *97fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname,98int banknum, int prognum);99100static void fluid_synth_update_presets(fluid_synth_t *synth);101static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth);102static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony);103static void init_dither(void);104static FLUID_INLINE int16_t round_clip_to_i16(float x);105static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount);106107static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth);108static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth,109fluid_voice_t *new_voice);110static int fluid_synth_sfunload_callback(void *data, unsigned int msec);111static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth,112int bank, int prog);113static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth,114fluid_tuning_t *tuning,115int bank, int prog, int apply);116static void fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth,117fluid_tuning_t *old_tuning,118fluid_tuning_t *new_tuning,119int apply, int unref_new);120static void fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth,121fluid_channel_t *channel);122static int fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan,123fluid_tuning_t *tuning, int apply);124static void fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan,125int param, float value);126static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id);127128129static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels);130131static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data, int data_lsb);132133/* Callback handlers for real-time settings */134static void fluid_synth_handle_gain(void *data, const char *name, double value);135static void fluid_synth_handle_polyphony(void *data, const char *name, int value);136static void fluid_synth_handle_device_id(void *data, const char *name, int value);137static void fluid_synth_handle_overflow(void *data, const char *name, double value);138static void fluid_synth_handle_important_channels(void *data, const char *name,139const char *value);140static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value);141static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value);142143144static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan);145static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val);146static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val);147148/***************************************************************149*150* GLOBAL151*/152153/* has the synth module been initialized? */154/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */155static fluid_atomic_int_t fluid_synth_initialized = {0};156157/* default modulators158* SF2.01 page 52 ff:159*160* There is a set of predefined default modulators. They have to be161* explicitly overridden by the sound font in order to turn them off.162*/163164static fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */165/*not static */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */166static fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */167static fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */168static fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */169static fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */170static fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */171static fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */172static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */173static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */174static fluid_mod_t custom_balance_mod; /* Non-standard modulator */175176177/* custom_breath2att_modulator is not a default modulator specified in SF178it is intended to replace default_vel2att_mod on demand using179API fluid_set_breath_mode() or command shell setbreathmode.180*/181static fluid_mod_t custom_breath2att_mod;182183/* reverb presets */184static const fluid_revmodel_presets_t revmodel_preset[] =185{186/* name */ /* roomsize */ /* damp */ /* width */ /* level */187{ "Test 1", 0.2f, 0.0f, 0.5f, 0.9f },188{ "Test 2", 0.4f, 0.2f, 0.5f, 0.8f },189{ "Test 3", 0.6f, 0.4f, 0.5f, 0.7f },190{ "Test 4", 0.8f, 0.7f, 0.5f, 0.6f },191{ "Test 5", 0.8f, 1.0f, 0.5f, 0.5f },192};193194195/***************************************************************196*197* INITIALIZATION & UTILITIES198*/199200void fluid_synth_settings(fluid_settings_t *settings)201{202fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED);203204fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED);205fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0, 1.0, 0);206fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0, 1.0, 0);207fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0, 100.0, 0);208fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0, 1.0, 0);209210fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED);211fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0);212fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0, 10.0, 0);213fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1, 5.0, 0);214fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0, 256.0, 0);215216fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED);217fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED);218fluid_settings_register_str(settings, "midi.portname", "", 0);219220#ifdef DEFAULT_SOUNDFONT221fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0);222#endif223224fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0);225fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0);226fluid_settings_register_num(settings, "synth.gain", 0.2, 0.0, 10.0, 0);227fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0);228fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0);229fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0);230fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0);231fluid_settings_register_num(settings, "synth.sample-rate", 44100.0, 8000.0, 96000.0, 0);232fluid_settings_register_int(settings, "synth.device-id", 16, 0, 127, 0);233#ifdef ENABLE_MIXER_THREADS234fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0);235#else236fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0);237#endif238239fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);240241fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);242243fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0);244fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0);245fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0);246fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0);247fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0);248fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0);249fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0);250251fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0);252fluid_settings_add_option(settings, "synth.midi-bank-select", "gm");253fluid_settings_add_option(settings, "synth.midi-bank-select", "gs");254fluid_settings_add_option(settings, "synth.midi-bank-select", "xg");255fluid_settings_add_option(settings, "synth.midi-bank-select", "mma");256257fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED);258}259260/**261* Get FluidSynth runtime version.262* @param major Location to store major number263* @param minor Location to store minor number264* @param micro Location to store micro number265*/266void fluid_version(int *major, int *minor, int *micro)267{268*major = FLUIDSYNTH_VERSION_MAJOR;269*minor = FLUIDSYNTH_VERSION_MINOR;270*micro = FLUIDSYNTH_VERSION_MICRO;271}272273/**274* Get FluidSynth runtime version as a string.275* @return FluidSynth version string, which is internal and should not be276* modified or freed.277*/278char *279fluid_version_str(void)280{281return FLUIDSYNTH_VERSION;282}283284/*285* void fluid_synth_init286*287* Does all the initialization for this module.288*/289static void290fluid_synth_init(void)291{292#ifdef TRAP_ON_FPE293#if !defined(__GLIBC__) && defined(__linux__)294#warning "Trap on FPE is only supported when using glibc!"295#else296/* Turn on floating point exception traps */297feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID);298#endif299#endif300301init_dither();302303#if 0 /* unused in Wine */304/* custom_breath2att_mod is not a default modulator specified in SF2.01.305it is intended to replace default_vel2att_mod on demand using306API fluid_set_breath_mode() or command shell setbreathmode.307*/308fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */309BREATH_MSB, /* Source. breath MSB corresponds to 2. */310FLUID_MOD_CC /* MIDI continuous controller */311| FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */312| FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */313| FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */314);315fluid_mod_set_source2(&custom_breath2att_mod, 0, 0); /* No 2nd source */316fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */317fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */318319/* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */320fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */321FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */322FLUID_MOD_GC /* Not a MIDI continuous controller */323| FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */324| FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */325| FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */326);327fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */328fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */329fluid_mod_set_amount(&default_vel2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */330331332333/* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff334* Have to make a design decision here. The specs don't make any sense this way or another.335* One sound font, 'Kingston Piano', which has been praised for its quality, tries to336* override this modulator with an amount of 0 and positive polarity (instead of what337* the specs say, D=1) for the secondary source.338* So if we change the polarity to 'positive', one of the best free sound fonts works...339*/340fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */341FLUID_MOD_GC /* CC=0 */342| FLUID_MOD_LINEAR /* type=0 */343| FLUID_MOD_UNIPOLAR /* P=0 */344| FLUID_MOD_NEGATIVE /* D=1 */345);346fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */347FLUID_MOD_GC /* CC=0 */348| FLUID_MOD_SWITCH /* type=3 */349| FLUID_MOD_UNIPOLAR /* P=0 */350// do not remove | FLUID_MOD_NEGATIVE /* D=1 */351| FLUID_MOD_POSITIVE /* D=0 */352);353fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */354fluid_mod_set_amount(&default_vel2filter_mod, -2400);355356357358/* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */359fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */360FLUID_MOD_GC /* CC=0 */361| FLUID_MOD_LINEAR /* type=0 */362| FLUID_MOD_UNIPOLAR /* P=0 */363| FLUID_MOD_POSITIVE /* D=0 */364);365fluid_mod_set_source2(&default_at2viblfo_mod, 0, 0); /* no second source */366fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */367fluid_mod_set_amount(&default_at2viblfo_mod, 50);368369370371/* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */372fluid_mod_set_source1(&default_mod2viblfo_mod, MODULATION_MSB, /* Index=1 */373FLUID_MOD_CC /* CC=1 */374| FLUID_MOD_LINEAR /* type=0 */375| FLUID_MOD_UNIPOLAR /* P=0 */376| FLUID_MOD_POSITIVE /* D=0 */377);378fluid_mod_set_source2(&default_mod2viblfo_mod, 0, 0); /* no second source */379fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */380fluid_mod_set_amount(&default_mod2viblfo_mod, 50);381382383384/* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/385fluid_mod_set_source1(&default_att_mod, VOLUME_MSB, /* index=7 */386FLUID_MOD_CC /* CC=1 */387| FLUID_MOD_CONCAVE /* type=1 */388| FLUID_MOD_UNIPOLAR /* P=0 */389| FLUID_MOD_NEGATIVE /* D=1 */390);391fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */392fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */393fluid_mod_set_amount(&default_att_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */394395396397/* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */398fluid_mod_set_source1(&default_pan_mod, PAN_MSB, /* index=10 */399FLUID_MOD_CC /* CC=1 */400| FLUID_MOD_LINEAR /* type=0 */401| FLUID_MOD_BIPOLAR /* P=1 */402| FLUID_MOD_POSITIVE /* D=0 */403);404fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */405fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */406/* Amount: 500. The SF specs $8.4.6, p. 55 says: "Amount = 1000407tenths of a percent". The center value (64) corresponds to 50%,408so it follows that amount = 50% x 1000/% = 500. */409fluid_mod_set_amount(&default_pan_mod, 500.0);410411412/* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/413fluid_mod_set_source1(&default_expr_mod, EXPRESSION_MSB, /* index=11 */414FLUID_MOD_CC /* CC=1 */415| FLUID_MOD_CONCAVE /* type=1 */416| FLUID_MOD_UNIPOLAR /* P=0 */417| FLUID_MOD_NEGATIVE /* D=1 */418);419fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */420fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */421fluid_mod_set_amount(&default_expr_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */422423424425/* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */426fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* index=91 */427FLUID_MOD_CC /* CC=1 */428| FLUID_MOD_LINEAR /* type=0 */429| FLUID_MOD_UNIPOLAR /* P=0 */430| FLUID_MOD_POSITIVE /* D=0 */431);432fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */433fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */434fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */435436437438/* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */439fluid_mod_set_source1(&default_chorus_mod, EFFECTS_DEPTH3, /* index=93 */440FLUID_MOD_CC /* CC=1 */441| FLUID_MOD_LINEAR /* type=0 */442| FLUID_MOD_UNIPOLAR /* P=0 */443| FLUID_MOD_POSITIVE /* D=0 */444);445fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */446fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */447fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */448449450451/* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */452/* Initial Pitch is not a "standard" generator, because it isn't mentioned in the453list of generators in the SF2 specifications. That's why destination Initial Pitch454is replaced here by fine tune generator.455*/456fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */457FLUID_MOD_GC /* CC =0 */458| FLUID_MOD_LINEAR /* type=0 */459| FLUID_MOD_BIPOLAR /* P=1 */460| FLUID_MOD_POSITIVE /* D=0 */461);462fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */463FLUID_MOD_GC /* CC=0 */464| FLUID_MOD_LINEAR /* type=0 */465| FLUID_MOD_UNIPOLAR /* P=0 */466| FLUID_MOD_POSITIVE /* D=0 */467);468/* Also see the comment in gen.h about GEN_PITCH */469fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */470fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */471472473/* Non-standard MIDI continuous controller 8 to channel stereo balance */474fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */475FLUID_MOD_CC /* CC=1 */476| FLUID_MOD_CONCAVE /* type=1 */477| FLUID_MOD_BIPOLAR /* P=1 */478| FLUID_MOD_POSITIVE /* D=0 */479);480fluid_mod_set_source2(&custom_balance_mod, 0, 0);481fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */482/* Amount: 96 dB of attenuation (on the opposite channel) */483fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */484#endif /* unused in Wine */485486#if defined(LIBINSTPATCH_SUPPORT)487/* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */488if(!fluid_instpatch_supports_multi_init())489{490fluid_instpatch_init();491}492#endif493}494495static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth)496{497return fluid_atomic_int_get(&synth->ticks_since_start);498}499500static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t *synth, int val)501{502fluid_atomic_int_add(&synth->ticks_since_start, val);503}504505506/***************************************************************507* FLUID SAMPLE TIMERS508* Timers that use written audio data as timing reference509*/510struct _fluid_sample_timer_t511{512fluid_sample_timer_t *next; /* Single linked list of timers */513unsigned long starttick;514fluid_timer_callback_t callback;515void *data;516int isfinished;517};518519/*520* fluid_sample_timer_process - called when synth->ticks is updated521*/522static void fluid_sample_timer_process(fluid_synth_t *synth)523{524fluid_sample_timer_t *st;525long msec;526int cont;527unsigned int ticks = fluid_synth_get_ticks(synth);528529for(st = synth->sample_timers; st; st = st->next)530{531if(st->isfinished)532{533continue;534}535536msec = (long)(1000.0 * ((double)(ticks - st->starttick)) / synth->sample_rate);537cont = (*st->callback)(st->data, msec);538539if(cont == 0)540{541st->isfinished = 1;542}543}544}545546fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data)547{548fluid_sample_timer_t *result = FLUID_NEW(fluid_sample_timer_t);549550if(result == NULL)551{552FLUID_LOG(FLUID_ERR, "Out of memory");553return NULL;554}555556fluid_sample_timer_reset(synth, result);557result->data = data;558result->callback = callback;559result->next = synth->sample_timers;560synth->sample_timers = result;561return result;562}563564void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer)565{566fluid_sample_timer_t **ptr;567fluid_return_if_fail(synth != NULL);568fluid_return_if_fail(timer != NULL);569570ptr = &synth->sample_timers;571572while(*ptr)573{574if(*ptr == timer)575{576*ptr = timer->next;577FLUID_FREE(timer);578return;579}580581ptr = &((*ptr)->next);582}583}584585void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer)586{587timer->starttick = fluid_synth_get_ticks(synth);588timer->isfinished = 0;589}590591/***************************************************************592*593* FLUID SYNTH594*/595596static FLUID_INLINE void597fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t method, int intparam,598fluid_real_t realparam)599{600fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL);601fluid_return_if_fail(synth->eventhandler->mixer != NULL);602fluid_rvoice_eventhandler_push_int_real(synth->eventhandler, method,603synth->eventhandler->mixer,604intparam, realparam);605}606607static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth)608{609int i;610fluid_settings_getint(synth->settings, "synth.min-note-length", &i);611return (unsigned int)(i * synth->sample_rate / 1000.0f);612}613614/**615* Create new FluidSynth instance.616* @param settings Configuration parameters to use (used directly).617* @return New FluidSynth instance or NULL on error618*619* @note The @p settings parameter is used directly, but the synth does not take ownership of it.620* Hence, the caller is responsible for freeing it, when no longer needed.621* Further note that you may modify FluidSettings of the622* @p settings instance. However, only those FluidSettings marked as 'realtime' will623* affect the synth immediately. See the \ref fluidsettings for more details.624*625* @warning The @p settings object should only be used by a single synth at a time. I.e. creating626* multiple synth instances with a single @p settings object causes undefined behavior. Once the627* "single synth" has been deleted, you may use the @p settings object again for another synth.628*/629fluid_synth_t *630new_fluid_synth(fluid_settings_t *settings)631{632fluid_synth_t *synth;633fluid_sfloader_t *loader;634char *important_channels;635int i, prio_level = 0;636int with_ladspa = 0;637double sample_rate_min, sample_rate_max;638639/* initialize all the conversion tables and other stuff */640if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1))641{642fluid_synth_init();643}644645/* allocate a new synthesizer object */646synth = FLUID_NEW(fluid_synth_t);647648if(synth == NULL)649{650FLUID_LOG(FLUID_ERR, "Out of memory");651return NULL;652}653654FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));655656#if defined(LIBINSTPATCH_SUPPORT)657if(fluid_instpatch_supports_multi_init())658{659fluid_instpatch_init();660}661#endif662663fluid_rec_mutex_init(synth->mutex);664fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex);665synth->public_api_count = 0;666667synth->settings = settings;668669fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb);670fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus);671fluid_settings_getint(settings, "synth.verbose", &synth->verbose);672673fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony);674fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate);675fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max);676fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels);677fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);678fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);679fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels);680fluid_settings_getint(settings, "synth.effects-groups", &synth->effects_groups);681fluid_settings_getnum_float(settings, "synth.gain", &synth->gain);682fluid_settings_getint(settings, "synth.device-id", &synth->device_id);683fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);684685fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion);686fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released);687fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained);688fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume);689fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age);690fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important);691692/* register the callbacks */693fluid_settings_callback_num(settings, "synth.gain",694fluid_synth_handle_gain, synth);695fluid_settings_callback_int(settings, "synth.polyphony",696fluid_synth_handle_polyphony, synth);697fluid_settings_callback_int(settings, "synth.device-id",698fluid_synth_handle_device_id, synth);699fluid_settings_callback_num(settings, "synth.overflow.percussion",700fluid_synth_handle_overflow, synth);701fluid_settings_callback_num(settings, "synth.overflow.sustained",702fluid_synth_handle_overflow, synth);703fluid_settings_callback_num(settings, "synth.overflow.released",704fluid_synth_handle_overflow, synth);705fluid_settings_callback_num(settings, "synth.overflow.age",706fluid_synth_handle_overflow, synth);707fluid_settings_callback_num(settings, "synth.overflow.volume",708fluid_synth_handle_overflow, synth);709fluid_settings_callback_num(settings, "synth.overflow.important",710fluid_synth_handle_overflow, synth);711fluid_settings_callback_str(settings, "synth.overflow.important-channels",712fluid_synth_handle_important_channels, synth);713fluid_settings_callback_num(settings, "synth.reverb.room-size",714fluid_synth_handle_reverb_chorus_num, synth);715fluid_settings_callback_num(settings, "synth.reverb.damp",716fluid_synth_handle_reverb_chorus_num, synth);717fluid_settings_callback_num(settings, "synth.reverb.width",718fluid_synth_handle_reverb_chorus_num, synth);719fluid_settings_callback_num(settings, "synth.reverb.level",720fluid_synth_handle_reverb_chorus_num, synth);721fluid_settings_callback_int(settings, "synth.reverb.active",722fluid_synth_handle_reverb_chorus_int, synth);723fluid_settings_callback_int(settings, "synth.chorus.active",724fluid_synth_handle_reverb_chorus_int, synth);725fluid_settings_callback_int(settings, "synth.chorus.nr",726fluid_synth_handle_reverb_chorus_int, synth);727fluid_settings_callback_num(settings, "synth.chorus.level",728fluid_synth_handle_reverb_chorus_num, synth);729fluid_settings_callback_num(settings, "synth.chorus.depth",730fluid_synth_handle_reverb_chorus_num, synth);731fluid_settings_callback_num(settings, "synth.chorus.speed",732fluid_synth_handle_reverb_chorus_num, synth);733734/* do some basic sanity checking on the settings */735736if(synth->midi_channels % 16 != 0)737{738int n = synth->midi_channels / 16;739synth->midi_channels = (n + 1) * 16;740fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels);741FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. "742"I'll increase the number of channels to the next multiple.");743}744745if(synth->audio_channels < 1)746{747FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. "748"Changing this setting to 1.");749synth->audio_channels = 1;750}751else if(synth->audio_channels > 128)752{753FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). "754"Limiting this setting to 128.", synth->audio_channels);755synth->audio_channels = 128;756}757758if(synth->audio_groups < 1)759{760FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. "761"Changing this setting to 1.");762synth->audio_groups = 1;763}764else if(synth->audio_groups > 128)765{766FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). "767"Limiting this setting to 128.", synth->audio_groups);768synth->audio_groups = 128;769}770771if(synth->effects_channels < 2)772{773FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)."774"Setting effects channels to 2.", synth->effects_channels);775synth->effects_channels = 2;776}777778/*779number of buffers rendered by the mixer is determined by synth->audio_groups.780audio from MIDI channel is rendered, mapped and mixed in these buffers.781782Typically synth->audio_channels is only used by audio driver and should be set783to the same value that synth->audio_groups. In some situation using LADSPA,784it is best to diminish audio-channels so that the driver will be able to pass785the audio to audio devices in the case these devices have a limited number of786audio channels.787788audio-channels must not be greater then audio-groups, otherwise these789audio output above audio-groups will not be rendered by the mixeur.790*/791if(synth->audio_channels > synth->audio_groups)792{793synth->audio_channels = synth->audio_groups;794fluid_settings_setint(settings, "synth.audio-channels", synth->audio_channels);795FLUID_LOG(FLUID_WARN, "Requested audio-channels to high. "796"Limiting this setting to audio-groups.");797}798799if(fluid_settings_dupstr(settings, "synth.overflow.important-channels",800&important_channels) == FLUID_OK)801{802if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK)803{804FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels");805}806807FLUID_FREE(important_channels);808}809810/* as soon as the synth is created it starts playing. */811synth->state = FLUID_SYNTH_PLAYING;812813synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */814815fluid_atomic_int_set(&synth->ticks_since_start, 0);816synth->tuning = NULL;817fluid_private_init(synth->tuning_iter);818819/* Initialize multi-core variables if multiple cores enabled */820if(synth->cores > 1)821{822fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level);823}824825/* Allocate event queue for rvoice mixer */826/* In an overflow situation, a new voice takes about 50 spaces in the queue! */827synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64,828synth->polyphony, synth->audio_groups,829synth->effects_channels, synth->effects_groups,830(fluid_real_t)sample_rate_max, synth->sample_rate,831synth->cores - 1, prio_level);832833if(synth->eventhandler == NULL)834{835goto error_recovery;836}837838/* Setup the list of default modulators.839* Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */840synth->default_mod = NULL;841fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD);842fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD);843fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD);844fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD);845fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD);846fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD);847fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD);848fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD);849fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD);850fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD);851fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD);852853/* Create and initialize the Fx unit.*/854fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa);855856if(with_ladspa)857{858#ifdef LADSPA859synth->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate,860FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE);861862if(synth->ladspa_fx == NULL)863{864FLUID_LOG(FLUID_ERR, "Out of memory");865goto error_recovery;866}867868fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx,869synth->audio_groups);870#else /* LADSPA */871FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support");872#endif /* LADSPA */873}874875/* allocate and add the dls sfont loader */876#ifdef LIBINSTPATCH_SUPPORT877loader = new_fluid_instpatch_loader(settings);878879if(loader == NULL)880{881FLUID_LOG(FLUID_WARN, "Failed to create the instpatch SoundFont loader");882}883else884{885fluid_synth_add_sfloader(synth, loader);886}887#endif888889/* allocate and add the default sfont loader */890loader = new_fluid_defsfloader(settings);891892if(loader == NULL)893{894FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader");895}896else897{898fluid_synth_add_sfloader(synth, loader);899}900901/* allocate all channel objects */902synth->channel = FLUID_ARRAY(fluid_channel_t *, synth->midi_channels);903904if(synth->channel == NULL)905{906FLUID_LOG(FLUID_ERR, "Out of memory");907goto error_recovery;908}909910FLUID_MEMSET(synth->channel, 0, synth->midi_channels * sizeof(*synth->channel));911for(i = 0; i < synth->midi_channels; i++)912{913synth->channel[i] = new_fluid_channel(synth, i);914915if(synth->channel[i] == NULL)916{917goto error_recovery;918}919}920921/* allocate all synthesis processes */922synth->nvoice = synth->polyphony;923synth->voice = FLUID_ARRAY(fluid_voice_t *, synth->nvoice);924925if(synth->voice == NULL)926{927goto error_recovery;928}929930FLUID_MEMSET(synth->voice, 0, synth->nvoice * sizeof(*synth->voice));931for(i = 0; i < synth->nvoice; i++)932{933synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate);934935if(synth->voice[i] == NULL)936{937goto error_recovery;938}939}940941/* sets a default basic channel */942/* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */943/* (i.e all channels are polyphonic) */944/* Must be called after channel objects allocation */945fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,946synth->midi_channels);947948synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth);949950951fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,952synth->polyphony, 0.0f);953fluid_synth_reverb_on(synth, -1, synth->with_reverb);954fluid_synth_chorus_on(synth, -1, synth->with_chorus);955956synth->cur = FLUID_BUFSIZE;957synth->curmax = 0;958synth->dither_index = 0;959960{961double values[FLUID_REVERB_PARAM_LAST];962963fluid_settings_getnum(settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE]);964fluid_settings_getnum(settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP]);965fluid_settings_getnum(settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH]);966fluid_settings_getnum(settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL]);967968fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values);969}970971{972double values[FLUID_CHORUS_PARAM_LAST];973974fluid_settings_getint(settings, "synth.chorus.nr", &i);975values[FLUID_CHORUS_NR] = (double)i;976fluid_settings_getnum(settings, "synth.chorus.level", &values[FLUID_CHORUS_LEVEL]);977fluid_settings_getnum(settings, "synth.chorus.speed", &values[FLUID_CHORUS_SPEED]);978fluid_settings_getnum(settings, "synth.chorus.depth", &values[FLUID_CHORUS_DEPTH]);979values[FLUID_CHORUS_TYPE] = (double)FLUID_CHORUS_DEFAULT_TYPE;980981fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values);982}983984985synth->bank_select = FLUID_BANK_STYLE_GS;986987if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm"))988{989synth->bank_select = FLUID_BANK_STYLE_GM;990}991else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs"))992{993synth->bank_select = FLUID_BANK_STYLE_GS;994}995else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg"))996{997synth->bank_select = FLUID_BANK_STYLE_XG;998}999else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma"))1000{1001synth->bank_select = FLUID_BANK_STYLE_MMA;1002}10031004fluid_synth_process_event_queue(synth);10051006/* FIXME */1007synth->start = fluid_curtime();10081009return synth;10101011error_recovery:1012delete_fluid_synth(synth);1013return NULL;1014}101510161017/**1018* Delete a FluidSynth instance.1019* @param synth FluidSynth instance to delete1020*1021* @note Other users of a synthesizer instance, such as audio and MIDI drivers,1022* should be deleted prior to freeing the FluidSynth instance.1023*/1024void1025delete_fluid_synth(fluid_synth_t *synth)1026{1027int i, k;1028fluid_list_t *list;1029fluid_sfont_t *sfont;1030fluid_sfloader_t *loader;10311032fluid_return_if_fail(synth != NULL);10331034fluid_profiling_print();10351036/* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after1037* this synth has been deleted*/10381039fluid_settings_callback_num(synth->settings, "synth.gain",1040NULL, NULL);1041fluid_settings_callback_int(synth->settings, "synth.polyphony",1042NULL, NULL);1043fluid_settings_callback_int(synth->settings, "synth.device-id",1044NULL, NULL);1045fluid_settings_callback_num(synth->settings, "synth.overflow.percussion",1046NULL, NULL);1047fluid_settings_callback_num(synth->settings, "synth.overflow.sustained",1048NULL, NULL);1049fluid_settings_callback_num(synth->settings, "synth.overflow.released",1050NULL, NULL);1051fluid_settings_callback_num(synth->settings, "synth.overflow.age",1052NULL, NULL);1053fluid_settings_callback_num(synth->settings, "synth.overflow.volume",1054NULL, NULL);1055fluid_settings_callback_num(synth->settings, "synth.overflow.important",1056NULL, NULL);1057fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels",1058NULL, NULL);1059fluid_settings_callback_num(synth->settings, "synth.reverb.room-size",1060NULL, NULL);1061fluid_settings_callback_num(synth->settings, "synth.reverb.damp",1062NULL, NULL);1063fluid_settings_callback_num(synth->settings, "synth.reverb.width",1064NULL, NULL);1065fluid_settings_callback_num(synth->settings, "synth.reverb.level",1066NULL, NULL);1067fluid_settings_callback_int(synth->settings, "synth.reverb.active",1068NULL, NULL);1069fluid_settings_callback_int(synth->settings, "synth.chorus.active",1070NULL, NULL);1071fluid_settings_callback_int(synth->settings, "synth.chorus.nr",1072NULL, NULL);1073fluid_settings_callback_num(synth->settings, "synth.chorus.level",1074NULL, NULL);1075fluid_settings_callback_num(synth->settings, "synth.chorus.depth",1076NULL, NULL);1077fluid_settings_callback_num(synth->settings, "synth.chorus.speed",1078NULL, NULL);10791080/* turn off all voices, needed to unload SoundFont data */1081if(synth->voice != NULL)1082{1083for(i = 0; i < synth->nvoice; i++)1084{1085fluid_voice_t *voice = synth->voice[i];10861087if(!voice)1088{1089continue;1090}10911092/* WARNING: A this point we must ensure that the reference counter1093of any soundfont sample owned by any rvoice belonging to the voice1094are correctly decremented. This is the contrary part to1095to fluid_voice_init() where the sample's reference counter is1096incremented.1097*/1098fluid_voice_unlock_rvoice(voice);1099fluid_voice_overflow_rvoice_finished(voice);11001101if(fluid_voice_is_playing(voice))1102{1103fluid_voice_off(voice);1104/* If we only use fluid_voice_off(voice) it will trigger a delayed1105* fluid_voice_stop(voice) via fluid_synth_check_finished_voices().1106* But here, we are deleting the fluid_synth_t instance so1107* fluid_voice_stop() will be never triggered resulting in1108* SoundFont data never unloaded (i.e a serious memory leak).1109* So, fluid_voice_stop() must be explicitly called to insure1110* unloading SoundFont data1111*/1112fluid_voice_stop(voice);1113}1114}1115}11161117/* also unset all presets for clean SoundFont unload */1118if(synth->channel != NULL)1119{1120for(i = 0; i < synth->midi_channels; i++)1121{1122if(synth->channel[i] != NULL)1123{1124fluid_channel_set_preset(synth->channel[i], NULL);1125}1126}1127}11281129delete_fluid_rvoice_eventhandler(synth->eventhandler);11301131/* delete all the SoundFonts */1132for(list = synth->sfont; list; list = fluid_list_next(list))1133{1134sfont = fluid_list_get(list);1135fluid_sfont_delete_internal(sfont);1136}11371138delete_fluid_list(synth->sfont);11391140/* delete all the SoundFont loaders */11411142for(list = synth->loaders; list; list = fluid_list_next(list))1143{1144loader = (fluid_sfloader_t *) fluid_list_get(list);1145fluid_sfloader_delete(loader);1146}11471148delete_fluid_list(synth->loaders);11491150/* wait for and delete all the lazy sfont unloading timers */11511152for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list))1153{1154fluid_timer_t* timer = fluid_list_get(list);1155// explicitly join to wait for the unload really to happen1156fluid_timer_join(timer);1157// delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet1158delete_fluid_timer(timer);1159}11601161delete_fluid_list(synth->fonts_to_be_unloaded);11621163if(synth->channel != NULL)1164{1165for(i = 0; i < synth->midi_channels; i++)1166{1167delete_fluid_channel(synth->channel[i]);1168}11691170FLUID_FREE(synth->channel);1171}11721173if(synth->voice != NULL)1174{1175for(i = 0; i < synth->nvoice; i++)1176{1177delete_fluid_voice(synth->voice[i]);1178}11791180FLUID_FREE(synth->voice);1181}118211831184/* free the tunings, if any */1185if(synth->tuning != NULL)1186{1187for(i = 0; i < 128; i++)1188{1189if(synth->tuning[i] != NULL)1190{1191for(k = 0; k < 128; k++)1192{1193delete_fluid_tuning(synth->tuning[i][k]);1194}11951196FLUID_FREE(synth->tuning[i]);1197}1198}11991200FLUID_FREE(synth->tuning);1201}12021203fluid_private_free(synth->tuning_iter);12041205#ifdef LADSPA1206/* Release the LADSPA effects unit */1207delete_fluid_ladspa_fx(synth->ladspa_fx);1208#endif12091210/* delete all default modulators */1211delete_fluid_list_mod(synth->default_mod);12121213FLUID_FREE(synth->overflow.important_channels);12141215fluid_rec_mutex_destroy(synth->mutex);12161217FLUID_FREE(synth);12181219#if defined(LIBINSTPATCH_SUPPORT)1220if(fluid_instpatch_supports_multi_init())1221{1222fluid_instpatch_deinit();1223}1224#endif1225}12261227/**1228* Get a textual representation of the last error1229* @param synth FluidSynth instance1230* @return Pointer to string of last error message. Valid until the same1231* calling thread calls another FluidSynth function which fails. String is1232* internal and should not be modified or freed.1233* @deprecated This function is not thread-safe and does not work with multiple synths.1234* It has been deprecated. It may return "" in a future release and will eventually be removed.1235*/1236const char *1237fluid_synth_error(fluid_synth_t *synth)1238{1239return "";1240}12411242/**1243* Send a note-on event to a FluidSynth object.1244*1245* This function will take care of proper legato playing. If a note on channel @p chan is1246* already playing at the given key @p key, it will be released (even if it is sustained).1247* In other words, overlapping notes are not allowed.1248* @param synth FluidSynth instance1249* @param chan MIDI channel number (0 to MIDI channel count - 1)1250* @param key MIDI note number (0-127)1251* @param vel MIDI velocity (0-127, 0=noteoff)1252* @return #FLUID_OK on success, #FLUID_FAILED otherwise1253*/1254int1255fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel)1256{1257int result;1258fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);1259fluid_return_val_if_fail(vel >= 0 && vel <= 127, FLUID_FAILED);1260FLUID_API_ENTRY_CHAN(FLUID_FAILED);12611262/* Allowed only on MIDI channel enabled */1263FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);12641265result = fluid_synth_noteon_LOCAL(synth, chan, key, vel);1266FLUID_API_RETURN(result);1267}12681269/* Local synthesis thread variant of fluid_synth_noteon */1270static int1271fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, int vel)1272{1273fluid_channel_t *channel ;12741275/* notes with velocity zero go to noteoff */1276if(vel == 0)1277{1278return fluid_synth_noteoff_LOCAL(synth, chan, key);1279}12801281channel = synth->channel[chan];12821283/* makes sure this channel has a preset */1284if(channel->preset == NULL)1285{1286if(synth->verbose)1287{1288FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s",1289chan, key, vel, 0,1290fluid_synth_get_ticks(synth) / 44100.0f,1291(fluid_curtime() - synth->start) / 1000.0f,12920.0f, 0, "channel has no preset");1293}12941295return FLUID_FAILED;1296}12971298if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */1299{1300/* play the noteOn in monophonic */1301return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel);1302}1303else1304{1305/* channel is poly and legato CC is Off) */13061307/* plays the noteOn in polyphonic */1308/* Sets the note at first position in monophonic list */1309/* In the case where the musician intends to inter the channel in monophonic1310(by depressing the CC legato on), the next noteOn mono could be played legato1311with the previous note poly (if the musician choose this).1312*/1313fluid_channel_set_onenote_monolist(channel, (unsigned char) key,1314(unsigned char) vel);13151316/* If there is another voice process on the same channel and key,1317advance it to the release phase. */1318fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key);13191320/* a noteon poly is passed to fluid_synth_noteon_monopoly_legato().1321This allows an opportunity to get this note played legato with a previous1322note if a CC PTC have been received before this noteon. This behavior is1323a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.111324for details).1325*/1326return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel);1327}1328}13291330/**1331* Sends a note-off event to a FluidSynth object.1332* @param synth FluidSynth instance1333* @param chan MIDI channel number (0 to MIDI channel count - 1)1334* @param key MIDI note number (0-127)1335* @return #FLUID_OK on success, #FLUID_FAILED otherwise (may just mean that no1336* voices matched the note off event)1337*/1338int1339fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key)1340{1341int result;1342fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);1343FLUID_API_ENTRY_CHAN(FLUID_FAILED);13441345/* Allowed only on MIDI channel enabled */1346FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);13471348result = fluid_synth_noteoff_LOCAL(synth, chan, key);1349FLUID_API_RETURN(result);1350}13511352/* Local synthesis thread variant of fluid_synth_noteoff */1353static int1354fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key)1355{1356int status;1357fluid_channel_t *channel = synth->channel[chan];13581359if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */1360{1361/* play the noteOff in monophonic */1362status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key);1363}1364else1365{1366/* channel is poly and legato CC is Off) */1367/* removes the note from the monophonic list */1368if(channel->n_notes && key == fluid_channel_last_note(channel))1369{1370fluid_channel_clear_monolist(channel);1371}13721373status = fluid_synth_noteoff_monopoly(synth, chan, key, 0);1374}13751376/* Changes the state (Valid/Invalid) of the most recent note played in a1377staccato manner */1378fluid_channel_invalid_prev_note_staccato(channel);1379return status;1380}13811382/* Damps voices on a channel (turn notes off), if they're sustained by1383sustain pedal */1384static int1385fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t *synth, int chan)1386{1387fluid_channel_t *channel = synth->channel[chan];1388fluid_voice_t *voice;1389int i;13901391for(i = 0; i < synth->polyphony; i++)1392{1393voice = synth->voice[i];13941395if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice))1396{1397if(voice->key == channel->key_mono_sustained)1398{1399/* key_mono_sustained is a possible mono note sustainted1400(by sustain or sostenuto pedal). It must be marked released1401(INVALID_NOTE) here because it is released only by sustain pedal */1402channel->key_mono_sustained = INVALID_NOTE;1403}14041405fluid_voice_release(voice);1406}1407}14081409return FLUID_OK;1410}14111412/* Damps voices on a channel (turn notes off), if they're sustained by1413sostenuto pedal */1414static int1415fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan)1416{1417fluid_channel_t *channel = synth->channel[chan];1418fluid_voice_t *voice;1419int i;14201421for(i = 0; i < synth->polyphony; i++)1422{1423voice = synth->voice[i];14241425if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice))1426{1427if(voice->key == channel->key_mono_sustained)1428{1429/* key_mono_sustained is a possible mono note sustainted1430(by sustain or sostenuto pedal). It must be marked released1431(INVALID_NOTE) here because it is released only by sostenuto pedal */1432channel->key_mono_sustained = INVALID_NOTE;1433}14341435fluid_voice_release(voice);1436}1437}14381439return FLUID_OK;1440}14411442/**1443* Adds the specified modulator \c mod as default modulator to the synth. \c mod will1444* take effect for any subsequently created voice.1445* @param synth FluidSynth instance1446* @param mod Modulator info (values copied, passed in object can be freed immediately afterwards)1447* @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod)1448* @return #FLUID_OK on success, #FLUID_FAILED otherwise1449*1450* @note Not realtime safe (due to internal memory allocation) and therefore should not be called1451* from synthesis context at the risk of stalling audio output.1452*/1453int1454fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode)1455{1456fluid_mod_t *default_mod;1457fluid_mod_t *last_mod = NULL;1458fluid_mod_t *new_mod;14591460fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);1461fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);1462fluid_return_val_if_fail((mode == FLUID_SYNTH_ADD) || (mode == FLUID_SYNTH_OVERWRITE) , FLUID_FAILED);14631464/* Checks if modulators sources are valid */1465if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod"))1466{1467return FLUID_FAILED;1468}14691470fluid_synth_api_enter(synth);14711472default_mod = synth->default_mod;14731474while(default_mod != NULL)1475{1476if(fluid_mod_test_identity(default_mod, mod))1477{1478if(mode == FLUID_SYNTH_ADD)1479{1480default_mod->amount += mod->amount;1481}1482else // mode == FLUID_SYNTH_OVERWRITE1483{1484default_mod->amount = mod->amount;1485}14861487FLUID_API_RETURN(FLUID_OK);1488}14891490last_mod = default_mod;1491default_mod = default_mod->next;1492}14931494/* Add a new modulator (no existing modulator to add / overwrite). */1495new_mod = new_fluid_mod();14961497if(new_mod == NULL)1498{1499FLUID_API_RETURN(FLUID_FAILED);1500}15011502fluid_mod_clone(new_mod, mod);1503new_mod->next = NULL;15041505if(last_mod == NULL)1506{1507synth->default_mod = new_mod;1508}1509else1510{1511last_mod->next = new_mod;1512}15131514FLUID_API_RETURN(FLUID_OK);1515}15161517/**1518* Removes the specified modulator \c mod from the synth's default modulator list.1519* fluid_mod_test_identity() will be used to test modulator matching.1520* @param synth synth instance1521* @param mod The modulator to remove1522* @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise1523*1524* @note Not realtime safe (due to internal memory freeing) and therefore should not be called1525* from synthesis context at the risk of stalling audio output.1526*/1527int1528fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod)1529{1530fluid_mod_t *default_mod;1531fluid_mod_t *last_mod;15321533fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);1534fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);1535fluid_synth_api_enter(synth);15361537last_mod = default_mod = synth->default_mod;15381539while(default_mod != NULL)1540{1541if(fluid_mod_test_identity(default_mod, mod))1542{1543if(synth->default_mod == default_mod)1544{1545synth->default_mod = default_mod->next;1546}1547else1548{1549last_mod->next = default_mod->next;1550}15511552delete_fluid_mod(default_mod);1553FLUID_API_RETURN(FLUID_OK);1554}15551556last_mod = default_mod;1557default_mod = default_mod->next;1558}15591560FLUID_API_RETURN(FLUID_FAILED);1561}156215631564/**1565* Send a MIDI controller event on a MIDI channel.1566*1567* Most CCs are 7-bits wide in FluidSynth. There are a few exceptions which may be 14-bits wide as are documented here:1568* https://github.com/FluidSynth/fluidsynth/wiki/FluidFeatures#midi-control-change-implementation-chart1569*1570* @param synth FluidSynth instance1571* @param chan MIDI channel number (0 to MIDI channel count - 1)1572* @param num MIDI controller number (0-127)1573* @param val MIDI controller value (0-127)1574* @return #FLUID_OK on success, #FLUID_FAILED otherwise1575* @note This function supports MIDI Global Controllers which will be sent to1576* all channels of the basic channel if this basic channel is in mode OmniOff/Mono.1577* This is accomplished by sending the CC one MIDI channel below the basic1578* channel of the receiver.1579* Examples: let a synthesizer with 16 MIDI channels:1580* - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it1581* could be used as CC global for all channels belonging to basic channel 7.1582* - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used1583* as CC global for all channels belonging to basic channel 0.1584* @warning Contrary to the MIDI Standard, this function does not clear LSB controllers,1585* when MSB controllers are received.1586*/1587int1588fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val)1589{1590int result = FLUID_FAILED;1591fluid_channel_t *channel;1592fluid_return_val_if_fail(num >= 0 && num <= 127, FLUID_FAILED);1593fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);1594FLUID_API_ENTRY_CHAN(FLUID_FAILED);15951596channel = synth->channel[chan];15971598if(channel->mode & FLUID_CHANNEL_ENABLED)1599{1600/* chan is enabled */1601if(synth->verbose)1602{1603FLUID_LOG(FLUID_INFO, "cc\t\t%d\t%d\t%d", chan, num, val);1604}16051606fluid_channel_set_cc(channel, num, val);1607result = fluid_synth_cc_LOCAL(synth, chan, num);1608}1609else /* chan is disabled so it is a candidate for global channel */1610{1611/* looks for next basic channel */1612int n_chan = synth->midi_channels; /* MIDI Channels number */1613int basicchan ;16141615if(chan < n_chan - 1)1616{1617basicchan = chan + 1; /* next channel */1618}1619else1620{1621basicchan = 0; /* wrap to 0 */1622}16231624channel = synth->channel[basicchan];16251626/* Channel must be a basicchan in mode OMNIOFF_MONO */1627if((channel->mode & FLUID_CHANNEL_BASIC) &&1628((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO))1629{1630/* sends cc to all channels in this basic channel */1631int i, nbr = channel->mode_val;16321633for(i = basicchan; i < basicchan + nbr; i++)1634{1635if(synth->verbose)1636{1637FLUID_LOG(FLUID_INFO, "cc\t\t%d\t%d\t%d", i, num, val);1638}16391640fluid_channel_set_cc(synth->channel[i], num, val);1641result = fluid_synth_cc_LOCAL(synth, i, num);1642}1643}1644/* The channel chan is not a valid 'global channel' */1645else1646{1647result = FLUID_FAILED;1648}1649}16501651FLUID_API_RETURN(result);1652}16531654/* Local synthesis thread variant of MIDI CC set function.1655Most of CC are allowed to modulate but not all. A comment describes if CC num1656isn't allowed to modulate.1657Following explanations should help to understand both MIDI specifications and1658Soundfont specifications in regard to MIDI specs.16591660MIDI specs:1661CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31).1662It's up to the synthesizer to decide to take LSB values into account or not.1663Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param()1664(once fluid_voice_modulate() has been triggered). This is because actually1665fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs.1666So fluidsynth is using only 7 bit MSB (except for portamento time).1667In regard to MIDI specs Fluidsynth behaves correctly.16681669Soundfont specs 2.01 - 8.2.1:1670To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit)1671or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of1672CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators1673with CC LSB connected to sources inputs should be ignored).1674These specifics are particularly suited for synths that use 14 bits CCs. In this case,1675the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives1676both CC LSB and CC MSB but only CC MSB will trigger the modulation.1677This will produce correct synthesis parameters update from a correct 14 bits CC.1678If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and1679CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters1680update followed by correct synthesis parameters update.16811682However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore1683these SF recommendations on CC receive.1684*/1685static int1686fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)1687{1688fluid_channel_t *chan = synth->channel[channum];1689int nrpn_select;1690int value;16911692value = fluid_channel_get_cc(chan, num);16931694switch(num)1695{1696case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1697break;16981699/* CC omnioff, omnion, mono, poly */1700/* not allowed to modulate (spec SF 2.01 - 8.2.1) */1701case POLY_OFF:1702case POLY_ON:1703case OMNI_OFF:1704case OMNI_ON:17051706/* allowed only if channum is a basic channel */1707if(chan->mode & FLUID_CHANNEL_BASIC)1708{1709/* Construction of new_mode from current channel mode and this CC mode */1710int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK;17111712switch(num)1713{1714case POLY_OFF:1715new_mode |= FLUID_CHANNEL_POLY_OFF;1716break;17171718case POLY_ON:1719new_mode &= ~FLUID_CHANNEL_POLY_OFF;1720break;17211722case OMNI_OFF:1723new_mode |= FLUID_CHANNEL_OMNI_OFF;1724break;17251726case OMNI_ON:1727new_mode &= ~FLUID_CHANNEL_OMNI_OFF;1728break;17291730default: /* should never happen */1731return FLUID_FAILED;1732}17331734/* MIDI specs: if value is 0 it means all channels from channum to next1735basic channel minus 1 (if any) or to MIDI channel count minus 1.1736However, if value is > 0 (e.g. 4), the group of channels will be be1737limited to 4.1738value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode1739implies a group of only one channel.1740*/1741/* Checks value range and changes this existing basic channel group */1742value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value);17431744if(value != FLUID_FAILED)1745{1746/* reset the current basic channel before changing it */1747fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val);1748fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value);1749break; /* FLUID_OK */1750}1751}17521753return FLUID_FAILED;17541755case LEGATO_SWITCH: /* not allowed to modulate */1756/* handles Poly/mono commutation on Legato pedal On/Off.*/1757fluid_channel_cc_legato(chan, value);1758break;17591760case PORTAMENTO_SWITCH: /* not allowed to modulate */1761/* Special handling of the monophonic list */1762/* Invalids the most recent note played in a staccato manner */1763fluid_channel_invalid_prev_note_staccato(chan);1764break;17651766case SUSTAIN_SWITCH: /* not allowed to modulate */17671768/* Release voices if Sustain switch is released */1769if(value < 64) /* Sustain is released */1770{1771fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum);1772}17731774break;17751776case SOSTENUTO_SWITCH: /* not allowed to modulate */17771778/* Release voices if Sostetuno switch is released */1779if(value < 64) /* Sostenuto is released */1780{1781fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);1782}1783else /* Sostenuto is depressed */1784/* Update sostenuto order id when pedaling on Sostenuto */1785{1786chan->sostenuto_orderid = synth->noteid; /* future voice id value */1787}17881789break;17901791case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1792fluid_channel_set_bank_msb(chan, value & 0x7F);1793break;17941795case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1796fluid_channel_set_bank_lsb(chan, value & 0x7F);1797break;17981799case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1800fluid_synth_all_notes_off_LOCAL(synth, channum);1801break;18021803case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1804fluid_synth_all_sounds_off_LOCAL(synth, channum);1805break;18061807case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1808fluid_channel_init_ctrl(chan, 1);1809// the hold pedals have been reset, we maybe need to release voices1810fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum);1811fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);1812fluid_synth_modulate_voices_all_LOCAL(synth, channum);1813break;18141815case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1816case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1817{1818/* handle both because msb might come first */1819int lsb_value = fluid_channel_get_cc(chan, DATA_ENTRY_LSB);1820int msb_value = fluid_channel_get_cc(chan, DATA_ENTRY_MSB);1821int data = (msb_value << 7) + lsb_value;18221823if(chan->nrpn_active) /* NRPN is active? */1824{1825/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */1826if((fluid_channel_get_cc(chan, NRPN_MSB) == 120)1827&& (fluid_channel_get_cc(chan, NRPN_LSB) < 100))1828{1829nrpn_select = chan->nrpn_select;18301831if(nrpn_select < GEN_LAST)1832{1833float val = fluid_gen_scale_nrpn(nrpn_select, data);1834if(synth->verbose)1835{1836FLUID_LOG(FLUID_INFO, "NRPN\t%d\t%d\t%d\t%f", channum, nrpn_select, data, val);1837}1838fluid_synth_set_gen_LOCAL(synth, channum, nrpn_select, val);1839}18401841chan->nrpn_select = 0; /* Reset to 0 */1842}1843else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs1844{1845// ALTITUDE.MID also manipulates AWE32 NRPNs by only using DATA LSB events - seems to be legal1846if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs1847{1848int gen = fluid_channel_get_cc(chan, NRPN_LSB);1849if(synth->verbose)1850{1851FLUID_LOG(FLUID_INFO, "AWE32 NRPN RAW: Chan %d, Gen %d, data %d | 0x%X, MSB: %d, LSB: %d", channum, gen, data, data, msb_value, lsb_value);1852}1853if(gen <= 26) // Effect 26 (reverb) is the last effect to select1854{1855fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data, lsb_value);1856}1857else1858{1859FLUID_LOG(FLUID_INFO, "Ignoring unknown AWE32 NRPN targetting effect %d", gen);1860}1861}1862}1863}1864else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */1865{1866switch(fluid_channel_get_cc(chan, RPN_LSB))1867{1868case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones plus cents */1869fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], msb_value + lsb_value / 100.0f); /* 0-127 maps to 0-100 cents */1870fluid_synth_update_pitch_wheel_sens_LOCAL(synth, channum); /* Update bend range */1871break;18721873case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */1874fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE,1875(float)(data - 8192) * (100.0f / 8192.0f));1876break;18771878case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */1879fluid_synth_set_gen_LOCAL(synth, channum, GEN_COARSETUNE,1880msb_value - 64);1881break;18821883case RPN_TUNING_PROGRAM_CHANGE:1884fluid_channel_set_tuning_prog(chan, msb_value);1885fluid_synth_activate_tuning(synth, channum,1886fluid_channel_get_tuning_bank(chan),1887msb_value, TRUE);1888break;18891890case RPN_TUNING_BANK_SELECT:1891fluid_channel_set_tuning_bank(chan, msb_value);1892break;18931894case RPN_MODULATION_DEPTH_RANGE:1895break;1896}1897}18981899break;1900}19011902case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1903fluid_channel_set_cc(chan, NRPN_LSB, 0);1904chan->nrpn_select = 0;1905chan->nrpn_active = 1;1906break;19071908case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */19091910/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */1911if(fluid_channel_get_cc(chan, NRPN_MSB) == 120)1912{1913if(value == 100)1914{1915chan->nrpn_select += 100;1916}1917else if(value == 101)1918{1919chan->nrpn_select += 1000;1920}1921else if(value == 102)1922{1923chan->nrpn_select += 10000;1924}1925else if(value < 100)1926{1927chan->nrpn_select += value;1928}1929}19301931chan->nrpn_active = 1;1932break;19331934case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1935case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1936chan->nrpn_active = 0;1937break;19381939case BREATH_MSB:1940/* handles CC Breath On/Off noteOn/noteOff mode */1941fluid_channel_cc_breath_note_on_off(chan, value);19421943/* fall-through */1944default:1945/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */1946/* However, as long fluidsynth will use only CC 7 bits resolution, it1947is safe to ignore these SF recommendations on CC receive. See1948explanations above */1949/* if (! (32 <= num && num <= 63)) */1950{1951return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num);1952}1953}19541955return FLUID_OK;1956}19571958/**1959* Get current MIDI controller value on a MIDI channel.1960* @param synth FluidSynth instance1961* @param chan MIDI channel number (0 to MIDI channel count - 1)1962* @param num MIDI controller number (0-127)1963* @param pval Location to store MIDI controller value (0-127)1964* @return #FLUID_OK on success, #FLUID_FAILED otherwise1965*/1966int1967fluid_synth_get_cc(fluid_synth_t *synth, int chan, int num, int *pval)1968{1969fluid_return_val_if_fail(num >= 0 && num < 128, FLUID_FAILED);1970fluid_return_val_if_fail(pval != NULL, FLUID_FAILED);19711972FLUID_API_ENTRY_CHAN(FLUID_FAILED);19731974/* Allowed only on MIDI channel enabled */1975FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);19761977*pval = fluid_channel_get_cc(synth->channel[chan], num);1978FLUID_API_RETURN(FLUID_OK);1979}19801981/*1982* Handler for synth.device-id setting.1983*/1984static void1985fluid_synth_handle_device_id(void *data, const char *name, int value)1986{1987fluid_synth_t *synth = (fluid_synth_t *)data;1988fluid_return_if_fail(synth != NULL);19891990fluid_synth_api_enter(synth);1991synth->device_id = value;1992fluid_synth_api_exit(synth);1993}19941995/**1996* Process a MIDI SYSEX (system exclusive) message.1997* @param synth FluidSynth instance1998* @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7)1999* @param len Length of data in buffer2000* @param response Buffer to store response to or NULL to ignore2001* @param response_len IN/OUT parameter, in: size of response buffer, out:2002* amount of data written to response buffer (if #FLUID_FAILED is returned and2003* this value is non-zero, it indicates the response buffer is too small)2004* @param handled Optional location to store boolean value if message was2005* recognized and handled or not (set to TRUE if it was handled)2006* @param dryrun TRUE to just do a dry run but not actually execute the SYSEX2007* command (useful for checking if a SYSEX message would be handled)2008* @return #FLUID_OK on success, #FLUID_FAILED otherwise2009* @since 1.1.02010* @note When Fluidsynth receives an XG System Mode ON message, it compares the @p synth 's deviceID2011* directly with the deviceID of the SysEx message. This is contrary to the XG spec (page 42), which2012* requires to only compare the lower nibble. However, following the XG spec seems to break drum channels2013* for a lot of MIDI files out there and therefore we've decided for this customization. If you rely on2014* XG System Mode ON messages, make sure to set the setting \ref settings_synth_device-id to match the2015* deviceID provided in the SysEx message (in most cases, this will be <code>deviceID=16</code>).2016*2017* @code2018* SYSEX format (0xF0 and 0xF7 bytes shall not be passed to this function):2019* Non-realtime: 0xF0 0x7E <DeviceId> [BODY] 0xF72020* Realtime: 0xF0 0x7F <DeviceId> [BODY] 0xF72021* Tuning messages: 0xF0 0x7E/0x7F <DeviceId> 0x08 <sub ID2> [BODY] <ChkSum> 0xF72022* GS DT1 messages: 0xF0 0x41 <DeviceId> 0x42 0x12 [ADDRESS (3 bytes)] [DATA] <ChkSum> 0xF72023* @endcode2024*/2025int2026fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,2027char *response, int *response_len, int *handled, int dryrun)2028{2029int avail_response = 0;20302031if(handled)2032{2033*handled = FALSE;2034}20352036if(response_len)2037{2038avail_response = *response_len;2039*response_len = 0;2040}20412042fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2043fluid_return_val_if_fail(data != NULL, FLUID_FAILED);2044fluid_return_val_if_fail(len > 0, FLUID_FAILED);2045fluid_return_val_if_fail(!response || response_len, FLUID_FAILED);20462047if(len < 4)2048{2049return FLUID_OK;2050}20512052/* MIDI tuning SYSEX message? */2053if((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME)2054&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2055&& data[2] == MIDI_SYSEX_MIDI_TUNING_ID)2056{2057int result;2058fluid_synth_api_enter(synth);2059result = fluid_synth_sysex_midi_tuning(synth, data, len, response,2060response_len, avail_response,2061handled, dryrun);20622063FLUID_API_RETURN(result);2064}20652066/* GM or GM2 system on */2067if(data[0] == MIDI_SYSEX_UNIV_NON_REALTIME2068&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2069&& data[2] == MIDI_SYSEX_GM_ID)2070{2071if(handled)2072{2073*handled = TRUE;2074}2075if(!dryrun && (data[3] == MIDI_SYSEX_GM_ON2076|| data[3] == MIDI_SYSEX_GM2_ON))2077{2078int result;2079fluid_synth_api_enter(synth);2080synth->bank_select = FLUID_BANK_STYLE_GM;2081result = fluid_synth_system_reset_LOCAL(synth);2082FLUID_API_RETURN(result);2083}2084return FLUID_OK;2085}20862087/* GS DT1 message */2088if(data[0] == MIDI_SYSEX_MANUF_ROLAND2089&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2090&& data[2] == MIDI_SYSEX_GS_ID2091&& data[3] == MIDI_SYSEX_GS_DT1)2092{2093int result;2094fluid_synth_api_enter(synth);2095result = fluid_synth_sysex_gs_dt1(synth, data, len, response,2096response_len, avail_response,2097handled, dryrun);2098FLUID_API_RETURN(result);2099}21002101/* XG message */2102if(data[0] == MIDI_SYSEX_MANUF_YAMAHA2103&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2104&& data[2] == MIDI_SYSEX_XG_ID)2105{2106int result;2107fluid_synth_api_enter(synth);2108result = fluid_synth_sysex_xg(synth, data, len, response,2109response_len, avail_response,2110handled, dryrun);2111FLUID_API_RETURN(result);2112}21132114return FLUID_OK;2115}21162117/* Handler for MIDI tuning SYSEX messages */2118static int2119fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,2120char *response, int *response_len, int avail_response,2121int *handled, int dryrun)2122{2123int realtime, msgid;2124int bank = 0, prog, channels;2125double tunedata[128];2126int keys[128];2127char name[17]={0};2128int note, frac, frac2;2129uint8_t chksum;2130int i, count, index;2131const char *dataptr;2132char *resptr;21332134realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME;2135msgid = data[3];21362137switch(msgid)2138{2139case MIDI_SYSEX_TUNING_BULK_DUMP_REQ:2140case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK:2141if(data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)2142{2143if(len != 5 || data[4] & 0x80 || !response)2144{2145return FLUID_OK;2146}21472148*response_len = 406;2149prog = data[4];2150}2151else2152{2153if(len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response)2154{2155return FLUID_OK;2156}21572158*response_len = 407;2159bank = data[4];2160prog = data[5];2161}21622163if(dryrun)2164{2165if(handled)2166{2167*handled = TRUE;2168}21692170return FLUID_OK;2171}21722173if(avail_response < *response_len)2174{2175return FLUID_FAILED;2176}21772178/* Get tuning data, return if tuning not found */2179if(fluid_synth_tuning_dump(synth, bank, prog, name, 17, tunedata) == FLUID_FAILED)2180{2181*response_len = 0;2182return FLUID_OK;2183}21842185resptr = response;21862187*resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME;2188*resptr++ = synth->device_id;2189*resptr++ = MIDI_SYSEX_MIDI_TUNING_ID;2190*resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP;21912192if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK)2193{2194*resptr++ = bank;2195}21962197*resptr++ = prog;2198/* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */2199FLUID_MEMCPY(resptr, name, 16);2200resptr += 16;22012202for(i = 0; i < 128; i++)2203{2204note = tunedata[i] / 100.0;2205fluid_clip(note, 0, 127);22062207frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0;2208fluid_clip(frac, 0, 16383);22092210*resptr++ = note;2211*resptr++ = frac >> 7;2212*resptr++ = frac & 0x7F;2213}22142215if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)2216{2217/* NOTE: Checksum is not as straight forward as the bank based messages */2218chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID2219^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog;22202221for(i = 21; i < 128 * 3 + 21; i++)2222{2223chksum ^= response[i];2224}2225}2226else2227{2228for(i = 1, chksum = 0; i < 406; i++)2229{2230chksum ^= response[i];2231}2232}22332234*resptr++ = chksum & 0x7F;22352236if(handled)2237{2238*handled = TRUE;2239}22402241break;22422243case MIDI_SYSEX_TUNING_NOTE_TUNE:2244case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK:2245dataptr = data + 4;22462247if(msgid == MIDI_SYSEX_TUNING_NOTE_TUNE)2248{2249if(len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6)2250{2251return FLUID_OK;2252}2253}2254else2255{2256if(len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x802257|| len != data[6] * 4 + 7)2258{2259return FLUID_OK;2260}22612262bank = *dataptr++;2263}22642265if(dryrun)2266{2267if(handled)2268{2269*handled = TRUE;2270}22712272return FLUID_OK;2273}22742275prog = *dataptr++;2276count = *dataptr++;22772278for(i = 0, index = 0; i < count; i++)2279{2280note = *dataptr++;22812282if(note & 0x80)2283{2284return FLUID_OK;2285}22862287keys[index] = note;22882289note = *dataptr++;2290frac = *dataptr++;2291frac2 = *dataptr++;22922293if(note & 0x80 || frac & 0x80 || frac2 & 0x80)2294{2295return FLUID_OK;2296}22972298frac = frac << 7 | frac2;22992300/* No change pitch value? Doesn't really make sense to send that, but.. */2301if(note == 0x7F && frac == 16383)2302{2303continue;2304}23052306tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0);2307index++;2308}23092310if(index > 0)2311{2312if(fluid_synth_tune_notes(synth, bank, prog, index, keys, tunedata,2313realtime) == FLUID_FAILED)2314{2315return FLUID_FAILED;2316}2317}23182319if(handled)2320{2321*handled = TRUE;2322}23232324break;23252326case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE:2327case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE:2328if((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19)2329|| (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31))2330{2331return FLUID_OK;2332}23332334if(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80)2335{2336return FLUID_OK;2337}23382339if(dryrun)2340{2341if(handled)2342{2343*handled = TRUE;2344}23452346return FLUID_OK;2347}23482349channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];23502351if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)2352{2353for(i = 0; i < 12; i++)2354{2355frac = data[i + 7];23562357if(frac & 0x80)2358{2359return FLUID_OK;2360}23612362tunedata[i] = (int)frac - 64;2363}2364}2365else2366{2367for(i = 0; i < 12; i++)2368{2369frac = data[i * 2 + 7];2370frac2 = data[i * 2 + 8];23712372if(frac & 0x80 || frac2 & 0x80)2373{2374return FLUID_OK;2375}23762377tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0);2378}2379}23802381if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX",2382tunedata, realtime) == FLUID_FAILED)2383{2384return FLUID_FAILED;2385}23862387if(channels)2388{2389for(i = 0; i < 16; i++)2390{2391if(channels & (1 << i))2392{2393fluid_synth_activate_tuning(synth, i, 0, 0, realtime);2394}2395}2396}23972398if(handled)2399{2400*handled = TRUE;2401}24022403break;2404}24052406return FLUID_OK;2407}24082409/* Handler for GS DT1 messages */2410static int2411fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len,2412char *response, int *response_len, int avail_response,2413int *handled, int dryrun)2414{2415int addr;2416int len_data;2417int checksum = 0, i;24182419if(len < 9) // at least one byte of data should be transmitted2420{2421FLUID_LOG(FLUID_INFO, "SysEx DT1: message too short, dropping it.");2422return FLUID_FAILED;2423}2424len_data = len - 8;2425addr = (data[4] << 16) | (data[5] << 8) | data[6];24262427for (i = 4; i < len - 1; ++i)2428{2429checksum += data[i];2430}2431checksum = 0x80 - (checksum & 0x7F);2432if (checksum != data[len - 1])2433{2434FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping message on addr 0x%x due to incorrect checksum 0x%x. Correct checksum: 0x%x", addr, (int)data[len - 1], checksum);2435return FLUID_FAILED;2436}24372438if (addr == 0x40007F) // Mode set2439{2440if (len_data > 1 || (data[7] != 0 && data[7] != 0x7f))2441{2442FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid mode set message");2443return FLUID_FAILED;2444}2445if (handled)2446{2447*handled = TRUE;2448}2449if (!dryrun)2450{2451if (data[7] == 0)2452{2453synth->bank_select = FLUID_BANK_STYLE_GS;2454}2455else2456{2457synth->bank_select = FLUID_BANK_STYLE_GM;2458}2459return fluid_synth_system_reset_LOCAL(synth);2460}2461return FLUID_OK;2462}24632464if (synth->bank_select != FLUID_BANK_STYLE_GS)2465{2466return FLUID_OK; // Silently ignore all other messages2467}24682469if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part2470{2471if (len_data > 1 || data[7] > 0x02)2472{2473FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid rhythm part message");2474return FLUID_FAILED;2475}2476if (handled)2477{2478*handled = TRUE;2479}2480if (!dryrun)2481{2482int chan = (addr >> 8) & 0x0F;2483//See the Patch Part parameters section in SC-88Pro/8850 owner's manual2484chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1);2485synth->channel[chan]->channel_type =2486data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM;24872488FLUID_LOG(FLUID_DBG, "SysEx DT1: setting MIDI channel %d to type %d", chan, (int)synth->channel[chan]->channel_type);2489//Roland synths seem to "remember" the last instrument a channel2490//used in the selected mode. This behavior is not replicated here.2491fluid_synth_program_change(synth, chan, 0);2492}2493return FLUID_OK;2494}24952496//silently ignore2497return FLUID_OK;2498}24992500/* Handler for XG messages */2501static int2502fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, int len,2503char *response, int *response_len, int avail_response,2504int *handled, int dryrun)2505{2506int addr;2507int len_data;25082509if(len < 7) // at least one byte of data should be transmitted2510{2511return FLUID_FAILED;2512}2513len_data = len - 6;2514addr = (data[3] << 16) | (data[4] << 8) | data[5];25152516if (addr == 0x00007E // Reset2517|| addr == 0x00007F) // Reset to factory2518{2519if (len_data > 1 || data[6] != 0)2520{2521return FLUID_FAILED;2522}2523if (handled)2524{2525*handled = TRUE;2526}2527if (!dryrun)2528{2529synth->bank_select = FLUID_BANK_STYLE_XG;2530return fluid_synth_system_reset_LOCAL(synth);2531}2532return FLUID_OK;2533}25342535/* No other messages handled yet2536if (synth->bank_select != FLUID_BANK_STYLE_XG)2537{2538return FLUID_OK; // Silently ignore all other messages2539}*/25402541//silently ignore2542return FLUID_OK;2543}25442545/**2546* Turn off all voices that are playing on the given MIDI channel, by putting them into release phase.2547* @param synth FluidSynth instance2548* @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)2549* @return #FLUID_OK on success, #FLUID_FAILED otherwise2550* @since 1.1.42551*/2552int2553fluid_synth_all_notes_off(fluid_synth_t *synth, int chan)2554{2555int result;25562557fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2558fluid_return_val_if_fail(chan >= -1, FLUID_FAILED);2559fluid_synth_api_enter(synth);25602561if(chan >= synth->midi_channels)2562{2563result = FLUID_FAILED;2564}2565else2566{2567/* Allowed (even for channel disabled) as chan = -1 selects all channels */2568result = fluid_synth_all_notes_off_LOCAL(synth, chan);2569}25702571FLUID_API_RETURN(result);2572}25732574/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */2575//static int2576int2577fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan)2578{2579fluid_voice_t *voice;2580int i;25812582for(i = 0; i < synth->polyphony; i++)2583{2584voice = synth->voice[i];25852586if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))2587{2588fluid_voice_noteoff(voice);2589}2590}25912592return FLUID_OK;2593}25942595/**2596* Immediately stop all voices on the given MIDI channel (skips release phase).2597* @param synth FluidSynth instance2598* @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)2599* @return #FLUID_OK on success, #FLUID_FAILED otherwise2600* @since 1.1.42601*/2602int2603fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan)2604{2605int result;26062607fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2608fluid_return_val_if_fail(chan >= -1, FLUID_FAILED);2609fluid_synth_api_enter(synth);26102611if(chan >= synth->midi_channels)2612{2613result = FLUID_FAILED;2614}2615else2616{2617/* Allowed (even for channel disabled) as chan = -1 selects all channels */2618result = fluid_synth_all_sounds_off_LOCAL(synth, chan);2619}26202621FLUID_API_RETURN(result);2622}26232624/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */2625static int2626fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan)2627{2628fluid_voice_t *voice;2629int i;26302631for(i = 0; i < synth->polyphony; i++)2632{2633voice = synth->voice[i];26342635if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))2636{2637fluid_voice_off(voice);2638}2639}26402641return FLUID_OK;2642}26432644/**2645* Reset reverb engine2646* @param synth FluidSynth instance2647* @return #FLUID_OK on success, #FLUID_FAILED otherwise2648*/2649int2650fluid_synth_reset_reverb(fluid_synth_t *synth)2651{2652fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2653fluid_synth_api_enter(synth);2654fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);2655FLUID_API_RETURN(FLUID_OK);2656}26572658/**2659* Reset chorus engine2660* @param synth FluidSynth instance2661* @return #FLUID_OK on success, #FLUID_FAILED otherwise2662*/2663int2664fluid_synth_reset_chorus(fluid_synth_t *synth)2665{2666fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2667fluid_synth_api_enter(synth);2668fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);2669FLUID_API_RETURN(FLUID_OK);2670}267126722673/**2674* Send MIDI system reset command (big red 'panic' button), turns off notes, resets2675* controllers and restores initial basic channel configuration.2676* @param synth FluidSynth instance2677* @return #FLUID_OK on success, #FLUID_FAILED otherwise2678*/2679int2680fluid_synth_system_reset(fluid_synth_t *synth)2681{2682int result;2683fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2684fluid_synth_api_enter(synth);2685result = fluid_synth_system_reset_LOCAL(synth);2686FLUID_API_RETURN(result);2687}26882689/* Local variant of the system reset command */2690static int2691fluid_synth_system_reset_LOCAL(fluid_synth_t *synth)2692{2693int i;26942695if(synth->verbose)2696{2697FLUID_LOG(FLUID_INFO, "=== systemreset ===");2698}26992700fluid_synth_all_sounds_off_LOCAL(synth, -1);27012702for(i = 0; i < synth->midi_channels; i++)2703{2704fluid_channel_reset(synth->channel[i]);2705}27062707/* Basic channel 0, Mode Omni On Poly */2708fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,2709synth->midi_channels);27102711fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);2712fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);27132714return FLUID_OK;2715}27162717/**2718* Update voices on a MIDI channel after a MIDI control change.2719* @param synth FluidSynth instance2720* @param chan MIDI channel number (0 to MIDI channel count - 1)2721* @param is_cc Boolean value indicating if ctrl is a CC controller or not2722* @param ctrl MIDI controller value2723* @return #FLUID_OK on success, #FLUID_FAILED otherwise2724*/2725static int2726fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl)2727{2728fluid_voice_t *voice;2729int i;27302731for(i = 0; i < synth->polyphony; i++)2732{2733voice = synth->voice[i];27342735if(fluid_voice_get_channel(voice) == chan)2736{2737fluid_voice_modulate(voice, is_cc, ctrl);2738}2739}27402741return FLUID_OK;2742}27432744/**2745* Update voices on a MIDI channel after all MIDI controllers have been changed.2746* @param synth FluidSynth instance2747* @param chan MIDI channel number (0 to MIDI channel count - 1)2748* @return #FLUID_OK on success, #FLUID_FAILED otherwise2749*/2750static int2751fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan)2752{2753fluid_voice_t *voice;2754int i;27552756for(i = 0; i < synth->polyphony; i++)2757{2758voice = synth->voice[i];27592760if(fluid_voice_get_channel(voice) == chan)2761{2762fluid_voice_modulate_all(voice);2763}2764}27652766return FLUID_OK;2767}27682769/**2770* Set the MIDI channel pressure controller value.2771* @param synth FluidSynth instance2772* @param chan MIDI channel number (0 to MIDI channel count - 1)2773* @param val MIDI channel pressure value (0-127)2774* @return #FLUID_OK on success, #FLUID_FAILED otherwise2775*/2776int2777fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val)2778{2779int result;2780fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);27812782FLUID_API_ENTRY_CHAN(FLUID_FAILED);27832784/* Allowed only on MIDI channel enabled */2785FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);27862787if(synth->verbose)2788{2789FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);2790}27912792fluid_channel_set_channel_pressure(synth->channel[chan], val);2793result = fluid_synth_update_channel_pressure_LOCAL(synth, chan);27942795FLUID_API_RETURN(result);2796}27972798/* Updates channel pressure from within synthesis thread */2799static int2800fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int chan)2801{2802return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_CHANNELPRESSURE);2803}28042805/**2806* Set the MIDI polyphonic key pressure controller value.2807* @param synth FluidSynth instance2808* @param chan MIDI channel number (0 to MIDI channel count - 1)2809* @param key MIDI key number (0-127)2810* @param val MIDI key pressure value (0-127)2811* @return #FLUID_OK on success, #FLUID_FAILED otherwise2812* @since 2.0.02813*/2814int2815fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val)2816{2817int result;2818fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);2819fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);28202821FLUID_API_ENTRY_CHAN(FLUID_FAILED);28222823/* Allowed only on MIDI channel enabled */2824FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);28252826if(synth->verbose)2827{2828FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val);2829}28302831fluid_channel_set_key_pressure(synth->channel[chan], key, val);2832result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key);28332834FLUID_API_RETURN(result);2835}28362837/* Updates key pressure from within synthesis thread */2838static int2839fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key)2840{2841fluid_voice_t *voice;2842int i;2843int result = FLUID_OK;28442845for(i = 0; i < synth->polyphony; i++)2846{2847voice = synth->voice[i];28482849if(voice->chan == chan && voice->key == key)2850{2851result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE);28522853if(result != FLUID_OK)2854{2855return result;2856}2857}2858}28592860return result;2861}28622863/**2864* Set the MIDI pitch bend controller value on a MIDI channel.2865* @param synth FluidSynth instance2866* @param chan MIDI channel number (0 to MIDI channel count - 1)2867* @param val MIDI pitch bend value (0-16383 with 8192 being center)2868* @return #FLUID_OK on success, #FLUID_FAILED otherwise2869*/2870int2871fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val)2872{2873int result;2874fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED);2875FLUID_API_ENTRY_CHAN(FLUID_FAILED);28762877/* Allowed only on MIDI channel enabled */2878FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);28792880if(synth->verbose)2881{2882FLUID_LOG(FLUID_INFO, "pitchb\t\t%d\t%d", chan, val);2883}28842885fluid_channel_set_pitch_bend(synth->channel[chan], val);2886result = fluid_synth_update_pitch_bend_LOCAL(synth, chan);28872888FLUID_API_RETURN(result);2889}28902891/* Local synthesis thread variant of pitch bend */2892static int2893fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan)2894{2895return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL);2896}28972898/**2899* Get the MIDI pitch bend controller value on a MIDI channel.2900* @param synth FluidSynth instance2901* @param chan MIDI channel number (0 to MIDI channel count - 1)2902* @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with2903* 8192 being center)2904* @return #FLUID_OK on success, #FLUID_FAILED otherwise2905*/2906int2907fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend)2908{2909int result;2910fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED);2911FLUID_API_ENTRY_CHAN(FLUID_FAILED);29122913/* Allowed only on MIDI channel enabled */2914FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29152916*ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]);2917result = FLUID_OK;29182919FLUID_API_RETURN(result);2920}29212922/**2923* Set MIDI pitch wheel sensitivity on a MIDI channel.2924* @param synth FluidSynth instance2925* @param chan MIDI channel number (0 to MIDI channel count - 1)2926* @param val Pitch wheel sensitivity value in semitones2927* @return #FLUID_OK on success, #FLUID_FAILED otherwise2928*/2929int2930fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val)2931{2932int result;2933fluid_return_val_if_fail(val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */2934FLUID_API_ENTRY_CHAN(FLUID_FAILED);29352936/* Allowed only on MIDI channel enabled */2937FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29382939if(synth->verbose)2940{2941FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val);2942}29432944fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val);2945result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan);29462947FLUID_API_RETURN(result);2948}29492950/* Local synthesis thread variant of set pitch wheel sensitivity */2951static int2952fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan)2953{2954return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS);2955}29562957/**2958* Get MIDI pitch wheel sensitivity on a MIDI channel.2959* @param synth FluidSynth instance2960* @param chan MIDI channel number (0 to MIDI channel count - 1)2961* @param pval Location to store pitch wheel sensitivity value in semitones2962* @return #FLUID_OK on success, #FLUID_FAILED otherwise2963* @since Sometime AFTER v1.0 API freeze.2964*/2965int2966fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval)2967{2968int result;2969fluid_return_val_if_fail(pval != NULL, FLUID_FAILED);2970FLUID_API_ENTRY_CHAN(FLUID_FAILED);29712972/* Allowed only on MIDI channel enabled */2973FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29742975*pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]);2976result = FLUID_OK;29772978FLUID_API_RETURN(result);2979}29802981/**2982* Assign a preset to a MIDI channel.2983* @param synth FluidSynth instance2984* @param chan MIDI channel number (0 to MIDI channel count - 1)2985* @param preset Preset to assign to channel or NULL to clear (ownership is taken over)2986* @return #FLUID_OK on success, #FLUID_FAILED otherwise2987*/2988static int2989fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset)2990{2991fluid_channel_t *channel;29922993fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2994fluid_return_val_if_fail(chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);29952996channel = synth->channel[chan];29972998return fluid_channel_set_preset(channel, preset);2999}30003001/* Get a preset by SoundFont, bank and program numbers.3002* Returns preset pointer or NULL.3003*/3004static fluid_preset_t *3005fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum,3006int banknum, int prognum)3007{3008fluid_sfont_t *sfont;3009fluid_list_t *list;30103011/* 128 indicates an "unset" operation" */3012if(prognum == FLUID_UNSET_PROGRAM)3013{3014return NULL;3015}30163017for(list = synth->sfont; list; list = fluid_list_next(list))3018{3019sfont = fluid_list_get(list);30203021if(fluid_sfont_get_id(sfont) == sfontnum)3022{3023return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);3024}3025}30263027return NULL;3028}30293030/* Get a preset by SoundFont name, bank and program.3031* Returns preset pointer or NULL.3032*/3033static fluid_preset_t *3034fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname,3035int banknum, int prognum)3036{3037fluid_sfont_t *sfont;3038fluid_list_t *list;30393040for(list = synth->sfont; list; list = fluid_list_next(list))3041{3042sfont = fluid_list_get(list);30433044if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0)3045{3046return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);3047}3048}30493050return NULL;3051}30523053/* Find a preset by bank and program numbers.3054* Returns preset pointer or NULL.3055*/3056fluid_preset_t *3057fluid_synth_find_preset(fluid_synth_t *synth, int banknum,3058int prognum)3059{3060fluid_preset_t *preset;3061fluid_sfont_t *sfont;3062fluid_list_t *list;30633064for(list = synth->sfont; list; list = fluid_list_next(list))3065{3066sfont = fluid_list_get(list);30673068preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);30693070if(preset)3071{3072return preset;3073}3074}30753076return NULL;3077}30783079/**3080* Send a program change event on a MIDI channel.3081* @param synth FluidSynth instance3082* @param chan MIDI channel number (0 to MIDI channel count - 1)3083* @param prognum MIDI program number (0-127)3084* @return #FLUID_OK on success, #FLUID_FAILED otherwise3085*/3086/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented3087* since fluid_synth_unset_program() should be used instead. */3088int3089fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum)3090{3091fluid_preset_t *preset = NULL;3092fluid_channel_t *channel;3093int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED;30943095fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED);3096FLUID_API_ENTRY_CHAN(FLUID_FAILED);30973098/* Allowed only on MIDI channel enabled */3099FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);31003101channel = synth->channel[chan];31023103if(channel->channel_type == CHANNEL_TYPE_DRUM)3104{3105banknum = DRUM_INST_BANK;3106}3107else3108{3109fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL);3110}31113112if(synth->verbose)3113{3114FLUID_LOG(FLUID_INFO, "prog\t\t%d\t%d\t%d", chan, banknum, prognum);3115}31163117/* I think this is a hack for MIDI files that do bank changes in GM mode.3118* Proper way to handle this would probably be to ignore bank changes when in3119* GM mode. - JG3120* This is now possible by setting synth.midi-bank-select=gm, but let the hack3121* stay for the time being. - DH3122*/3123if(prognum != FLUID_UNSET_PROGRAM)3124{3125subst_bank = banknum;3126subst_prog = prognum;31273128preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);31293130/* Fallback to another preset if not found */3131if(!preset)3132{3133/* Percussion: Fallback to preset 0 in percussion bank */3134if(channel->channel_type == CHANNEL_TYPE_DRUM)3135{3136subst_prog = 0;3137subst_bank = DRUM_INST_BANK;3138preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);3139}3140/* Melodic instrument */3141else3142{3143/* Fallback first to bank 0:prognum */3144subst_bank = 0;3145preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);31463147/* Fallback to first preset in bank 0 (usually piano...) */3148if(!preset)3149{3150subst_prog = 0;3151preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);3152}3153}31543155if(preset)3156{3157FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]",3158chan, banknum, prognum, subst_bank, subst_prog);3159}3160else3161{3162FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum);3163}3164}3165}31663167/* Assign the SoundFont ID and program number to the channel */3168fluid_channel_set_sfont_bank_prog(channel, preset ? fluid_sfont_get_id(preset->sfont) : 0,3169-1, prognum);3170result = fluid_synth_set_preset(synth, chan, preset);31713172FLUID_API_RETURN(result);3173}31743175/**3176* Set instrument bank number on a MIDI channel.3177* @param synth FluidSynth instance3178* @param chan MIDI channel number (0 to MIDI channel count - 1)3179* @param bank MIDI bank number3180* @return #FLUID_OK on success, #FLUID_FAILED otherwise3181* @note This function does not change the instrument currently assigned to \c chan,3182* as it is usually called prior to fluid_synth_program_change(). If you still want3183* instrument changes to take effect immediately, call fluid_synth_program_reset()3184* after having set up the bank configuration.3185*3186*/3187int3188fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank)3189{3190int result;3191fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED);3192fluid_return_val_if_fail(bank >= 0, FLUID_FAILED);3193FLUID_API_ENTRY_CHAN(FLUID_FAILED);31943195/* Allowed only on MIDI channel enabled */3196FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);31973198fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1);3199result = FLUID_OK;32003201FLUID_API_RETURN(result);3202}32033204/**3205* Set SoundFont ID on a MIDI channel.3206* @param synth FluidSynth instance3207* @param chan MIDI channel number (0 to MIDI channel count - 1)3208* @param sfont_id ID of a loaded SoundFont3209* @return #FLUID_OK on success, #FLUID_FAILED otherwise3210* @note This function does not change the instrument currently assigned to \c chan,3211* as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change().3212* If you still want instrument changes to take effect immediately, call fluid_synth_program_reset()3213* after having selected the soundfont.3214*/3215int3216fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id)3217{3218int result;3219FLUID_API_ENTRY_CHAN(FLUID_FAILED);32203221/* Allowed only on MIDI channel enabled */3222FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);32233224fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);3225result = FLUID_OK;32263227FLUID_API_RETURN(result);3228}32293230/**3231* Set the preset of a MIDI channel to an unassigned state.3232* @param synth FluidSynth instance3233* @param chan MIDI channel number (0 to MIDI channel count - 1)3234* @return #FLUID_OK on success, #FLUID_FAILED otherwise3235* @since 1.1.13236*3237* @note Channel retains its SoundFont ID and bank numbers, while the program3238* number is set to an "unset" state. MIDI program changes may re-assign a3239* preset if one matches.3240*/3241int3242fluid_synth_unset_program(fluid_synth_t *synth, int chan)3243{3244FLUID_API_ENTRY_CHAN(FLUID_FAILED);3245FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM));3246}32473248/**3249* Get current SoundFont ID, bank number and program number for a MIDI channel.3250* @param synth FluidSynth instance3251* @param chan MIDI channel number (0 to MIDI channel count - 1)3252* @param sfont_id Location to store SoundFont ID3253* @param bank_num Location to store MIDI bank number3254* @param preset_num Location to store MIDI program number3255* @return #FLUID_OK on success, #FLUID_FAILED otherwise3256*/3257int3258fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id,3259int *bank_num, int *preset_num)3260{3261int result;3262fluid_channel_t *channel;32633264fluid_return_val_if_fail(sfont_id != NULL, FLUID_FAILED);3265fluid_return_val_if_fail(bank_num != NULL, FLUID_FAILED);3266fluid_return_val_if_fail(preset_num != NULL, FLUID_FAILED);3267FLUID_API_ENTRY_CHAN(FLUID_FAILED);32683269/* Allowed only on MIDI channel enabled */3270FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);32713272channel = synth->channel[chan];3273fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, preset_num);32743275/* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */3276if(*preset_num == FLUID_UNSET_PROGRAM)3277{3278*preset_num = 0;3279}32803281result = FLUID_OK;32823283FLUID_API_RETURN(result);3284}32853286/**3287* Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers.3288* @param synth FluidSynth instance3289* @param chan MIDI channel number (0 to MIDI channel count - 1)3290* @param sfont_id ID of a loaded SoundFont3291* @param bank_num MIDI bank number3292* @param preset_num MIDI program number3293* @return #FLUID_OK on success, #FLUID_FAILED otherwise3294*/3295int3296fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id,3297int bank_num, int preset_num)3298{3299fluid_preset_t *preset = NULL;3300fluid_channel_t *channel;3301int result;3302fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3303fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);33043305FLUID_API_ENTRY_CHAN(FLUID_FAILED);33063307/* Allowed only on MIDI channel enabled */3308FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);33093310channel = synth->channel[chan];33113312preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);33133314if(preset == NULL)3315{3316FLUID_LOG(FLUID_ERR,3317"There is no preset with bank number %d and preset number %d in SoundFont %d",3318bank_num, preset_num, sfont_id);3319FLUID_API_RETURN(FLUID_FAILED);3320}33213322/* Assign the new SoundFont ID, bank and program number to the channel */3323fluid_channel_set_sfont_bank_prog(channel, sfont_id, bank_num, preset_num);3324result = fluid_synth_set_preset(synth, chan, preset);33253326FLUID_API_RETURN(result);3327}33283329/**3330* Pins all samples of the given preset.3331*3332* @param synth FluidSynth instance3333* @param sfont_id ID of a loaded SoundFont3334* @param bank_num MIDI bank number3335* @param preset_num MIDI program number3336* @return #FLUID_OK if the preset was found, pinned and loaded3337* into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK3338* is returned, even if <code>synth.dynamic-sample-loading</code> is disabled or3339* the preset doesn't support dynamic-sample-loading.3340*3341* This function will attempt to pin all samples of the given preset and3342* load them into memory, if they are currently unloaded. "To pin" in this3343* context means preventing them from being unloaded by an upcoming channel3344* prog change.3345*3346* @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled.3347* By default, dynamic-sample-loading is disabled and all samples are kept in memory.3348* Furthermore, this is only useful for presets which support dynamic-sample-loading (currently,3349* only preset loaded with the default soundfont loader do).3350*3351* @since 2.2.03352*/3353int3354fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num)3355{3356int ret;3357fluid_preset_t *preset;33583359fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3360fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3361fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);33623363fluid_synth_api_enter(synth);33643365preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);33663367if(preset == NULL)3368{3369FLUID_LOG(FLUID_ERR,3370"There is no preset with bank number %d and preset number %d in SoundFont %d",3371bank_num, preset_num, sfont_id);3372FLUID_API_RETURN(FLUID_FAILED);3373}33743375ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages33763377FLUID_API_RETURN(ret);3378}33793380/**3381* Unpin all samples of the given preset.3382*3383* @param synth FluidSynth instance3384* @param sfont_id ID of a loaded SoundFont3385* @param bank_num MIDI bank number3386* @param preset_num MIDI program number3387* @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise3388*3389* This function undoes the effect of fluid_synth_pin_preset(). If the preset is3390* not currently used, its samples will be unloaded.3391*3392* @note Only useful for presets loaded with the default soundfont loader and3393* only if \ref settings_synth_dynamic-sample-loading is enabled.3394*3395* @since 2.2.03396*/3397int3398fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num)3399{3400int ret;3401fluid_preset_t *preset;34023403fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3404fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3405fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);34063407fluid_synth_api_enter(synth);34083409preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);34103411if(preset == NULL)3412{3413FLUID_LOG(FLUID_ERR,3414"There is no preset with bank number %d and preset number %d in SoundFont %d",3415bank_num, preset_num, sfont_id);3416FLUID_API_RETURN(FLUID_FAILED);3417}34183419ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages34203421FLUID_API_RETURN(ret);3422}34233424/**3425* Select an instrument on a MIDI channel by SoundFont name, bank and program numbers.3426* @param synth FluidSynth instance3427* @param chan MIDI channel number (0 to MIDI channel count - 1)3428* @param sfont_name Name of a loaded SoundFont3429* @param bank_num MIDI bank number3430* @param preset_num MIDI program number3431* @return #FLUID_OK on success, #FLUID_FAILED otherwise3432* @since 1.1.03433*/3434int3435fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan,3436const char *sfont_name, int bank_num,3437int preset_num)3438{3439fluid_preset_t *preset = NULL;3440fluid_channel_t *channel;3441int result;3442fluid_return_val_if_fail(sfont_name != NULL, FLUID_FAILED);3443FLUID_API_ENTRY_CHAN(FLUID_FAILED);34443445/* Allowed only on MIDI channel enabled */3446FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);34473448channel = synth->channel[chan];34493450preset = fluid_synth_get_preset_by_sfont_name(synth, sfont_name, bank_num,3451preset_num);34523453if(preset == NULL)3454{3455FLUID_LOG(FLUID_ERR,3456"There is no preset with bank number %d and preset number %d in SoundFont %s",3457bank_num, preset_num, sfont_name);3458FLUID_API_RETURN(FLUID_FAILED);3459}34603461/* Assign the new SoundFont ID, bank and program number to the channel */3462fluid_channel_set_sfont_bank_prog(channel, fluid_sfont_get_id(preset->sfont),3463bank_num, preset_num);3464result = fluid_synth_set_preset(synth, chan, preset);34653466FLUID_API_RETURN(result);3467}34683469/*3470* This function assures that every MIDI channel has a valid preset3471* (NULL is okay). This function is called after a SoundFont is3472* unloaded or reloaded.3473*/3474static void3475fluid_synth_update_presets(fluid_synth_t *synth)3476{3477fluid_channel_t *channel;3478fluid_preset_t *preset;3479int sfont, bank, prog;3480int chan;34813482for(chan = 0; chan < synth->midi_channels; chan++)3483{3484channel = synth->channel[chan];3485fluid_channel_get_sfont_bank_prog(channel, &sfont, &bank, &prog);3486preset = fluid_synth_get_preset(synth, sfont, bank, prog);3487fluid_synth_set_preset(synth, chan, preset);3488}3489}34903491static void3492fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate)3493{3494int i;3495fluid_clip(sample_rate, 8000.0f, 96000.0f);3496synth->sample_rate = sample_rate;34973498synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth);34993500for(i = 0; i < synth->polyphony; i++)3501{3502fluid_voice_set_output_rate(synth->voice[i], sample_rate);3503}3504}35053506/**3507* Set up an event to change the sample-rate of the synth during the next rendering call.3508* @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth.3509* @deprecated As of fluidsynth 2.1.0 this function has been deprecated.3510* Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized).3511* The sample-rate change may also require memory allocation deep down in the effect units.3512* However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering.3513* This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block.3514* Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change.3515* Long story short: don't use it.3516* @code{.cpp}3517fluid_synth_t* synth; // assume initialized3518// [...]3519// sample-rate change needed? Delete the audio driver, if any.3520delete_fluid_audio_driver(adriver);3521// then delete the synth3522delete_fluid_synth(synth);3523// update the sample-rate3524fluid_settings_setnum(settings, "synth.sample-rate", 22050.0);3525// and re-create objects3526synth = new_fluid_synth(settings);3527adriver = new_fluid_audio_driver(settings, synth);3528* @endcode3529* @param synth FluidSynth instance3530* @param sample_rate New sample-rate (Hz)3531* @since 1.1.23532*/3533void3534fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate)3535{3536fluid_return_if_fail(synth != NULL);3537fluid_synth_api_enter(synth);35383539fluid_synth_set_sample_rate_LOCAL(synth, sample_rate);35403541fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate,35420, synth->sample_rate);3543fluid_synth_api_exit(synth);3544}35453546// internal sample rate change function for the jack driver3547// executes immediately, therefore, make sure no rendering call is running!3548void3549fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate)3550{3551fluid_rvoice_param_t param[MAX_EVENT_PARAMS];3552fluid_return_if_fail(synth != NULL);3553fluid_synth_api_enter(synth);35543555fluid_synth_set_sample_rate_LOCAL(synth, sample_rate);35563557param[0].i = 0;3558param[1].real = synth->sample_rate;3559fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param);35603561fluid_synth_api_exit(synth);3562}356335643565/* Handler for synth.gain setting. */3566static void3567fluid_synth_handle_gain(void *data, const char *name, double value)3568{3569fluid_synth_t *synth = (fluid_synth_t *)data;3570fluid_synth_set_gain(synth, (float) value);3571}35723573/**3574* Set synth output gain value.3575* @param synth FluidSynth instance3576* @param gain Gain value (function clamps value to the range 0.0 to 10.0)3577*/3578void3579fluid_synth_set_gain(fluid_synth_t *synth, float gain)3580{3581fluid_return_if_fail(synth != NULL);3582fluid_synth_api_enter(synth);35833584fluid_clip(gain, 0.0f, 10.0f);35853586synth->gain = gain;3587fluid_synth_update_gain_LOCAL(synth);3588fluid_synth_api_exit(synth);3589}35903591/* Called by synthesis thread to update the gain in all voices */3592static void3593fluid_synth_update_gain_LOCAL(fluid_synth_t *synth)3594{3595fluid_voice_t *voice;3596float gain;3597int i;35983599gain = synth->gain;36003601for(i = 0; i < synth->polyphony; i++)3602{3603voice = synth->voice[i];36043605if(fluid_voice_is_playing(voice))3606{3607fluid_voice_set_gain(voice, gain);3608}3609}3610}36113612/**3613* Get synth output gain value.3614* @param synth FluidSynth instance3615* @return Synth gain value (0.0 to 10.0)3616*/3617float3618fluid_synth_get_gain(fluid_synth_t *synth)3619{3620float result;3621fluid_return_val_if_fail(synth != NULL, 0.0);3622fluid_synth_api_enter(synth);36233624result = synth->gain;3625FLUID_API_RETURN(result);3626}36273628/*3629* Handler for synth.polyphony setting.3630*/3631static void3632fluid_synth_handle_polyphony(void *data, const char *name, int value)3633{3634fluid_synth_t *synth = (fluid_synth_t *)data;3635fluid_synth_set_polyphony(synth, value);3636}36373638/**3639* Set synthesizer polyphony (max number of voices).3640* @param synth FluidSynth instance3641* @param polyphony Polyphony to assign3642* @return #FLUID_OK on success, #FLUID_FAILED otherwise3643* @since 1.0.63644*/3645int3646fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony)3647{3648int result;3649fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3650fluid_return_val_if_fail(polyphony >= 1 && polyphony <= 65535, FLUID_FAILED);3651fluid_synth_api_enter(synth);36523653result = fluid_synth_update_polyphony_LOCAL(synth, polyphony);36543655FLUID_API_RETURN(result);3656}36573658/* Called by synthesis thread to update the polyphony value */3659static int3660fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony)3661{3662fluid_voice_t *voice;3663int i;36643665if(new_polyphony > synth->nvoice)3666{3667/* Create more voices */3668fluid_voice_t **new_voices = FLUID_REALLOC(synth->voice,3669sizeof(fluid_voice_t *) * new_polyphony);36703671if(new_voices == NULL)3672{3673return FLUID_FAILED;3674}36753676synth->voice = new_voices;36773678for(i = synth->nvoice; i < new_polyphony; i++)3679{3680synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate);36813682if(synth->voice[i] == NULL)3683{3684return FLUID_FAILED;3685}36863687fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags);3688}36893690synth->nvoice = new_polyphony;3691}36923693synth->polyphony = new_polyphony;36943695/* turn off any voices above the new limit */3696for(i = synth->polyphony; i < synth->nvoice; i++)3697{3698voice = synth->voice[i];36993700if(fluid_voice_is_playing(voice))3701{3702fluid_voice_off(voice);3703}3704}37053706fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,3707synth->polyphony, 0.0f);37083709return FLUID_OK;3710}37113712/**3713* Get current synthesizer polyphony (max number of voices).3714* @param synth FluidSynth instance3715* @return Synth polyphony value.3716* @since 1.0.63717*/3718int3719fluid_synth_get_polyphony(fluid_synth_t *synth)3720{3721int result;3722fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3723fluid_synth_api_enter(synth);37243725result = synth->polyphony;3726FLUID_API_RETURN(result);3727}37283729/**3730* @brief Get current number of active voices.3731*3732* I.e. the no. of voices that have been3733* started and have not yet finished. Unless called from synthesis context,3734* this number does not necessarily have to be equal to the number of voices3735* currently processed by the DSP loop, see below.3736* @param synth FluidSynth instance3737* @return Number of currently active voices.3738* @since 1.1.03739*3740* @note To generate accurate continuous statistics of the voice count, caller3741* should ensure this function is called synchronously with the audio synthesis3742* process. This can be done in the new_fluid_audio_driver2() audio callback3743* function for example. Otherwise every call to this function may return different3744* voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by3745* e.g. an audio driver or the applications audio rendering thread.3746*/3747int3748fluid_synth_get_active_voice_count(fluid_synth_t *synth)3749{3750int result;3751fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3752fluid_synth_api_enter(synth);37533754result = synth->active_voice_count;3755FLUID_API_RETURN(result);3756}37573758/**3759* Get the internal synthesis buffer size value.3760* @param synth FluidSynth instance3761* @return Internal buffer size in audio frames.3762*3763* Audio is synthesized at this number of frames at a time. Defaults to 64 frames. I.e. the synth can only react to notes,3764* control changes, and other audio affecting events after having processed 64 audio frames.3765*/3766int3767fluid_synth_get_internal_bufsize(fluid_synth_t *synth)3768{3769return FLUID_BUFSIZE;3770}37713772/**3773* Resend a bank select and a program change for every channel and assign corresponding instruments.3774* @param synth FluidSynth instance3775* @return #FLUID_OK on success, #FLUID_FAILED otherwise3776*3777* This function is called mainly after a SoundFont has been loaded,3778* unloaded or reloaded.3779*/3780int3781fluid_synth_program_reset(fluid_synth_t *synth)3782{3783int i, prog;3784fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3785fluid_synth_api_enter(synth);37863787/* try to set the correct presets */3788for(i = 0; i < synth->midi_channels; i++)3789{3790fluid_channel_get_sfont_bank_prog(synth->channel[i], NULL, NULL, &prog);3791fluid_synth_program_change(synth, i, prog);3792}37933794FLUID_API_RETURN(FLUID_OK);3795}37963797/**3798* Synthesize a block of floating point audio to separate audio buffers (multi-channel rendering).3799*3800* @param synth FluidSynth instance3801* @param len Count of audio frames to synthesize3802* @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)3803* @param right Array of float buffers to store right channel of planar audio (size: dito)3804* @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)3805* @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito)3806* @return #FLUID_OK on success, #FLUID_FAILED otherwise3807*3808* First effect channel used by reverb, second for chorus.3809*3810* @note Should only be called from synthesis thread.3811*3812* @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release.3813* It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more3814* powerful and flexible fluid_synth_process().3815*3816* Usage example:3817* @code{.cpp}3818const int FramesToRender = 64;3819int channels;3820// retrieve number of stereo audio channels3821fluid_settings_getint(settings, "synth.audio-channels", &channels);38223823// we need twice as many (mono-)buffers3824channels *= 2;38253826// fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16:3827// each midi channel gets rendered to its own stereo buffer, rather than having3828// one buffer and interleaved PCM3829float** mix_buf = new float*[channels];3830for(int i = 0; i < channels; i++)3831{3832mix_buf[i] = new float[FramesToRender];3833}38343835// retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan)3836// and chrous (second chan))3837fluid_settings_getint(settings, "synth.effects-channels", &channels);3838channels *= 2;38393840float** fx_buf = new float*[channels];3841for(int i = 0; i < channels; i++)3842{3843fx_buf[i] = new float[FramesToRender];3844}38453846float** mix_buf_l = mix_buf;3847float** mix_buf_r = &mix_buf[channels/2];38483849float** fx_buf_l = fx_buf;3850float** fx_buf_r = &fx_buf[channels/2];38513852fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r)3853* @endcode3854*/3855int3856fluid_synth_nwrite_float(fluid_synth_t *synth, int len,3857float **left, float **right,3858float **fx_left, float **fx_right)3859{3860fluid_real_t *left_in, *fx_left_in;3861fluid_real_t *right_in, *fx_right_in;3862double time = fluid_utime();3863int i, num, available, count;3864#ifdef WITH_FLOAT3865int bytes;3866#endif3867float cpu_load;38683869fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3870fluid_return_val_if_fail(left != NULL, FLUID_FAILED);3871fluid_return_val_if_fail(right != NULL, FLUID_FAILED);3872fluid_return_val_if_fail(len >= 0, FLUID_FAILED);3873fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below38743875/* First, take what's still available in the buffer */3876count = 0;3877num = synth->cur;38783879if(synth->cur < FLUID_BUFSIZE)3880{3881available = FLUID_BUFSIZE - synth->cur;3882fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);3883fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);38843885num = (available > len) ? len : available;3886#ifdef WITH_FLOAT3887bytes = num * sizeof(float);3888#endif38893890for(i = 0; i < synth->audio_channels; i++)3891{3892#ifdef WITH_FLOAT3893FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3894FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3895#else //WITH_FLOAT3896int j;38973898for(j = 0; j < num; j++)3899{3900left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3901right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3902}39033904#endif //WITH_FLOAT3905}39063907for(i = 0; i < synth->effects_channels; i++)3908{3909#ifdef WITH_FLOAT39103911if(fx_left != NULL)3912{3913FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3914}39153916if(fx_right != NULL)3917{3918FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3919}39203921#else //WITH_FLOAT3922int j;39233924if(fx_left != NULL)3925{3926for(j = 0; j < num; j++)3927{3928fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3929}3930}39313932if(fx_right != NULL)3933{3934for(j = 0; j < num; j++)3935{3936fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3937}3938}39393940#endif //WITH_FLOAT3941}39423943count += num;3944num += synth->cur; /* if we're now done, num becomes the new synth->cur below */3945}39463947/* Then, run one_block() and copy till we have 'len' samples */3948while(count < len)3949{3950fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0);3951fluid_synth_render_blocks(synth, 1); // TODO:3952fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);3953fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);39543955num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE;3956#ifdef WITH_FLOAT3957bytes = num * sizeof(float);3958#endif39593960for(i = 0; i < synth->audio_channels; i++)3961{3962#ifdef WITH_FLOAT3963FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3964FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3965#else //WITH_FLOAT3966int j;39673968for(j = 0; j < num; j++)3969{3970left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];3971right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];3972}39733974#endif //WITH_FLOAT3975}39763977for(i = 0; i < synth->effects_channels; i++)3978{3979#ifdef WITH_FLOAT39803981if(fx_left != NULL)3982{3983FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3984}39853986if(fx_right != NULL)3987{3988FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3989}39903991#else //WITH_FLOAT3992int j;39933994if(fx_left != NULL)3995{3996for(j = 0; j < num; j++)3997{3998fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];3999}4000}40014002if(fx_right != NULL)4003{4004for(j = 0; j < num; j++)4005{4006fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];4007}4008}40094010#endif //WITH_FLOAT4011}40124013count += num;4014}40154016synth->cur = num;40174018time = fluid_utime() - time;4019cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4020fluid_atomic_float_set(&synth->cpu_load, cpu_load);40214022return FLUID_OK;4023}40244025/**4026* mixes the samples of \p in to \p out4027*4028* @param out the output sample buffer to mix to4029* @param ooff sample offset in \p out4030* @param in the rvoice_mixer input sample buffer to mix from4031* @param ioff sample offset in \p in4032* @param buf_idx the sample buffer index of \p in to mix from4033* @param num number of samples to mix4034*/4035static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out,4036int ooff,4037const fluid_real_t *FLUID_RESTRICT in,4038int ioff,4039int buf_idx,4040int num)4041{4042if(out != NULL)4043{4044int j;40454046for(j = 0; j < num; j++)4047{4048out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];4049}4050}4051}40524053/**4054* Synthesize floating point audio to stereo audio channels4055* (implements the default interface #fluid_audio_func_t).4056*4057* @param synth FluidSynth instance4058*4059* @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx.4060* Zero value is permitted, the function does nothing and return FLUID_OK.4061*4062* @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo)4063* and in the range <code>0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.4064* Note that zero value is valid and allows to skip mixing effects in all fx output buffers.4065*4066* @param fx Array of buffers to store effects audio to. Buffers may4067* alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer.4068*4069* @param nout Count of arrays in \c out. Must be a multiple of 24070* (because of stereo) and in the range <code>0 <= nout/2 <= fluid_synth_count_audio_channels()</code>.4071* Note that zero value is valid and allows to skip mixing dry audio in all out output buffers.4072*4073* @param out Array of buffers to store (dry) audio to. Buffers may4074* alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer.4075*4076* @return #FLUID_OK on success,4077* #FLUID_FAILED otherwise,4078* - <code>fx == NULL</code> while <code>nfx > 0</code>, or <code>out == NULL</code> while <code>nout > 0</code>.4079* - \c nfx or \c nout not multiple of 2.4080* - <code>len < 0</code>.4081* - \c nfx or \c nout exceed the range explained above.4082*4083* Synthesize and <strong>mix</strong> audio to a given number of planar audio buffers.4084* Therefore pass <code>nout = N*2</code> float buffers to \p out in order to render4085* the synthesized audio to \p N stereo channels. Each float buffer must be4086* able to hold \p len elements.4087*4088* \p out contains an array of planar buffers for normal, dry, stereo4089* audio (alternating left and right). Like:4090@code{.cpp}4091out[0] = left_buffer_audio_channel_04092out[1] = right_buffer_audio_channel_04093out[2] = left_buffer_audio_channel_14094out[3] = right_buffer_audio_channel_14095...4096out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i4097out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i4098@endcode4099*4100* for zero-based channel index \p i.4101* The buffer layout of \p fx used for storing effects4102* like reverb and chorus looks similar:4103@code{.cpp}4104fx[0] = left_buffer_channel_of_reverb_unit_04105fx[1] = right_buffer_channel_of_reverb_unit_04106fx[2] = left_buffer_channel_of_chorus_unit_04107fx[3] = right_buffer_channel_of_chorus_unit_04108fx[4] = left_buffer_channel_of_reverb_unit_14109fx[5] = right_buffer_channel_of_reverb_unit_14110fx[6] = left_buffer_channel_of_chorus_unit_14111fx[7] = right_buffer_channel_of_chorus_unit_14112fx[8] = left_buffer_channel_of_reverb_unit_24113...4114fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k4115fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k4116@endcode4117* where <code>0 <= k < fluid_synth_count_effects_groups()</code> is a zero-based index denoting the effects unit and4118* <code>0 <= j < fluid_synth_count_effects_channels()</code> is a zero-based index denoting the effect channel within4119* unit \p k.4120*4121* Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the4122* zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is4123* going to be rendered to use:4124*4125* <code>i = chan % fluid_synth_count_audio_groups()</code>4126*4127* <code>k = chan % fluid_synth_count_effects_groups()</code>4128*4129* @parblock4130* @note The owner of the sample buffers must zero them out before calling this4131* function, because any synthesized audio is mixed (i.e. added) to the buffers.4132* E.g. if fluid_synth_process() is called from a custom audio driver process function4133* (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers.4134* @endparblock4135*4136* @parblock4137* @note No matter how many buffers you pass in, fluid_synth_process()4138* will always render all audio channels to the4139* buffers in \c out and all effects channels to the4140* buffers in \c fx, provided that <code>nout > 0</code> and <code>nfx > 0</code> respectively. If4141* <code>nout/2 < fluid_synth_count_audio_channels()</code> it will wrap around. Same4142* is true for effects audio if <code>nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.4143* See usage examples below.4144* @endparblock4145*4146* @parblock4147* @note Should only be called from synthesis thread.4148* @endparblock4149*/4150int4151fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],4152int nout, float *out[])4153{4154return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks);4155}41564157/* declared public (instead of static) for testing purpose */4158int4159fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],4160int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int))4161{4162fluid_real_t *left_in, *fx_left_in;4163fluid_real_t *right_in, *fx_right_in;4164int nfxchan, nfxunits, naudchan;41654166double time = fluid_utime();4167int i, f, num, count, buffered_blocks;41684169float cpu_load;41704171fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);41724173/* fx NULL while nfx > 0 is invalid */4174fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED);4175/* nfx must be multiple of 2. Note that 0 value is valid and4176allows to skip mixing in fx output buffers4177*/4178fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);41794180/* out NULL while nout > 0 is invalid */4181fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED);4182/* nout must be multiple of 2. Note that 0 value is valid and4183allows to skip mixing in out output buffers4184*/4185fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);41864187/* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK.4188*/4189fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4190fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below41914192nfxchan = synth->effects_channels;4193nfxunits = synth->effects_groups;4194naudchan = synth->audio_channels;41954196fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED);4197fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED);41984199/* get internal mixer audio dry buffer's pointer (left and right channel) */4200fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4201/* get internal mixer audio effect buffer's pointer (left and right channel) */4202fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);42034204/* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one4205stereo output) we don't want rendered audio effect mixed in internal audio dry buffers.4206FALSE instructs the mixer that internal audio effects will be mixed in respective internal4207audio effects buffers.4208*/4209fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE);421042114212/* First, take what's still available in the buffer */4213count = 0;4214/* synth->cur indicates if available samples are still in internal mixer buffer */4215num = synth->cur;42164217buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4218if(synth->cur < buffered_blocks * FLUID_BUFSIZE)4219{4220/* yes, available sample are in internal mixer buffer */4221int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur;4222num = (available > len) ? len : available;42234224/* mixing dry samples (or skip if requested by the caller) */4225if(nout != 0)4226{4227for(i = 0; i < naudchan; i++)4228{4229/* mix num left samples from input mixer buffer (left_in) at input offset4230synth->cur to output buffer (out_buf) at offset 0 */4231float *out_buf = out[(i * 2) % nout];4232fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num);42334234/* mix num right samples from input mixer buffer (right_in) at input offset4235synth->cur to output buffer (out_buf) at offset 0 */4236out_buf = out[(i * 2 + 1) % nout];4237fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num);4238}4239}42404241/* mixing effects samples (or skip if requested by the caller) */4242if(nfx != 0)4243{4244// loop over all effects units4245for(f = 0; f < nfxunits; f++)4246{4247// write out all effects (i.e. reverb and chorus)4248for(i = 0; i < nfxchan; i++)4249{4250int buf_idx = f * nfxchan + i;42514252/* mix num left samples from input mixer buffer (fx_left_in) at input offset4253synth->cur to output buffer (out_buf) at offset 0 */4254float *out_buf = fx[(buf_idx * 2) % nfx];4255fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);42564257/* mix num right samples from input mixer buffer (fx_right_in) at input offset4258synth->cur to output buffer (out_buf) at offset 0 */4259out_buf = fx[(buf_idx * 2 + 1) % nfx];4260fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num);4261}4262}4263}42644265count += num;4266num += synth->cur; /* if we're now done, num becomes the new synth->cur below */4267}42684269/* Then, render blocks and copy till we have 'len' samples */4270while(count < len)4271{4272/* always render full bloc multiple of FLUID_BUFSIZE */4273int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4274/* render audio (dry and effect) to respective internal dry and effect buffers */4275int blockcount = block_render_func(synth, blocksleft);42764277num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE;42784279/* mixing dry samples (or skip if requested by the caller) */4280if(nout != 0)4281{4282for(i = 0; i < naudchan; i++)4283{4284/* mix num left samples from input mixer buffer (left_in) at input offset42850 to output buffer (out_buf) at offset count */4286float *out_buf = out[(i * 2) % nout];4287fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num);42884289/* mix num right samples from input mixer buffer (right_in) at input offset42900 to output buffer (out_buf) at offset count */4291out_buf = out[(i * 2 + 1) % nout];4292fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num);4293}4294}42954296/* mixing effects samples (or skip if requested by the caller) */4297if(nfx != 0)4298{4299// loop over all effects units4300for(f = 0; f < nfxunits; f++)4301{4302// write out all effects (i.e. reverb and chorus)4303for(i = 0; i < nfxchan; i++)4304{4305int buf_idx = f * nfxchan + i;43064307/* mix num left samples from input mixer buffer (fx_left_in) at input offset43080 to output buffer (out_buf) at offset count */4309float *out_buf = fx[(buf_idx * 2) % nfx];4310fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);43114312/* mix num right samples from input mixer buffer (fx_right_in) at input offset43130 to output buffer (out_buf) at offset count */4314out_buf = fx[(buf_idx * 2 + 1) % nfx];4315fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);4316}4317}4318}43194320count += num;4321}43224323synth->cur = num;43244325time = fluid_utime() - time;4326cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4327fluid_atomic_float_set(&synth->cpu_load, cpu_load);43284329return FLUID_OK;4330}433143324333/**4334* Synthesize a block of floating point audio samples to audio buffers.4335* @param synth FluidSynth instance4336* @param len Count of audio frames to synthesize4337* @param lout Array of floats to store left channel of audio4338* @param loff Offset index in 'lout' for first sample4339* @param lincr Increment between samples stored to 'lout'4340* @param rout Array of floats to store right channel of audio4341* @param roff Offset index in 'rout' for first sample4342* @param rincr Increment between samples stored to 'rout'4343* @return #FLUID_OK on success, #FLUID_FAILED otherwise4344*4345* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,4346* lincr = 2, rincr = 2).4347*4348* @note Should only be called from synthesis thread.4349* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4350*/4351int4352fluid_synth_write_float(fluid_synth_t *synth, int len,4353void *lout, int loff, int lincr,4354void *rout, int roff, int rincr)4355{4356void *channels_out[2] = {lout, rout};4357int channels_off[2] = {loff, roff };4358int channels_incr[2] = {lincr, rincr };43594360return fluid_synth_write_float_channels(synth, len, 2, channels_out,4361channels_off, channels_incr);4362}43634364/**4365* Synthesize a block of float audio samples channels to audio buffers.4366* The function is convenient for audio driver to render multiple stereo4367* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).4368*4369* @param synth FluidSynth instance.4370* @param len Count of audio frames to synthesize.4371* @param channels_count Count of channels in a frame.4372* must be multiple of 2 and channel_count/2 must not exceed the number4373* of internal mixer buffers (synth->audio_groups)4374* @param channels_out Array of channels_count pointers on 16 bit words to4375* store sample channels. Modified on return.4376* @param channels_off Array of channels_count offset index to add to respective pointer4377* in channels_out for first sample.4378* @param channels_incr Array of channels_count increment between consecutive4379* samples channels.4380* @return #FLUID_OK on success, #FLUID_FAILED otherwise.4381*4382* Useful for storing:4383* - interleaved channels in a unique buffer.4384* - non interleaved channels in an unique buffer (or in distinct buffers).4385*4386* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)4387* in a unique buffer:4388* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,...4389* sn:c1, sn:c2, sn:c3, sn:c4 }.4390*4391* @note Should only be called from synthesis thread.4392* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4393*/4394int4395fluid_synth_write_float_channels(fluid_synth_t *synth, int len,4396int channels_count,4397void *channels_out[], int channels_off[],4398int channels_incr[])4399{4400return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count,4401channels_out, channels_off, channels_incr,4402fluid_synth_render_blocks);4403}44044405int4406fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len,4407int channels_count,4408void *channels_out[], int channels_off[],4409int channels_incr[],4410int (*block_render_func)(fluid_synth_t *, int))4411{4412float **chan_out = (float **)channels_out;4413int n, cur, size;44144415/* pointers on first input mixer buffer */4416fluid_real_t *left_in;4417fluid_real_t *right_in;4418int bufs_in_count; /* number of stereo input buffers */4419int i;44204421/* start average cpu load probe */4422double time = fluid_utime();4423float cpu_load;44244425/* start profiling duration probe (if profiling is enabled) */4426fluid_profile_ref_var(prof_ref);44274428/* check parameters */4429fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);44304431fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4432fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below44334434/* check for valid channel_count: must be multiple of 2 and4435channel_count/2 must not exceed the number of internal mixer buffers4436(synth->audio_groups)4437*/4438fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */4439&& channels_count >= 2, FLUID_FAILED);44404441bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */4442fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);44434444fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);4445fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);4446fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);44474448/* initialize output channels buffers on first sample position */4449i = channels_count;4450do4451{4452i--;4453chan_out[i] += channels_off[i];4454}4455while(i);44564457/* Conversely to fluid_synth_process(),4458we want rendered audio effect mixed in internal audio dry buffers.4459TRUE instructs the mixer that internal audio effects will be mixed in internal4460audio dry buffers.4461*/4462fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);44634464/* get first internal mixer audio dry buffer's pointer (left and right channel) */4465fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);44664467size = len;44684469/* synth->cur indicates if available samples are still in internal mixer buffer */4470cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */44714472do4473{4474/* fill up the buffers as needed */4475if(cur >= synth->curmax)4476{4477/* render audio (dry and effect) to internal dry buffers */4478/* always render full blocs multiple of FLUID_BUFSIZE */4479int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4480synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);44814482/* get first internal mixer audio dry buffer's pointer (left and right channel) */4483fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4484cur = 0;4485}44864487/* calculate amount of available samples */4488n = synth->curmax - cur;44894490/* keep track of emitted samples */4491if(n > size)4492{4493n = size;4494}44954496size -= n;44974498/* update pointers to current position */4499left_in += cur + n;4500right_in += cur + n;45014502/* set final cursor position */4503cur += n;45044505/* reverse index */4506n = 0 - n;45074508do4509{4510i = bufs_in_count;4511do4512{4513/* input sample index in stereo buffer i */4514int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;4515int c = i << 1; /* channel index c to write */45164517/* write left input sample to channel sample */4518*chan_out[c] = (float) left_in[in_idx];45194520/* write right input sample to next channel sample */4521*chan_out[c+1] = (float) right_in[in_idx];45224523/* advance output pointers */4524chan_out[c] += channels_incr[c];4525chan_out[c+1] += channels_incr[c+1];4526}4527while(i);4528}4529while(++n < 0);4530}4531while(size);45324533synth->cur = cur; /* save current sample position. It will be used on next call */45344535/* save average cpu load, use by API for real time cpu load meter */4536time = fluid_utime() - time;4537cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4538fluid_atomic_float_set(&synth->cpu_load, cpu_load);45394540/* stop duration probe and save performance measurement (if profiling is enabled) */4541fluid_profile_write(FLUID_PROF_WRITE, prof_ref,4542fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4543len);4544return FLUID_OK;4545}45464547/* for testing purpose */4548int4549fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,4550void *lout, int loff, int lincr,4551void *rout, int roff, int rincr,4552int (*block_render_func)(fluid_synth_t *, int)4553)4554{4555void *channels_out[2] = {lout, rout};4556int channels_off[2] = {loff, roff };4557int channels_incr[2] = {lincr, rincr };45584559return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out,4560channels_off, channels_incr,4561block_render_func);4562}456345644565#define DITHER_SIZE 480004566#define DITHER_CHANNELS 245674568static float rand_table[DITHER_CHANNELS][DITHER_SIZE];45694570/* Init dither table */4571static void4572init_dither(void)4573{4574float d, dp;4575int c, i;45764577for(c = 0; c < DITHER_CHANNELS; c++)4578{4579dp = 0;45804581for(i = 0; i < DITHER_SIZE - 1; i++)4582{4583d = rand() / (float)RAND_MAX - 0.5f;4584rand_table[c][i] = d - dp;4585dp = d;4586}45874588rand_table[c][DITHER_SIZE - 1] = 0 - dp;4589}4590}45914592/* A portable replacement for roundf(), seems it may actually be faster too! */4593static FLUID_INLINE int16_t4594round_clip_to_i16(float x)4595{4596long i;45974598if(x >= 0.0f)4599{4600i = (long)(x + 0.5f);46014602if(FLUID_UNLIKELY(i > 32767))4603{4604i = 32767;4605}4606}4607else4608{4609i = (long)(x - 0.5f);46104611if(FLUID_UNLIKELY(i < -32768))4612{4613i = -32768;4614}4615}46164617return (int16_t)i;4618}46194620/**4621* Synthesize a block of 16 bit audio samples to audio buffers.4622* @param synth FluidSynth instance4623* @param len Count of audio frames to synthesize4624* @param lout Array of 16 bit words to store left channel of audio4625* @param loff Offset index in 'lout' for first sample4626* @param lincr Increment between samples stored to 'lout'4627* @param rout Array of 16 bit words to store right channel of audio4628* @param roff Offset index in 'rout' for first sample4629* @param rincr Increment between samples stored to 'rout'4630* @return #FLUID_OK on success, #FLUID_FAILED otherwise4631*4632* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,4633* lincr = 2, rincr = 2).4634*4635* @note Should only be called from synthesis thread.4636* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4637* @note Dithering is performed when converting from internal floating point to4638* 16 bit audio.4639*/4640int4641fluid_synth_write_s16(fluid_synth_t *synth, int len,4642void *lout, int loff, int lincr,4643void *rout, int roff, int rincr)4644{4645void *channels_out[2] = {lout, rout};4646int channels_off[2] = {loff, roff };4647int channels_incr[2] = {lincr, rincr };46484649return fluid_synth_write_s16_channels(synth, len, 2, channels_out,4650channels_off, channels_incr);4651}46524653/**4654* Synthesize a block of 16 bit audio samples channels to audio buffers.4655* The function is convenient for audio driver to render multiple stereo4656* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).4657*4658* @param synth FluidSynth instance.4659* @param len Count of audio frames to synthesize.4660* @param channels_count Count of channels in a frame.4661* must be multiple of 2 and channel_count/2 must not exceed the number4662* of internal mixer buffers (synth->audio_groups)4663* @param channels_out Array of channels_count pointers on 16 bit words to4664* store sample channels. Modified on return.4665* @param channels_off Array of channels_count offset index to add to respective pointer4666* in channels_out for first sample.4667* @param channels_incr Array of channels_count increment between consecutive4668* samples channels.4669* @return #FLUID_OK on success, #FLUID_FAILED otherwise.4670*4671* Useful for storing:4672* - interleaved channels in a unique buffer.4673* - non interleaved channels in an unique buffer (or in distinct buffers).4674*4675* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)4676* in a unique buffer:4677* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, ....4678* sn:c1, sn:c2, sn:c3, sn:c4 }.4679*4680* @note Should only be called from synthesis thread.4681* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4682* @note Dithering is performed when converting from internal floating point to4683* 16 bit audio.4684*/4685int4686fluid_synth_write_s16_channels(fluid_synth_t *synth, int len,4687int channels_count,4688void *channels_out[], int channels_off[],4689int channels_incr[])4690{4691int16_t **chan_out = (int16_t **)channels_out;4692int di, n, cur, size;46934694/* pointers on first input mixer buffer */4695fluid_real_t *left_in;4696fluid_real_t *right_in;4697int bufs_in_count; /* number of stereo input buffers */4698int i;46994700/* start average cpu load probe */4701double time = fluid_utime();4702float cpu_load;47034704/* start profiling duration probe (if profiling is enabled) */4705fluid_profile_ref_var(prof_ref);47064707/* check parameters */4708fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);47094710fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4711fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below47124713/* check for valid channel_count: must be multiple of 2 and4714channel_count/2 must not exceed the number of internal mixer buffers4715(synth->audio_groups)4716*/4717fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */4718&& channels_count >= 2, FLUID_FAILED);47194720bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */4721fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);47224723fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);4724fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);4725fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);47264727/* initialize output channels buffers on first sample position */4728i = channels_count;4729do4730{4731i--;4732chan_out[i] += channels_off[i];4733}4734while(i);47354736/* Conversely to fluid_synth_process(),4737we want rendered audio effect mixed in internal audio dry buffers.4738TRUE instructs the mixer that internal audio effects will be mixed in internal4739audio dry buffers.4740*/4741fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);4742/* get first internal mixer audio dry buffer's pointer (left and right channel) */4743fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);47444745size = len;4746/* synth->cur indicates if available samples are still in internal mixer buffer */4747cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */4748di = synth->dither_index;47494750do4751{4752/* fill up the buffers as needed */4753if(cur >= synth->curmax)4754{4755/* render audio (dry and effect) to internal dry buffers */4756/* always render full blocs multiple of FLUID_BUFSIZE */4757int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4758synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);47594760/* get first internal mixer audio dry buffer's pointer (left and right channel) */4761fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4762cur = 0;4763}47644765/* calculate amount of available samples */4766n = synth->curmax - cur;47674768/* keep track of emitted samples */4769if(n > size)4770{4771n = size;4772}47734774size -= n;47754776/* update pointers to current position */4777left_in += cur + n;4778right_in += cur + n;47794780/* set final cursor position */4781cur += n;47824783/* reverse index */4784n = 0 - n;47854786do4787{4788i = bufs_in_count;4789do4790{4791/* input sample index in stereo buffer i */4792int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;4793int c = i << 1; /* channel index c to write */47944795/* write left input sample to channel sample */4796*chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f +4797rand_table[0][di]);47984799/* write right input sample to next channel sample */4800*chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f +4801rand_table[1][di]);4802/* advance output pointers */4803chan_out[c] += channels_incr[c];4804chan_out[c+1] += channels_incr[c+1];4805}4806while(i);48074808if(++di >= DITHER_SIZE)4809{4810di = 0;4811}4812}4813while(++n < 0);4814}4815while(size);48164817synth->cur = cur; /* save current sample position. It will be used on next call */4818synth->dither_index = di; /* keep dither buffer continuous */48194820/* save average cpu load, used by API for real time cpu load meter */4821time = fluid_utime() - time;4822cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4823fluid_atomic_float_set(&synth->cpu_load, cpu_load);48244825/* stop duration probe and save performance measurement (if profiling is enabled) */4826fluid_profile_write(FLUID_PROF_WRITE, prof_ref,4827fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4828len);4829return FLUID_OK;4830}48314832/**4833* Converts stereo floating point sample data to signed 16 bit data with dithering.4834* @param dither_index Pointer to an integer which should be initialized to 04835* before the first call and passed unmodified to additional calls which are4836* part of the same synthesis output.4837* @param len Length in frames to convert4838* @param lin Buffer of left audio samples to convert from4839* @param rin Buffer of right audio samples to convert from4840* @param lout Array of 16 bit words to store left channel of audio4841* @param loff Offset index in 'lout' for first sample4842* @param lincr Increment between samples stored to 'lout'4843* @param rout Array of 16 bit words to store right channel of audio4844* @param roff Offset index in 'rout' for first sample4845* @param rincr Increment between samples stored to 'rout'4846*4847* @note Currently private to libfluidsynth.4848*/4849void4850fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin,4851void *lout, int loff, int lincr,4852void *rout, int roff, int rincr)4853{4854int i, j, k;4855int16_t *left_out = lout;4856int16_t *right_out = rout;4857int di = *dither_index;4858fluid_profile_ref_var(prof_ref);48594860for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr)4861{4862left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]);4863right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]);48644865if(++di >= DITHER_SIZE)4866{4867di = 0;4868}4869}48704871*dither_index = di; /* keep dither buffer continuous */48724873fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len);4874}48754876static void4877fluid_synth_check_finished_voices(fluid_synth_t *synth)4878{4879int j;4880fluid_rvoice_t *fv;48814882while(NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler)))4883{4884for(j = 0; j < synth->polyphony; j++)4885{4886if(synth->voice[j]->rvoice == fv)4887{4888fluid_voice_unlock_rvoice(synth->voice[j]);4889fluid_voice_stop(synth->voice[j]);4890break;4891}4892else if(synth->voice[j]->overflow_rvoice == fv)4893{4894/* Unlock the overflow_rvoice of the voice.4895Decrement the reference count of the sample owned by this4896rvoice.4897*/4898fluid_voice_overflow_rvoice_finished(synth->voice[j]);48994900/* Decrement synth active voice count. Must not be incorporated4901in fluid_voice_overflow_rvoice_finished() because4902fluid_voice_overflow_rvoice_finished() is called also4903at synth destruction and in this case the variable should be4904accessed via voice->channel->synth->active_voice_count.4905And for certain voices which are not playing, the field4906voice->channel is NULL.4907*/4908synth->active_voice_count--;4909break;4910}4911}4912}4913}49144915/**4916* Process all waiting events in the rvoice queue.4917* Make sure no (other) rendering is running in parallel when4918* you call this function!4919*/4920void fluid_synth_process_event_queue(fluid_synth_t *synth)4921{4922fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);4923}492449254926/**4927* Process blocks (FLUID_BUFSIZE) of audio.4928* Must be called from renderer thread only!4929* @return number of blocks rendered. Might (often) return less than requested4930*/4931static int4932fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount)4933{4934int i, maxblocks;4935fluid_profile_ref_var(prof_ref);49364937/* Assign ID of synthesis thread */4938// synth->synth_thread_id = fluid_thread_get_id ();49394940fluid_check_fpe("??? Just starting up ???");49414942fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);49434944/* do not render more blocks than we can store internally */4945maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer);49464947if(blockcount > maxblocks)4948{4949blockcount = maxblocks;4950}49514952for(i = 0; i < blockcount; i++)4953{4954fluid_sample_timer_process(synth);4955fluid_synth_add_ticks(synth, FLUID_BUFSIZE);49564957/* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all()4958* (should only happen with parallel render) stop processing and go for rendering4959*/4960if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler))4961{4962// Something has happened, we can't process more4963blockcount = i + 1;4964break;4965}4966}49674968fluid_check_fpe("fluid_sample_timer_process");49694970blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount);49714972/* Testcase, that provokes a denormal floating point error */4973#if 04974{4975float num = 1;49764977while(num != 0)4978{4979num *= 0.5;4980};4981};4982#endif4983fluid_check_fpe("??? Remainder of synth_one_block ???");4984fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref,4985fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4986blockcount * FLUID_BUFSIZE);4987return blockcount;4988}49894990/*4991* Handler for synth.reverb.* and synth.chorus.* double settings.4992*/4993static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value)4994{4995fluid_synth_t *synth = (fluid_synth_t *)data;4996fluid_return_if_fail(synth != NULL);49974998if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0)4999{5000fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value);5001}5002else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0)5003{5004fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value);5005}5006else if(FLUID_STRCMP(name, "synth.reverb.width") == 0)5007{5008fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value);5009}5010else if(FLUID_STRCMP(name, "synth.reverb.level") == 0)5011{5012fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value);5013}5014else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0)5015{5016fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value);5017}5018else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0)5019{5020fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value);5021}5022else if(FLUID_STRCMP(name, "synth.chorus.level") == 0)5023{5024fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value);5025}5026}50275028/*5029* Handler for synth.reverb.* and synth.chorus.* integer settings.5030*/5031static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value)5032{5033fluid_synth_t *synth = (fluid_synth_t *)data;5034fluid_return_if_fail(synth != NULL);50355036if(FLUID_STRCMP(name, "synth.reverb.active") == 0)5037{5038fluid_synth_reverb_on(synth, -1, value);5039}5040else if(FLUID_STRCMP(name, "synth.chorus.active") == 0)5041{5042fluid_synth_chorus_on(synth, -1, value);5043}5044else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0)5045{5046fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value);5047}5048}50495050/*5051* Handler for synth.overflow.* settings.5052*/5053static void fluid_synth_handle_overflow(void *data, const char *name, double value)5054{5055fluid_synth_t *synth = (fluid_synth_t *)data;5056fluid_return_if_fail(synth != NULL);50575058fluid_synth_api_enter(synth);50595060if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0)5061{5062synth->overflow.percussion = value;5063}5064else if(FLUID_STRCMP(name, "synth.overflow.released") == 0)5065{5066synth->overflow.released = value;5067}5068else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0)5069{5070synth->overflow.sustained = value;5071}5072else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0)5073{5074synth->overflow.volume = value;5075}5076else if(FLUID_STRCMP(name, "synth.overflow.age") == 0)5077{5078synth->overflow.age = value;5079}5080else if(FLUID_STRCMP(name, "synth.overflow.important") == 0)5081{5082synth->overflow.important = value;5083}50845085fluid_synth_api_exit(synth);5086}50875088/* Selects a voice for killing. */5089static fluid_voice_t *5090fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth)5091{5092int i;5093float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1;5094float this_voice_prio;5095fluid_voice_t *voice;5096int best_voice_index = -1;5097unsigned int ticks = fluid_synth_get_ticks(synth);50985099for(i = 0; i < synth->polyphony; i++)5100{51015102voice = synth->voice[i];51035104/* safeguard against an available voice. */5105if(_AVAILABLE(voice))5106{5107return voice;5108}51095110this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow,5111ticks);51125113/* check if this voice has less priority than the previous candidate. */5114if(this_voice_prio < best_prio)5115{5116best_voice_index = i;5117best_prio = this_voice_prio;5118}5119}51205121if(best_voice_index < 0)5122{5123return NULL;5124}51255126voice = synth->voice[best_voice_index];5127FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ",5128fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice));5129fluid_voice_off(voice);51305131return voice;5132}513351345135/**5136* Allocate a synthesis voice.5137* @param synth FluidSynth instance5138* @param sample Sample to assign to the voice5139* @param chan MIDI channel number (0 to MIDI channel count - 1)5140* @param key MIDI note number for the voice (0-127)5141* @param vel MIDI velocity for the voice (0-127)5142* @return Allocated synthesis voice or NULL on error5143*5144* This function is called by a SoundFont's preset in response to a noteon event.5145* The returned voice comes with default modulators and generators.5146* A single noteon event may create any number of voices, when the preset is layered.5147*5148* @note Should only be called from within synthesis thread, which includes5149* SoundFont loader preset noteon method.5150*/5151fluid_voice_t *5152fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample,5153int chan, int key, int vel)5154{5155fluid_return_val_if_fail(sample != NULL, NULL);5156fluid_return_val_if_fail(sample->data != NULL, NULL);5157FLUID_API_ENTRY_CHAN(NULL);5158FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL));51595160}51615162fluid_voice_t *5163fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range)5164{5165int i, k;5166fluid_voice_t *voice = NULL;5167fluid_channel_t *channel = NULL;5168unsigned int ticks;51695170/* check if there's an available synthesis process */5171for(i = 0; i < synth->polyphony; i++)5172{5173if(_AVAILABLE(synth->voice[i]))5174{5175voice = synth->voice[i];5176break;5177}5178}51795180/* No success yet? Then stop a running voice. */5181if(voice == NULL)5182{5183FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice");5184voice = fluid_synth_free_voice_by_kill_LOCAL(synth);5185}51865187if(voice == NULL)5188{5189FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);5190return NULL;5191}51925193ticks = fluid_synth_get_ticks(synth);51945195if(synth->verbose)5196{5197k = 0;51985199for(i = 0; i < synth->polyphony; i++)5200{5201if(!_AVAILABLE(synth->voice[i]))5202{5203k++;5204}5205}52065207FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",5208chan, key, vel, synth->storeid,5209(float) ticks / 44100.0f,5210(fluid_curtime() - synth->start) / 1000.0f,52110.0f,5212k);5213}52145215channel = synth->channel[chan];52165217if(fluid_voice_init(voice, sample, zone_range, channel, key, vel,5218synth->storeid, ticks, synth->gain) != FLUID_OK)5219{5220FLUID_LOG(FLUID_WARN, "Failed to initialize voice");5221return NULL;5222}52235224/* add the default modulators to the synthesis process. */5225/* custom_breath2att_modulator is not a default modulator specified in SF5226it is intended to replace default_vel2att_mod for this channel on demand using5227API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel.5228*/5229{5230int mono = fluid_channel_is_playing_mono(channel);5231fluid_mod_t *default_mod = synth->default_mod;52325233while(default_mod != NULL)5234{5235if(5236/* See if default_mod is the velocity_to_attenuation modulator */5237fluid_mod_test_identity(default_mod, &default_vel2att_mod) &&5238// See if a replacement by custom_breath2att_modulator has been demanded5239// for this channel5240((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) ||5241(mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO)))5242)5243{5244// Replacement of default_vel2att modulator by custom_breath2att_modulator5245fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);5246}5247else5248{5249fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);5250}52515252// Next default modulator to add to the voice5253default_mod = default_mod->next;5254}5255}52565257return voice;5258}52595260/* Kill all voices on a given channel, which have the same exclusive class5261* generator as new_voice.5262*/5263static void5264fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth,5265fluid_voice_t *new_voice)5266{5267int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS);5268int i;52695270/* Excl. class 0: No exclusive class */5271if(excl_class == 0)5272{5273return;5274}52755276/* Kill all notes on the same channel with the same exclusive class */5277for(i = 0; i < synth->polyphony; i++)5278{5279fluid_voice_t *existing_voice = synth->voice[i];52805281/* If voice is playing, on the same channel, has same exclusive5282* class and is not part of the same noteon event (voice group), then kill it */52835284if(fluid_voice_is_playing(existing_voice)5285&& fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice)5286&& fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS) == excl_class5287&& fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice))5288{5289fluid_voice_kill_excl(existing_voice);5290}5291}5292}52935294/**5295* Activate a voice previously allocated with fluid_synth_alloc_voice().5296* @param synth FluidSynth instance5297* @param voice Voice to activate5298*5299* This function is called by a SoundFont's preset in response to a noteon5300* event. Exclusive classes are processed here.5301*5302* @note Should only be called from within synthesis thread, which includes5303* SoundFont loader preset noteon method.5304*/5305void5306fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice)5307{5308fluid_return_if_fail(synth != NULL);5309fluid_return_if_fail(voice != NULL);5310// fluid_return_if_fail (fluid_synth_is_synth_thread (synth));5311fluid_synth_api_enter(synth);53125313/* Find the exclusive class of this voice. If set, kill all voices5314* that match the exclusive class and are younger than the first5315* voice process created by this noteon event. */5316fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);53175318fluid_voice_start(voice); /* Start the new voice */5319fluid_voice_lock_rvoice(voice);5320fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);5321fluid_synth_api_exit(synth);5322}53235324/**5325* Add a SoundFont loader to the synth. This function takes ownership of \c loader5326* and frees it automatically upon \c synth destruction.5327* @param synth FluidSynth instance5328* @param loader Loader API structure5329*5330* SoundFont loaders are used to add custom instrument loading to FluidSynth.5331* The caller supplied functions for loading files, allocating presets,5332* retrieving information on them and synthesizing note-on events. Using this5333* method even non SoundFont instruments can be synthesized, although limited5334* to the SoundFont synthesis model.5335*5336* @note Should only be called before any SoundFont files are loaded.5337*/5338void5339fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader)5340{5341fluid_return_if_fail(synth != NULL);5342fluid_return_if_fail(loader != NULL);5343fluid_synth_api_enter(synth);53445345/* Test if sfont is already loaded */5346if(synth->sfont == NULL)5347{5348synth->loaders = fluid_list_prepend(synth->loaders, loader);5349}53505351fluid_synth_api_exit(synth);5352}53535354/**5355* Load a SoundFont file (filename is interpreted by SoundFont loaders).5356* The newly loaded SoundFont will be put on top of the SoundFont5357* stack. Presets are searched starting from the SoundFont on the5358* top of the stack, working the way down the stack until a preset is found.5359*5360* @param synth FluidSynth instance5361* @param filename File to load5362* @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset())5363* @return SoundFont ID on success, #FLUID_FAILED on error5364*5365* @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it5366* to wide-char internally and then pass it to <code>_wfopen()</code>. Before FluidSynth 2.2.0, @c filename was treated as ANSI string5367* on Windows. All other platforms directly pass it to <code>fopen()</code> without any conversion (usually, UTF8 is accepted).5368*/5369int5370fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets)5371{5372fluid_sfont_t *sfont;5373fluid_list_t *list;5374fluid_sfloader_t *loader;5375int sfont_id;53765377fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5378fluid_return_val_if_fail(filename != NULL, FLUID_FAILED);5379fluid_synth_api_enter(synth);53805381sfont_id = synth->sfont_id;53825383if(++sfont_id != FLUID_FAILED)5384{5385/* MT NOTE: Loaders list should not change. */53865387for(list = synth->loaders; list; list = fluid_list_next(list))5388{5389loader = (fluid_sfloader_t *) fluid_list_get(list);53905391sfont = fluid_sfloader_load(loader, filename);53925393if(sfont != NULL)5394{5395sfont->refcount++;5396synth->sfont_id = sfont->id = sfont_id;53975398synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */53995400/* reset the presets for all channels if requested */5401if(reset_presets)5402{5403fluid_synth_program_reset(synth);5404}54055406FLUID_API_RETURN(sfont_id);5407}5408}5409}54105411FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);5412FLUID_API_RETURN(FLUID_FAILED);5413}54145415/**5416* Schedule a SoundFont for unloading.5417*5418* If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately.5419*5420* If any samples of the given SoundFont are still required by active voices,5421* the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing.5422* If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont5423* will be unloaded in any case.5424* Once this function returned, fluid_synth_sfcount() and similar functions will behave as if5425* the SoundFont has already been unloaded, even though the lazy-unloading is still pending.5426*5427* @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a5428* consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain5429* conditions. Calling delete_fluid_synth() does not recover this situation either.5430*5431* @param synth FluidSynth instance5432* @param id ID of SoundFont to unload5433* @param reset_presets TRUE to re-assign presets for all MIDI channels5434* @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise.5435*/5436int5437fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets)5438{5439fluid_sfont_t *sfont = NULL;5440fluid_list_t *list;54415442fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5443fluid_synth_api_enter(synth);54445445/* remove the SoundFont from the list */5446for(list = synth->sfont; list; list = fluid_list_next(list))5447{5448sfont = fluid_list_get(list);54495450if(fluid_sfont_get_id(sfont) == id)5451{5452synth->sfont = fluid_list_remove(synth->sfont, sfont);5453break;5454}5455}54565457if(!list)5458{5459FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);5460FLUID_API_RETURN(FLUID_FAILED);5461}54625463/* reset the presets for all channels (SoundFont will be freed when there are no more references) */5464if(reset_presets)5465{5466fluid_synth_program_reset(synth);5467}5468else5469{5470fluid_synth_update_presets(synth);5471}54725473/* -- Remove synth->sfont list's reference to SoundFont */5474fluid_synth_sfont_unref(synth, sfont);54755476FLUID_API_RETURN(FLUID_OK);5477}54785479/* Unref a SoundFont and destroy if no more references */5480void5481fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont)5482{5483fluid_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */54845485sfont->refcount--; /* -- Remove the sfont list's reference */54865487if(sfont->refcount == 0) /* No more references? - Attempt delete */5488{5489if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */5490{5491FLUID_LOG(FLUID_DBG, "Unloaded SoundFont");5492} /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */5493else5494{5495fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE);5496synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer);5497}5498}5499}55005501/* Callback to continually attempt to unload a SoundFont,5502* only if a SoundFont loader blocked the unload operation */5503static int5504fluid_synth_sfunload_callback(void *data, unsigned int msec)5505{5506fluid_sfont_t *sfont = data;55075508if(fluid_sfont_delete_internal(sfont) == 0)5509{5510FLUID_LOG(FLUID_DBG, "Unloaded SoundFont");5511return FALSE;5512}5513else5514{5515return TRUE;5516}5517}55185519/**5520* Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack.5521* @param synth FluidSynth instance5522* @param id ID of SoundFont to reload5523* @return SoundFont ID on success, #FLUID_FAILED on error5524*/5525int5526fluid_synth_sfreload(fluid_synth_t *synth, int id)5527{5528char *filename = NULL;5529fluid_sfont_t *sfont;5530fluid_sfloader_t *loader;5531fluid_list_t *list;5532int index, ret = FLUID_FAILED;55335534fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5535fluid_synth_api_enter(synth);55365537/* Search for SoundFont and get its index */5538for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++)5539{5540sfont = fluid_list_get(list);55415542if(fluid_sfont_get_id(sfont) == id)5543{5544break;5545}5546}55475548if(!list)5549{5550FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);5551goto exit;5552}55535554/* keep a copy of the SoundFont's filename */5555filename = FLUID_STRDUP(fluid_sfont_get_name(sfont));55565557if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK)5558{5559goto exit;5560}55615562/* MT Note: SoundFont loader list will not change */55635564for(list = synth->loaders; list; list = fluid_list_next(list))5565{5566loader = (fluid_sfloader_t *) fluid_list_get(list);55675568sfont = fluid_sfloader_load(loader, filename);55695570if(sfont != NULL)5571{5572sfont->id = id;5573sfont->refcount++;55745575synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */55765577/* reset the presets for all channels */5578fluid_synth_update_presets(synth);5579ret = id;5580goto exit;5581}5582}55835584FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);55855586exit:5587FLUID_FREE(filename);5588FLUID_API_RETURN(ret);5589}55905591/**5592* Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack and ownership is transferred to @p synth.5593* @param synth FluidSynth instance5594* @param sfont SoundFont to add5595* @return New assigned SoundFont ID or #FLUID_FAILED on error5596*/5597int5598fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)5599{5600int sfont_id;56015602fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5603fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);5604fluid_synth_api_enter(synth);56055606sfont_id = synth->sfont_id;56075608if(++sfont_id != FLUID_FAILED)5609{5610synth->sfont_id = sfont->id = sfont_id;5611synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */56125613/* reset the presets for all channels */5614fluid_synth_program_reset(synth);5615}56165617FLUID_API_RETURN(sfont_id);5618}56195620/**5621* Remove a SoundFont from the SoundFont stack without deleting it.5622* @param synth FluidSynth instance5623* @param sfont SoundFont to remove5624* @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise5625*5626* SoundFont is not freed and is left as the responsibility of the caller.5627*5628* @note The SoundFont should only be freed after there are no presets5629* referencing it. This can only be ensured by the SoundFont loader and5630* therefore this function should not normally be used.5631*/5632int5633fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)5634{5635fluid_sfont_t *sfont_tmp;5636fluid_list_t *list;5637int ret = FLUID_FAILED;56385639fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5640fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);5641fluid_synth_api_enter(synth);56425643/* remove the SoundFont from the list */5644for(list = synth->sfont; list; list = fluid_list_next(list))5645{5646sfont_tmp = fluid_list_get(list);56475648if(sfont_tmp == sfont)5649{5650synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp);5651ret = FLUID_OK;5652break;5653}5654}56555656/* reset the presets for all channels */5657fluid_synth_program_reset(synth);56585659FLUID_API_RETURN(ret);5660}56615662/**5663* Count number of loaded SoundFont files.5664* @param synth FluidSynth instance5665* @return Count of loaded SoundFont files.5666*/5667int5668fluid_synth_sfcount(fluid_synth_t *synth)5669{5670int count;56715672fluid_return_val_if_fail(synth != NULL, 0);5673fluid_synth_api_enter(synth);5674count = fluid_list_size(synth->sfont);5675FLUID_API_RETURN(count);5676}56775678/**5679* Get SoundFont by index.5680* @param synth FluidSynth instance5681* @param num SoundFont index on the stack (starting from 0 for top of stack).5682* @return SoundFont instance or NULL if invalid index5683*5684* @note Caller should be certain that SoundFont is not deleted (unloaded) for5685* the duration of use of the returned pointer.5686*/5687fluid_sfont_t *5688fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num)5689{5690fluid_sfont_t *sfont = NULL;5691fluid_list_t *list;56925693fluid_return_val_if_fail(synth != NULL, NULL);5694fluid_synth_api_enter(synth);5695list = fluid_list_nth(synth->sfont, num);56965697if(list)5698{5699sfont = fluid_list_get(list);5700}57015702FLUID_API_RETURN(sfont);5703}57045705/**5706* Get SoundFont by ID.5707* @param synth FluidSynth instance5708* @param id SoundFont ID5709* @return SoundFont instance or NULL if invalid ID5710*5711* @note Caller should be certain that SoundFont is not deleted (unloaded) for5712* the duration of use of the returned pointer.5713*/5714fluid_sfont_t *5715fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id)5716{5717fluid_sfont_t *sfont = NULL;5718fluid_list_t *list;57195720fluid_return_val_if_fail(synth != NULL, NULL);5721fluid_synth_api_enter(synth);57225723for(list = synth->sfont; list; list = fluid_list_next(list))5724{5725sfont = fluid_list_get(list);57265727if(fluid_sfont_get_id(sfont) == id)5728{5729break;5730}5731}57325733FLUID_API_RETURN(list ? sfont : NULL);5734}57355736/**5737* Get SoundFont by name.5738* @param synth FluidSynth instance5739* @param name Name of SoundFont5740* @return SoundFont instance or NULL if invalid name5741* @since 1.1.05742*5743* @note Caller should be certain that SoundFont is not deleted (unloaded) for5744* the duration of use of the returned pointer.5745*/5746fluid_sfont_t *5747fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name)5748{5749fluid_sfont_t *sfont = NULL;5750fluid_list_t *list;57515752fluid_return_val_if_fail(synth != NULL, NULL);5753fluid_return_val_if_fail(name != NULL, NULL);5754fluid_synth_api_enter(synth);57555756for(list = synth->sfont; list; list = fluid_list_next(list))5757{5758sfont = fluid_list_get(list);57595760if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0)5761{5762break;5763}5764}57655766FLUID_API_RETURN(list ? sfont : NULL);5767}57685769/**5770* Get active preset on a MIDI channel.5771* @param synth FluidSynth instance5772* @param chan MIDI channel number (0 to MIDI channel count - 1)5773* @return Preset or NULL if no preset active on \c chan5774*5775* @note Should only be called from within synthesis thread, which includes5776* SoundFont loader preset noteon methods. Not thread safe otherwise.5777*/5778fluid_preset_t *5779fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan)5780{5781fluid_preset_t *result;5782fluid_channel_t *channel;5783FLUID_API_ENTRY_CHAN(NULL);57845785channel = synth->channel[chan];5786result = channel->preset;5787fluid_synth_api_exit(synth);5788return result;5789}57905791/**5792* Get list of currently playing voices.5793* @param synth FluidSynth instance5794* @param buf Array to store voices to (NULL terminated if not filled completely)5795* @param bufsize Count of indexes in buf5796* @param id Voice ID to search for or < 0 to return list of all playing voices5797*5798* @note Should only be called from within synthesis thread, which includes5799* SoundFont loader preset noteon methods. Voices are only guaranteed to remain5800* unchanged until next synthesis process iteration.5801*/5802void5803fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize,5804int id)5805{5806int count = 0;5807int i;58085809fluid_return_if_fail(synth != NULL);5810fluid_return_if_fail(buf != NULL);5811fluid_synth_api_enter(synth);58125813for(i = 0; i < synth->polyphony && count < bufsize; i++)5814{5815fluid_voice_t *voice = synth->voice[i];58165817if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id))5818{5819buf[count++] = voice;5820}5821}58225823if(count < bufsize)5824{5825buf[count] = NULL;5826}58275828fluid_synth_api_exit(synth);5829}58305831/**5832* Enable or disable reverb effect.5833* @param synth FluidSynth instance5834* @param on TRUE to enable chorus, FALSE to disable5835* @deprecated Use fluid_synth_reverb_on() instead.5836*/5837void5838fluid_synth_set_reverb_on(fluid_synth_t *synth, int on)5839{5840fluid_return_if_fail(synth != NULL);5841fluid_synth_api_enter(synth);58425843synth->with_reverb = (on != 0);5844fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled,5845on != 0, 0.0f);5846fluid_synth_api_exit(synth);5847}58485849/**5850* Enable or disable reverb on one fx group unit.5851* @param synth FluidSynth instance5852* @param fx_group Index of the fx group.5853* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the5854* parameter will be applied to all fx groups.5855* @param on TRUE to enable reverb, FALSE to disable5856* @return #FLUID_OK on success, #FLUID_FAILED otherwise5857*/5858int5859fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on)5860{5861int ret;5862fluid_rvoice_param_t param[MAX_EVENT_PARAMS];5863fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);58645865fluid_synth_api_enter(synth);58665867if(fx_group < -1 || fx_group >= synth->effects_groups)5868{5869FLUID_API_RETURN(FLUID_FAILED);5870}58715872if(fx_group < 0 )5873{5874synth->with_reverb = (on != 0);5875}58765877param[0].i = fx_group;5878param[1].i = on;5879ret = fluid_rvoice_eventhandler_push(synth->eventhandler,5880fluid_rvoice_mixer_reverb_enable,5881synth->eventhandler->mixer,5882param);58835884FLUID_API_RETURN(ret);5885}58865887/**5888* Activate a reverb preset.5889* @param synth FluidSynth instance5890* @param num Reverb preset number5891* @return #FLUID_OK on success, #FLUID_FAILED otherwise5892*5893* @note Currently private to libfluidsynth.5894*/5895int5896fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num)5897{5898double values[FLUID_REVERB_PARAM_LAST];58995900fluid_return_val_if_fail(5901num < FLUID_N_ELEMENTS(revmodel_preset),5902FLUID_FAILED5903);59045905values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize;5906values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp;5907values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width;5908values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level;5909fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values);5910return FLUID_OK;5911}59125913/**5914* Set reverb parameters to all groups.5915*5916* @param synth FluidSynth instance5917* @param roomsize Reverb room size value (0.0-1.0)5918* @param damping Reverb damping value (0.0-1.0)5919* @param width Reverb width value (0.0-100.0)5920* @param level Reverb level value (0.0-1.0)5921* @return #FLUID_OK on success, #FLUID_FAILED otherwise5922* @deprecated Use the individual reverb setter functions in new code instead.5923*/5924int5925fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping,5926double width, double level)5927{5928double values[FLUID_REVERB_PARAM_LAST];59295930fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);59315932values[FLUID_REVERB_ROOMSIZE] = roomsize;5933values[FLUID_REVERB_DAMP] = damping;5934values[FLUID_REVERB_WIDTH] = width;5935values[FLUID_REVERB_LEVEL] = level;5936return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values);5937}59385939/**5940* Set reverb roomsize of all groups.5941*5942* @param synth FluidSynth instance5943* @param roomsize Reverb room size value (0.0-1.0)5944* @return #FLUID_OK on success, #FLUID_FAILED otherwise5945* @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead.5946*/5947int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize)5948{5949return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize);5950}59515952/**5953* Set reverb damping of all groups.5954*5955* @param synth FluidSynth instance5956* @param damping Reverb damping value (0.0-1.0)5957* @return #FLUID_OK on success, #FLUID_FAILED otherwise5958* @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead.5959*/5960int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping)5961{5962return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping);5963}59645965/**5966* Set reverb width of all groups.5967*5968* @param synth FluidSynth instance5969* @param width Reverb width value (0.0-100.0)5970* @return #FLUID_OK on success, #FLUID_FAILED otherwise5971* @deprecated Use fluid_synth_set_reverb_group_width() in new code instead.5972*/5973int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width)5974{5975return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width);5976}59775978/**5979* Set reverb level of all groups.5980*5981* @param synth FluidSynth instance5982* @param level Reverb level value (0.0-1.0)5983* @return #FLUID_OK on success, #FLUID_FAILED otherwise5984* @deprecated Use fluid_synth_set_reverb_group_level() in new code instead.5985*/5986int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level)5987{5988return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, level);5989}59905991/**5992* Set reverb roomsize to one or all fx groups.5993* @param synth FluidSynth instance.5994* @param fx_group Index of the fx group.5995* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the5996* parameter will be applied to all fx groups.5997* @param roomsize roomsize value to set. Must be in the range indicated by5998* synth.reverb.room-size setting.5999* @return #FLUID_OK on success, #FLUID_FAILED otherwise6000*/6001int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group,6002double roomsize)6003{6004return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize);6005}60066007/**6008* Set reverb damp to one or all fx groups.6009* @param synth FluidSynth instance.6010* @param fx_group Index of the fx group.6011* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6012* parameter will be applied to all fx groups.6013* @param damping damping value to set. Must be in the range indicated by6014* synth.reverb.damp setting.6015* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6016*/6017int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group,6018double damping)6019{6020return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping);6021}60226023/**6024* Set reverb width to one or all fx groups.6025* @param synth FluidSynth instance.6026* @param fx_group Index of the fx group.6027* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6028* parameter will be applied to all fx groups.6029* @param width width value to set. Must be in the range indicated by6030* synth.reverb.width setting.6031* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6032*/6033int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group,6034double width)6035{6036return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width);6037}60386039/**6040* Set reverb level to one or all fx groups.6041* @param synth FluidSynth instance.6042* @param fx_group Index of the fx group.6043* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6044* parameter will be applied to all fx groups.6045* @param level output level to set. Must be in the range indicated by6046* synth.reverb.level setting.6047* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6048*/6049int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group,6050double level)6051{6052return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level);6053}60546055/**6056* Set one reverb parameter to one fx groups.6057* @param synth FluidSynth instance.6058* @param fx_group Index of the fx group.6059* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6060* parameter will be applied to all fx groups.6061* @param enum indicating the parameter to set (#fluid_reverb_param).6062* FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0)6063* FLUID_REVERB_DAMP, reverb damping value (0.0-1.0)6064* FLUID_REVERB_WIDTH, reverb width value (0.0-100.0)6065* FLUID_REVERB_LEVEL, reverb level value (0.0-1.0)6066* @param value, parameter value6067* @return #FLUID_OK on success, #FLUID_FAILED otherwise6068*/6069int6070fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group,6071int param, double value)6072{6073int ret;6074double values[FLUID_REVERB_PARAM_LAST] = {0.0};6075static const char *name[FLUID_REVERB_PARAM_LAST] =6076{6077"synth.reverb.room-size", "synth.reverb.damp",6078"synth.reverb.width", "synth.reverb.level"6079};60806081double min; /* minimum value */6082double max; /* maximum value */60836084/* check parameters */6085fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6086fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED);6087fluid_synth_api_enter(synth);60886089if(fx_group < -1 || fx_group >= synth->effects_groups)6090{6091FLUID_API_RETURN(FLUID_FAILED);6092}60936094/* check if reverb value is in max min range */6095fluid_settings_getnum_range(synth->settings, name[param], &min, &max);6096if(value < min || value > max)6097{6098FLUID_API_RETURN(FLUID_FAILED);6099}61006101/* set the value */6102values[param] = value;6103ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values);6104FLUID_API_RETURN(ret);6105}61066107int6108fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set,6109const double values[])6110{6111fluid_rvoice_param_t param[MAX_EVENT_PARAMS];61126113/* if non of the flags is set, fail */6114fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED);61156116/* fx group shadow values are set here so that they will be returned if queried */6117fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set,6118values);61196120/* Synth shadow values are set here so that they will be returned if queried */6121if (fx_group < 0)6122{6123int i;6124for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++)6125{6126if(set & FLUID_REVPARAM_TO_SETFLAG(i))6127{6128synth->reverb_param[i] = values[i];6129}6130}6131}61326133param[0].i = fx_group;6134param[1].i = set;6135param[2].real = values[FLUID_REVERB_ROOMSIZE];6136param[3].real = values[FLUID_REVERB_DAMP];6137param[4].real = values[FLUID_REVERB_WIDTH];6138param[5].real = values[FLUID_REVERB_LEVEL];6139/* finally enqueue an rvoice event to the mixer to actual update reverb */6140return fluid_rvoice_eventhandler_push(synth->eventhandler,6141fluid_rvoice_mixer_set_reverb_params,6142synth->eventhandler->mixer,6143param);6144}61456146/**6147* Get reverb room size of all fx groups.6148* @param synth FluidSynth instance6149* @return Reverb room size (0.0-1.2)6150* @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead.6151*/6152double6153fluid_synth_get_reverb_roomsize(fluid_synth_t *synth)6154{6155double roomsize = 0.0;6156fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize);6157return roomsize;6158}61596160/**6161* Get reverb damping of all fx groups.6162* @param synth FluidSynth instance6163* @return Reverb damping value (0.0-1.0)6164* @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead.6165*/6166double6167fluid_synth_get_reverb_damp(fluid_synth_t *synth)6168{6169double damp = 0.0;6170fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp);6171return damp;6172}61736174/**6175* Get reverb level of all fx groups.6176* @param synth FluidSynth instance6177* @return Reverb level value (0.0-1.0)6178* @deprecated Use fluid_synth_get_reverb_group_level() in new code instead.6179*/6180double6181fluid_synth_get_reverb_level(fluid_synth_t *synth)6182{6183double level = 0.0;6184fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level);6185return level;6186}61876188/**6189* Get reverb width of all fx groups.6190* @param synth FluidSynth instance6191* @return Reverb width value (0.0-100.0)6192* @deprecated Use fluid_synth_get_reverb_group_width() in new code instead.6193*/6194double6195fluid_synth_get_reverb_width(fluid_synth_t *synth)6196{6197double width = 0.0;6198fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width);6199return width;6200}62016202/**6203* get reverb roomsize of one or all groups.6204* @param synth FluidSynth instance.6205* @param fx_group Index of the fx group.6206* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6207* parameter common to all fx groups is fetched.6208* @param roomsize valid pointer on the value to return.6209* @return #FLUID_OK on success, #FLUID_FAILED otherwise6210*/6211int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group,6212double *roomsize)6213{6214return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize);6215}62166217/**6218* get reverb damp of one or all groups.6219* @param synth FluidSynth instance.6220* @param fx_group Index of the fx group.6221* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6222* parameter common to all fx groups is fetched.6223* @param damping valid pointer on the value to return.6224* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6225*/6226int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group,6227double *damping)6228{6229return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping);6230}62316232/**6233* get reverb width of one or all groups6234* @param synth FluidSynth instance.6235* @param fx_group Index of the fx group.6236* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6237* parameter common to all fx groups is fetched.6238* @param width valid pointer on the value to return.6239* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6240*/6241int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group,6242double *width)6243{6244return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width);6245}62466247/**6248* get reverb level of one or all groups.6249* @param synth FluidSynth instance.6250* @param fx_group Index of the fx group.6251* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6252* parameter common to all fx groups is fetched.6253* @param level valid pointer on the value to return.6254* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6255*/6256int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group,6257double *level)6258{6259return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level);6260}626162626263/**6264* Get one reverb parameter value of one fx groups.6265* @param synth FluidSynth instance6266* @param fx_group index of the fx group to get parameter value from.6267* Must be in the range -1 to synth->effects_groups-1. If -1 get the6268* parameter common to all fx groups.6269* @param enum indicating the parameter to get (#fluid_reverb_param).6270* FLUID_REVERB_ROOMSIZE, reverb room size value.6271* FLUID_REVERB_DAMP, reverb damping value.6272* FLUID_REVERB_WIDTH, reverb width value.6273* FLUID_REVERB_LEVEL, reverb level value.6274* @param value pointer on the value to return.6275* @return FLUID_OK if success, FLUID_FAILED otherwise.6276*/6277static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group,6278int param, double *value)6279{6280fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6281fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED);6282fluid_return_val_if_fail(value != NULL, FLUID_FAILED);6283fluid_synth_api_enter(synth);62846285if(fx_group < -1 || fx_group >= synth->effects_groups)6286{6287FLUID_API_RETURN(FLUID_FAILED);6288}62896290if (fx_group < 0)6291{6292/* return reverb param common to all fx groups */6293*value = synth->reverb_param[param];6294}6295else6296{6297/* return reverb param of fx group at index fx_group */6298*value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer,6299fx_group, param);6300}63016302FLUID_API_RETURN(FLUID_OK);6303}63046305/**6306* Enable or disable all chorus groups.6307* @param synth FluidSynth instance6308* @param on TRUE to enable chorus, FALSE to disable6309* @deprecated Use fluid_synth_chorus_on() in new code instead.6310*/6311void6312fluid_synth_set_chorus_on(fluid_synth_t *synth, int on)6313{6314fluid_return_if_fail(synth != NULL);6315fluid_synth_api_enter(synth);63166317synth->with_chorus = (on != 0);6318fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled,6319on != 0, 0.0f);6320fluid_synth_api_exit(synth);6321}63226323/**6324* Enable or disable chorus on one or all groups.6325* @param synth FluidSynth instance6326* @param fx_group Index of the fx group.6327* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6328* parameter will be applied to all fx groups.6329* @param on TRUE to enable chorus, FALSE to disable6330* @return #FLUID_OK on success, #FLUID_FAILED otherwise6331*/6332int6333fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on)6334{6335int ret;6336fluid_rvoice_param_t param[MAX_EVENT_PARAMS];6337fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);63386339fluid_synth_api_enter(synth);63406341if(fx_group < -1 || fx_group >= synth->effects_groups)6342{6343FLUID_API_RETURN(FLUID_FAILED);6344}63456346if(fx_group < 0 )6347{6348synth->with_chorus = (on != 0);6349}63506351param[0].i = fx_group;6352param[1].i = on;6353ret = fluid_rvoice_eventhandler_push(synth->eventhandler,6354fluid_rvoice_mixer_chorus_enable,6355synth->eventhandler->mixer,6356param);63576358FLUID_API_RETURN(ret);6359}63606361/**6362* Set chorus parameters to all fx groups.6363* Keep in mind, that the needed CPU time is proportional to 'nr'.6364* @param synth FluidSynth instance6365* @param nr Chorus voice count (0-99, CPU time consumption proportional to6366* this value)6367* @param level Chorus level (0.0-10.0)6368* @param speed Chorus speed in Hz (0.1-5.0)6369* @param depth_ms Chorus depth (max value depends on synth sample-rate,6370* 0.0-21.0 is safe for sample-rate values up to 96KHz)6371* @param type Chorus waveform type (#fluid_chorus_mod)6372* @return #FLUID_OK on success, #FLUID_FAILED otherwise6373* @deprecated Use the individual chorus setter functions in new code instead.6374*6375* Keep in mind, that the needed CPU time is proportional to 'nr'.6376*/6377int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level,6378double speed, double depth_ms, int type)6379{6380double values[FLUID_CHORUS_PARAM_LAST];63816382fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);63836384values[FLUID_CHORUS_NR] = nr;6385values[FLUID_CHORUS_LEVEL] = level;6386values[FLUID_CHORUS_SPEED] = speed;6387values[FLUID_CHORUS_DEPTH] = depth_ms;6388values[FLUID_CHORUS_TYPE] = type;6389return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values);6390}63916392/**6393* Set the chorus voice count of all groups.6394*6395* @param synth FluidSynth instance6396* @param nr Chorus voice count (0-99, CPU time consumption proportional to6397* this value)6398* @return #FLUID_OK on success, #FLUID_FAILED otherwise6399* @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead.6400*/6401int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr)6402{6403return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr);6404}64056406/**6407* Set the chorus level of all groups.6408*6409* @param synth FluidSynth instance6410* @param level Chorus level (0.0-10.0)6411* @return #FLUID_OK on success, #FLUID_FAILED otherwise6412* @deprecated Use fluid_synth_set_chorus_group_level() in new code instead.6413*/6414int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level)6415{6416return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level);6417}64186419/**6420* Set the chorus speed of all groups.6421*6422* @param synth FluidSynth instance6423* @param speed Chorus speed in Hz (0.1-5.0)6424* @return #FLUID_OK on success, #FLUID_FAILED otherwise6425* @deprecated Use fluid_synth_set_chorus_group_speed() in new code instead.6426*/6427int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed)6428{6429return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed);6430}64316432/**6433* Set the chorus depth of all groups.6434*6435* @param synth FluidSynth instance6436* @param depth_ms Chorus depth (max value depends on synth sample-rate,6437* 0.0-21.0 is safe for sample-rate values up to 96KHz)6438* @return #FLUID_OK on success, #FLUID_FAILED otherwise6439* @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead.6440*/6441int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms)6442{6443return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms);6444}64456446/**6447* Set the chorus type of all groups.6448*6449* @param synth FluidSynth instance6450* @param type Chorus waveform type (#fluid_chorus_mod)6451* @return #FLUID_OK on success, #FLUID_FAILED otherwise6452* @deprecated Use fluid_synth_set_chorus_group_type() in new code instead.6453*/6454int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type)6455{6456return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type);6457}64586459/**6460* Set chorus voice count nr to one or all chorus groups.6461* @param synth FluidSynth instance.6462* @param fx_group Index of the fx group.6463* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6464* parameter will be applied to all groups.6465* @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr}6466* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6467*/6468int6469fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr)6470{6471return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr);6472}64736474/**6475* Set chorus output level to one or all chorus groups.6476* @param synth FluidSynth instance.6477* @param fx_group Index of the fx group.6478* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6479* parameter will be applied to all groups.6480* @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level}6481* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6482*/6483int6484fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level)6485{6486return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level);6487}64886489/**6490* Set chorus lfo speed to one or all chorus groups.6491* @param synth FluidSynth instance.6492* @param fx_group Index of the fx group.6493* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6494* parameter will be applied to all groups.6495* @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed}6496* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6497*/6498int6499fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed)6500{6501return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed);6502}65036504/**6505* Set chorus lfo depth to one or all chorus groups.6506* @param synth FluidSynth instance.6507* @param fx_group Index of the fx group.6508* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6509* parameter will be applied to all groups.6510* @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth}6511* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6512*/6513int6514fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms)6515{6516return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms);6517}65186519/**6520* Set chorus lfo waveform type to one or all chorus groups.6521* @param synth FluidSynth instance.6522* @param fx_group Index of the fx group.6523* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6524* parameter will be applied to all groups.6525* @param type Lfo waveform type to set. (#fluid_chorus_mod)6526* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6527*/6528int6529fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type)6530{6531return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type);6532}65336534/**6535* Set one chorus parameter to one fx groups.6536* @param synth FluidSynth instance.6537* @param fx_group Index of the fx group.6538* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6539* parameter will be applied to all groups.6540* @param enum indicating the parameter to set (#fluid_chorus_param).6541* FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to6542* this value).6543* FLUID_CHORUS_LEVEL, chorus level (0.0-10.0).6544* FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0).6545* FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate,6546* 0.0-21.0 is safe for sample-rate values up to 96KHz).6547* FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod)6548* @param value, parameter value6549* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6550*/6551int6552fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param,6553double value)6554{6555int ret;6556double values[FLUID_CHORUS_PARAM_LAST] = {0.0};65576558/* setting name (except lfo waveform type) */6559static const char *name[FLUID_CHORUS_PARAM_LAST-1] =6560{6561"synth.chorus.nr", "synth.chorus.level",6562"synth.chorus.speed", "synth.chorus.depth"6563};65646565/* check parameters */6566fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6567fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED);6568fluid_synth_api_enter(synth);65696570if(fx_group < -1 || fx_group >= synth->effects_groups)6571{6572FLUID_API_RETURN(FLUID_FAILED);6573}65746575/* check if chorus value is in max min range */6576if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */6577{6578int min = FLUID_CHORUS_MOD_SINE;6579int max = FLUID_CHORUS_MOD_TRIANGLE;6580if(param == FLUID_CHORUS_NR)6581{6582fluid_settings_getint_range(synth->settings, name[param], &min, &max);6583}6584if((int)value < min || (int)value > max)6585{6586FLUID_API_RETURN(FLUID_FAILED);6587}6588}6589else /* float value */6590{6591double min;6592double max;6593fluid_settings_getnum_range(synth->settings, name[param], &min, &max);6594if(value < min || value > max)6595{6596FLUID_API_RETURN(FLUID_FAILED);6597}6598}65996600/* set the value */6601values[param] = value;6602ret = fluid_synth_set_chorus_full(synth, fx_group,6603FLUID_CHORPARAM_TO_SETFLAG(param), values);6604FLUID_API_RETURN(ret);6605}66066607int6608fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set,6609const double values[])6610{6611fluid_rvoice_param_t param[MAX_EVENT_PARAMS];66126613/* if non of the flags is set, fail */6614fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED);66156616/* fx group shadow values are set here so that they will be returned if queried */6617fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group,6618set, values);66196620/* Synth shadow values are set here so that they will be returned if queried */6621if (fx_group < 0)6622{6623int i;6624for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++)6625{6626if(set & FLUID_CHORPARAM_TO_SETFLAG(i))6627{6628synth->chorus_param[i] = values[i];6629}6630}6631}66326633param[0].i = fx_group;6634param[1].i = set;6635param[2].i = (int)values[FLUID_CHORUS_NR];6636param[3].real = values[FLUID_CHORUS_LEVEL];6637param[4].real = values[FLUID_CHORUS_SPEED];6638param[5].real = values[FLUID_CHORUS_DEPTH];6639param[6].i = (int)values[FLUID_CHORUS_TYPE];6640return fluid_rvoice_eventhandler_push(synth->eventhandler,6641fluid_rvoice_mixer_set_chorus_params,6642synth->eventhandler->mixer,6643param);6644}66456646/**6647* Get chorus voice number (delay line count) value of all fx groups.6648* @param synth FluidSynth instance6649* @return Chorus voice count6650* @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead.6651*/6652int6653fluid_synth_get_chorus_nr(fluid_synth_t *synth)6654{6655double nr = 0.0;6656fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr);6657return (int)nr;6658}66596660/**6661* Get chorus level of all fx groups.6662* @param synth FluidSynth instance6663* @return Chorus level value6664* @deprecated Use fluid_synth_get_chorus_group_level() in new code instead.6665*/6666double6667fluid_synth_get_chorus_level(fluid_synth_t *synth)6668{6669double level = 0.0;6670fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level);6671return level;6672}66736674/**6675* Get chorus speed in Hz of all fx groups.6676* @param synth FluidSynth instance6677* @return Chorus speed in Hz6678* @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead.6679*/6680double6681fluid_synth_get_chorus_speed(fluid_synth_t *synth)6682{6683double speed = 0.0;6684fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed);6685return speed;6686}66876688/**6689* Get chorus depth of all fx groups.6690* @param synth FluidSynth instance6691* @return Chorus depth6692* @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead.6693*/6694double6695fluid_synth_get_chorus_depth(fluid_synth_t *synth)6696{6697double depth = 0.0;6698fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth);6699return depth;6700}67016702/**6703* Get chorus waveform type of all fx groups.6704* @param synth FluidSynth instance6705* @return Chorus waveform type (#fluid_chorus_mod)6706* @deprecated Use fluid_synth_get_chorus_group_type() in new code instead.6707*/6708int6709fluid_synth_get_chorus_type(fluid_synth_t *synth)6710{6711double type = 0.0;6712fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type);6713return (int)type;6714}67156716/**6717* Get chorus count nr of one or all fx groups.6718* @param synth FluidSynth instance.6719* @param fx_group Index of the fx group from which to fetch the chorus voice count.6720* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6721* parameter common to all fx groups is fetched.6722* @param nr valid pointer on value to return.6723* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6724*/6725int6726fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr)6727{6728double num_nr = 0.0;6729int status;6730status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr);6731*nr = (int)num_nr;6732return status;6733}67346735/**6736* Get chorus output level of one or all fx groups.6737* @param synth FluidSynth instance.6738* @param fx_group Index of the fx group from which chorus level to fetch.6739* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6740* parameter common to all fx groups is fetched.6741* @param level valid pointer on value to return.6742* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6743*/6744int6745fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level)6746{6747return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level);6748}67496750/**6751* Get chorus waveform lfo speed of one or all fx groups.6752* @param synth FluidSynth instance.6753* @param fx_group Index of the fx group from which lfo speed to fetch.6754* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6755* parameter common to all fx groups is fetched.6756* @param speed valid pointer on value to return.6757* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6758*/6759int6760fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed)6761{6762return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed);6763}67646765/**6766* Get chorus lfo depth of one or all fx groups.6767* @param synth FluidSynth instance6768* @param fx_group Index of the fx group from which lfo depth to fetch.6769* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6770* parameter common to all fx groups is fetched.6771* @param depth_ms valid pointer on value to return.6772* @return #FLUID_OK on success, #FLUID_FAILED otherwise6773*/6774int6775fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms)6776{6777return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms);6778}67796780/**6781* Get chorus waveform type of one or all fx groups.6782* @param synth FluidSynth instance6783* @param fx_group Index of the fx group from which to fetch the waveform type.6784* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6785* parameter common to all fx groups is fetched.6786* @param type valid pointer on waveform type to return (#fluid_chorus_mod)6787* @return #FLUID_OK on success, #FLUID_FAILED otherwise6788*/6789int6790fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type)6791{6792double num_type = 0.0;6793int status;6794status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type);6795*type = (int)num_type;6796return status;6797}67986799/**6800* Get chorus parameter value of one or all fx groups.6801* @param synth FluidSynth instance6802* @param fx_group index of the fx group6803* @param enum indicating the parameter to get.6804* FLUID_CHORUS_NR, chorus voice count.6805* FLUID_CHORUS_LEVEL, chorus level.6806* FLUID_CHORUS_SPEED, chorus speed.6807* FLUID_CHORUS_DEPTH, chorus depth.6808* FLUID_CHORUS_TYPE, chorus waveform type.6809* @param value pointer on the value to return.6810* @return FLUID_OK if success, FLUID_FAILED otherwise.6811*/6812static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group,6813int param, double *value)6814{6815fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6816fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED);6817fluid_return_val_if_fail(value != NULL, FLUID_FAILED);6818fluid_synth_api_enter(synth);68196820if(fx_group < -1 || fx_group >= synth->effects_groups)6821{6822FLUID_API_RETURN(FLUID_FAILED);6823}68246825if (fx_group < 0)6826{6827/* return chorus param common to all fx groups */6828*value = synth->chorus_param[param];6829}6830else6831{6832/* return chorus param of fx group at index group */6833*value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer,6834fx_group, param);6835}68366837FLUID_API_RETURN(FLUID_OK);6838}68396840/*6841* If the same note is hit twice on the same channel, then the older6842* voice process is advanced to the release stage. Using a mechanical6843* MIDI controller, the only way this can happen is when the sustain6844* pedal is held. In this case the behaviour implemented here is6845* natural for many instruments. Note: One noteon event can trigger6846* several voice processes, for example a stereo sample. Don't6847* release those...6848*/6849void6850fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan,6851int key)6852{6853int i;6854fluid_voice_t *voice;68556856/* storeid is a parameter for fluid_voice_init() */6857synth->storeid = synth->noteid++;68586859/* for "monophonic playing" key is the previous sustained note6860if it exists (0 to 127) or INVALID_NOTE otherwise */6861if(key == INVALID_NOTE)6862{6863return;6864}68656866for(i = 0; i < synth->polyphony; i++)6867{6868voice = synth->voice[i];68696870if(fluid_voice_is_playing(voice)6871&& (fluid_voice_get_channel(voice) == chan)6872&& (fluid_voice_get_key(voice) == key)6873&& (fluid_voice_get_id(voice) != synth->noteid))6874{6875enum fluid_midi_channel_type type = synth->channel[chan]->channel_type;68766877/* Id of voices that was sustained by sostenuto */6878if(fluid_voice_is_sostenuto(voice))6879{6880synth->storeid = fluid_voice_get_id(voice);6881}68826883switch(type)6884{6885case CHANNEL_TYPE_DRUM:6886/* release the voice, this should make riding hi-hats or snares sound more6887* realistic (Discussion #1196) */6888fluid_voice_off(voice);6889break;6890case CHANNEL_TYPE_MELODIC:6891/* Force the voice into release stage except if pedaling (sostenuto or sustain) is active.6892* This gives a more realistic sound to pianos and possibly other instruments (see PR #905). */6893fluid_voice_noteoff(voice);6894break;6895default:6896FLUID_LOG(FLUID_ERR, "This should never happen: unknown channel type %d", (int)type);6897break;6898}6899}6900}6901}69026903/**6904* Set synthesis interpolation method on one or all MIDI channels.6905* @param synth FluidSynth instance6906* @param chan MIDI channel to set interpolation method on or -1 for all channels6907* @param interp_method Interpolation method (#fluid_interp)6908* @return #FLUID_OK on success, #FLUID_FAILED otherwise6909*/6910int6911fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method)6912{6913int i;69146915fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6916fluid_synth_api_enter(synth);69176918if(chan < -1 || chan >= synth->midi_channels)6919{6920FLUID_API_RETURN(FLUID_FAILED);6921}69226923if(synth->channel[0] == NULL)6924{6925FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!");6926FLUID_API_RETURN(FLUID_FAILED);6927}69286929for(i = 0; i < synth->midi_channels; i++)6930{6931if(chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan)6932{6933fluid_channel_set_interp_method(synth->channel[i], interp_method);6934}6935}69366937FLUID_API_RETURN(FLUID_OK);6938};69396940/**6941* Get the total count of MIDI channels.6942* @param synth FluidSynth instance6943* @return Count of MIDI channels6944*/6945int6946fluid_synth_count_midi_channels(fluid_synth_t *synth)6947{6948int result;6949fluid_return_val_if_fail(synth != NULL, 0);6950fluid_synth_api_enter(synth);69516952result = synth->midi_channels;6953FLUID_API_RETURN(result);6954}69556956/**6957* Get the total count of audio channels.6958* @param synth FluidSynth instance6959* @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc)6960*/6961int6962fluid_synth_count_audio_channels(fluid_synth_t *synth)6963{6964int result;6965fluid_return_val_if_fail(synth != NULL, 0);6966fluid_synth_api_enter(synth);69676968result = synth->audio_channels;6969FLUID_API_RETURN(result);6970}69716972/**6973* Get the total number of allocated audio channels. Usually identical to the6974* number of audio channels. Can be employed by LADSPA effects subsystem.6975*6976* @param synth FluidSynth instance6977* @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc)6978*/6979int6980fluid_synth_count_audio_groups(fluid_synth_t *synth)6981{6982int result;6983fluid_return_val_if_fail(synth != NULL, 0);6984fluid_synth_api_enter(synth);69856986result = synth->audio_groups;6987FLUID_API_RETURN(result);6988}69896990/**6991* Get the total number of allocated effects channels.6992* @param synth FluidSynth instance6993* @return Count of allocated effects channels6994*/6995int6996fluid_synth_count_effects_channels(fluid_synth_t *synth)6997{6998int result;6999fluid_return_val_if_fail(synth != NULL, 0);7000fluid_synth_api_enter(synth);70017002result = synth->effects_channels;7003FLUID_API_RETURN(result);7004}70057006/**7007* Get the total number of allocated effects units.7008*7009* This is the same number as initially provided by the setting \setting{synth_effects-groups}.7010* @param synth FluidSynth instance7011* @return Count of allocated effects units7012*/7013int7014fluid_synth_count_effects_groups(fluid_synth_t *synth)7015{7016int result;7017fluid_return_val_if_fail(synth != NULL, 0);7018fluid_synth_api_enter(synth);70197020result = synth->effects_groups;7021FLUID_API_RETURN(result);7022}70237024/**7025* Get the synth CPU load value.7026* @param synth FluidSynth instance7027* @return Estimated CPU load value in percent (0-100)7028*/7029double7030fluid_synth_get_cpu_load(fluid_synth_t *synth)7031{7032fluid_return_val_if_fail(synth != NULL, 0);7033return fluid_atomic_float_get(&synth->cpu_load);7034}70357036/* Get tuning for a given bank:program */7037static fluid_tuning_t *7038fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog)7039{70407041if((synth->tuning == NULL) ||7042(synth->tuning[bank] == NULL) ||7043(synth->tuning[bank][prog] == NULL))7044{7045return NULL;7046}70477048return synth->tuning[bank][prog];7049}70507051/* Replace tuning on a given bank:program (need not already exist).7052* Synth mutex should already be locked by caller. */7053static int7054fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, fluid_tuning_t *tuning,7055int bank, int prog, int apply)7056{7057fluid_tuning_t *old_tuning;70587059if(synth->tuning == NULL)7060{7061synth->tuning = FLUID_ARRAY(fluid_tuning_t **, 128);70627063if(synth->tuning == NULL)7064{7065FLUID_LOG(FLUID_PANIC, "Out of memory");7066return FLUID_FAILED;7067}70687069FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t **));7070}70717072if(synth->tuning[bank] == NULL)7073{7074synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t *, 128);70757076if(synth->tuning[bank] == NULL)7077{7078FLUID_LOG(FLUID_PANIC, "Out of memory");7079return FLUID_FAILED;7080}70817082FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t *));7083}70847085old_tuning = synth->tuning[bank][prog];7086synth->tuning[bank][prog] = tuning;70877088if(old_tuning)7089{7090if(!fluid_tuning_unref(old_tuning, 1)) /* -- unref old tuning */7091{7092/* Replace old tuning if present */7093fluid_synth_replace_tuning_LOCAL(synth, old_tuning, tuning, apply, FALSE);7094}7095}70967097return FLUID_OK;7098}70997100/* Replace a tuning with a new one in all MIDI channels. new_tuning can be7101* NULL, in which case channels are reset to default equal tempered scale. */7102static void7103fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, fluid_tuning_t *old_tuning,7104fluid_tuning_t *new_tuning, int apply, int unref_new)7105{7106fluid_channel_t *channel;7107int old_tuning_unref = 0;7108int i;71097110for(i = 0; i < synth->midi_channels; i++)7111{7112channel = synth->channel[i];71137114if(fluid_channel_get_tuning(channel) == old_tuning)7115{7116old_tuning_unref++;71177118if(new_tuning)7119{7120fluid_tuning_ref(new_tuning); /* ++ ref new tuning for channel */7121}71227123fluid_channel_set_tuning(channel, new_tuning);71247125if(apply)7126{7127fluid_synth_update_voice_tuning_LOCAL(synth, channel);7128}7129}7130}71317132/* Send unref old tuning event if any unrefs */7133if(old_tuning && old_tuning_unref)7134{7135fluid_tuning_unref(old_tuning, old_tuning_unref);7136}71377138if(!unref_new || !new_tuning)7139{7140return;7141}71427143fluid_tuning_unref(new_tuning, 1);7144}71457146/* Update voice tunings in realtime */7147static void7148fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, fluid_channel_t *channel)7149{7150fluid_voice_t *voice;7151int i;71527153for(i = 0; i < synth->polyphony; i++)7154{7155voice = synth->voice[i];71567157if(fluid_voice_is_on(voice) && (voice->channel == channel))7158{7159fluid_voice_calculate_gen_pitch(voice);7160fluid_voice_update_param(voice, GEN_PITCH);7161}7162}7163}71647165/**7166* Set the tuning of the entire MIDI note scale.7167* @param synth FluidSynth instance7168* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7169* @param prog Tuning preset number (0-127), not related to MIDI instrument program7170* @param name Label name for this tuning7171* @param pitch Array of pitch values (length of 128, each value is number of7172* cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc).7173* Pass NULL to create a equal tempered (normal) scale.7174* @param apply TRUE to apply new tuning in realtime to existing notes which7175* are using the replaced tuning (if any), FALSE otherwise7176* @return #FLUID_OK on success, #FLUID_FAILED otherwise7177* @since 1.1.07178*/7179int7180fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog,7181const char *name, const double *pitch, int apply)7182{7183fluid_tuning_t *tuning;7184int retval = FLUID_OK;71857186fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7187fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7188fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7189fluid_return_val_if_fail(name != NULL, FLUID_FAILED);71907191fluid_synth_api_enter(synth);71927193tuning = new_fluid_tuning(name, bank, prog);71947195if(tuning)7196{7197if(pitch)7198{7199fluid_tuning_set_all(tuning, pitch);7200}72017202retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply);72037204if(retval == FLUID_FAILED)7205{7206fluid_tuning_unref(tuning, 1);7207}7208}7209else7210{7211retval = FLUID_FAILED;7212}72137214FLUID_API_RETURN(retval);7215}72167217/**7218* Activate an octave tuning on every octave in the MIDI note scale.7219* @param synth FluidSynth instance7220* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7221* @param prog Tuning preset number (0-127), not related to MIDI instrument program7222* @param name Label name for this tuning7223* @param pitch Array of pitch values (length of 12 for each note of an octave7224* starting at note C, values are number of offset cents to add to the normal7225* tuning amount)7226* @param apply TRUE to apply new tuning in realtime to existing notes which7227* are using the replaced tuning (if any), FALSE otherwise7228* @return #FLUID_OK on success, #FLUID_FAILED otherwise7229* @since 1.1.07230*/7231int7232fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog,7233const char *name, const double *pitch, int apply)7234{7235fluid_tuning_t *tuning;7236int retval = FLUID_OK;72377238fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7239fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7240fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7241fluid_return_val_if_fail(name != NULL, FLUID_FAILED);7242fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED);72437244fluid_synth_api_enter(synth);7245tuning = new_fluid_tuning(name, bank, prog);72467247if(tuning)7248{7249fluid_tuning_set_octave(tuning, pitch);7250retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply);72517252if(retval == FLUID_FAILED)7253{7254fluid_tuning_unref(tuning, 1);7255}7256}7257else7258{7259retval = FLUID_FAILED;7260}72617262FLUID_API_RETURN(retval);7263}72647265/**7266* Set tuning values for one or more MIDI notes for an existing tuning.7267* @param synth FluidSynth instance7268* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7269* @param prog Tuning preset number (0-127), not related to MIDI instrument program7270* @param len Number of MIDI notes to assign7271* @param key Array of MIDI key numbers (length of 'len', values 0-127)7272* @param pitch Array of pitch values (length of 'len', values are number of7273* cents from MIDI note 0)7274* @param apply TRUE to apply tuning change in realtime to existing notes using7275* the specified tuning, FALSE otherwise7276* @return #FLUID_OK on success, #FLUID_FAILED otherwise7277*7278* @note Prior to version 1.1.0 it was an error to specify a tuning that didn't7279* already exist. Starting with 1.1.0, the default equal tempered scale will be7280* used as a basis, if no tuning exists for the given bank and prog.7281*/7282int7283fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog,7284int len, const int *key, const double *pitch, int apply)7285{7286fluid_tuning_t *old_tuning, *new_tuning;7287int retval = FLUID_OK;7288int i;72897290fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7291fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7292fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7293fluid_return_val_if_fail(len > 0, FLUID_FAILED);7294fluid_return_val_if_fail(key != NULL, FLUID_FAILED);7295fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED);72967297fluid_synth_api_enter(synth);72987299old_tuning = fluid_synth_get_tuning(synth, bank, prog);73007301if(old_tuning)7302{7303new_tuning = fluid_tuning_duplicate(old_tuning);7304}7305else7306{7307new_tuning = new_fluid_tuning("Unnamed", bank, prog);7308}73097310if(new_tuning)7311{7312for(i = 0; i < len; i++)7313{7314fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]);7315}73167317retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply);73187319if(retval == FLUID_FAILED)7320{7321fluid_tuning_unref(new_tuning, 1);7322}7323}7324else7325{7326retval = FLUID_FAILED;7327}73287329FLUID_API_RETURN(retval);7330}73317332/**7333* Activate a tuning scale on a MIDI channel.7334* @param synth FluidSynth instance7335* @param chan MIDI channel number (0 to MIDI channel count - 1)7336* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7337* @param prog Tuning preset number (0-127), not related to MIDI instrument program7338* @param apply TRUE to apply tuning change to active notes, FALSE otherwise7339* @return #FLUID_OK on success, #FLUID_FAILED otherwise7340* @since 1.1.07341*7342* @note A default equal tempered scale will be created, if no tuning exists7343* on the given bank and prog.7344*/7345int7346fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog,7347int apply)7348{7349fluid_tuning_t *tuning;7350int retval = FLUID_OK;73517352//fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);7353//fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);7354fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7355fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);73567357FLUID_API_ENTRY_CHAN(FLUID_FAILED);73587359tuning = fluid_synth_get_tuning(synth, bank, prog);73607361/* If no tuning exists, create a new default tuning. We do this, so that7362* it can be replaced later, if any changes are made. */7363if(!tuning)7364{7365tuning = new_fluid_tuning("Unnamed", bank, prog);73667367if(tuning)7368{7369fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE);7370}7371}73727373if(tuning)7374{7375fluid_tuning_ref(tuning); /* ++ ref for outside of lock */7376}73777378if(!tuning)7379{7380FLUID_API_RETURN(FLUID_FAILED);7381}73827383fluid_tuning_ref(tuning); /* ++ ref new tuning for following function */7384retval = fluid_synth_set_tuning_LOCAL(synth, chan, tuning, apply);73857386fluid_tuning_unref(tuning, 1); /* -- unref for outside of lock */73877388FLUID_API_RETURN(retval);7389}73907391/* Local synthesis thread set tuning function (takes over tuning reference) */7392static int7393fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan,7394fluid_tuning_t *tuning, int apply)7395{7396fluid_tuning_t *old_tuning;7397fluid_channel_t *channel;73987399channel = synth->channel[chan];74007401old_tuning = fluid_channel_get_tuning(channel);7402fluid_channel_set_tuning(channel, tuning); /* !! Takes over callers reference */74037404if(apply)7405{7406fluid_synth_update_voice_tuning_LOCAL(synth, channel);7407}74087409/* Send unref old tuning event */7410if(old_tuning)7411{7412fluid_tuning_unref(old_tuning, 1);7413}741474157416return FLUID_OK;7417}74187419/**7420* Clear tuning scale on a MIDI channel (use default equal tempered scale).7421* @param synth FluidSynth instance7422* @param chan MIDI channel number (0 to MIDI channel count - 1)7423* @param apply TRUE to apply tuning change to active notes, FALSE otherwise7424* @return #FLUID_OK on success, #FLUID_FAILED otherwise7425* @since 1.1.07426*/7427int7428fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply)7429{7430int retval = FLUID_OK;74317432FLUID_API_ENTRY_CHAN(FLUID_FAILED);74337434retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply);74357436FLUID_API_RETURN(retval);7437}74387439/**7440* Start tuning iteration.7441* @param synth FluidSynth instance7442*/7443void7444fluid_synth_tuning_iteration_start(fluid_synth_t *synth)7445{7446fluid_return_if_fail(synth != NULL);7447fluid_synth_api_enter(synth);7448fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER(0));7449fluid_synth_api_exit(synth);7450}74517452/**7453* Advance to next tuning.7454* @param synth FluidSynth instance7455* @param bank Location to store MIDI bank number of next tuning scale7456* @param prog Location to store MIDI program number of next tuning scale7457* @return 1 if tuning iteration advanced, 0 if no more tunings7458*/7459int7460fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog)7461{7462void *pval;7463int b = 0, p = 0;74647465fluid_return_val_if_fail(synth != NULL, 0);7466fluid_return_val_if_fail(bank != NULL, 0);7467fluid_return_val_if_fail(prog != NULL, 0);7468fluid_synth_api_enter(synth);74697470/* Current tuning iteration stored as: bank << 8 | program */7471pval = fluid_private_get(synth->tuning_iter);7472p = FLUID_POINTER_TO_INT(pval);7473b = (p >> 8) & 0xFF;7474p &= 0xFF;74757476if(!synth->tuning)7477{7478FLUID_API_RETURN(0);7479}74807481for(; b < 128; b++, p = 0)7482{7483if(synth->tuning[b] == NULL)7484{7485continue;7486}74877488for(; p < 128; p++)7489{7490if(synth->tuning[b][p] == NULL)7491{7492continue;7493}74947495*bank = b;7496*prog = p;74977498if(p < 127)7499{7500fluid_private_set(synth->tuning_iter,7501FLUID_INT_TO_POINTER(b << 8 | (p + 1)));7502}7503else7504{7505fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8));7506}75077508FLUID_API_RETURN(1);7509}7510}75117512FLUID_API_RETURN(0);7513}75147515/**7516* Get the entire note tuning for a given MIDI bank and program.7517* @param synth FluidSynth instance7518* @param bank MIDI bank number of tuning7519* @param prog MIDI program number of tuning7520* @param name Location to store tuning name or NULL to ignore7521* @param len Maximum number of chars to store to 'name' (including NULL byte)7522* @param pitch Array to store tuning scale to or NULL to ignore (len of 128)7523* @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise7524*/7525int7526fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog,7527char *name, int len, double *pitch)7528{7529fluid_tuning_t *tuning;75307531fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7532fluid_synth_api_enter(synth);75337534tuning = fluid_synth_get_tuning(synth, bank, prog);75357536if(tuning)7537{7538if(name)7539{7540FLUID_SNPRINTF(name, len - 1, "%s", fluid_tuning_get_name(tuning));7541name[len - 1] = 0; /* make sure the string is null terminated */7542}75437544if(pitch)7545{7546FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double));7547}7548}75497550FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED);7551}75527553/**7554* Get settings assigned to a synth.7555* @param synth FluidSynth instance7556* @return FluidSynth settings which are assigned to the synth7557*/7558fluid_settings_t *7559fluid_synth_get_settings(fluid_synth_t *synth)7560{7561fluid_return_val_if_fail(synth != NULL, NULL);75627563return synth->settings;7564}75657566/**7567* Apply an offset to a SoundFont generator on a MIDI channel.7568*7569* This function allows to set an offset for the specified destination generator in real-time.7570* The offset will be applied immediately to all voices that are currently and subsequently playing7571* on the given MIDI channel. This functionality works equivalent to using NRPN MIDI messages to7572* manipulate synthesis parameters. See SoundFont spec, paragraph 8.1.3, for details on SoundFont7573* generator parameters and valid ranges, as well as paragraph 9.6 for details on NRPN messages.7574* @param synth FluidSynth instance7575* @param chan MIDI channel number (0 to MIDI channel count - 1)7576* @param param SoundFont generator ID (#fluid_gen_type)7577* @param value Offset value (in native units of the generator) to assign to the MIDI channel7578* @return #FLUID_OK on success, #FLUID_FAILED otherwise7579*/7580int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value)7581{7582fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);7583FLUID_API_ENTRY_CHAN(FLUID_FAILED);75847585fluid_synth_set_gen_LOCAL(synth, chan, param, value);75867587FLUID_API_RETURN(FLUID_OK);7588}75897590/* Synthesis thread local set gen function */7591static void7592fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value)7593{7594fluid_voice_t *voice;7595int i;75967597fluid_channel_set_gen(synth->channel[chan], param, value);75987599for(i = 0; i < synth->polyphony; i++)7600{7601voice = synth->voice[i];76027603if(fluid_voice_get_channel(voice) == chan)7604{7605fluid_voice_set_param(voice, param, value);7606}7607}7608}76097610// The "SB AWE32 Developer's Information Pack" provides a lookup table for the filter resonance.7611// Instead of a single Q value, a high and low Q value is given. This suggests a variable-Q filter design, which is7612// incompatible to fluidsynth's IIR filter. Therefore we need to somehow derive a single Q value.7613// Options are:7614// * mean7615// * geometric distance (sqrt(q_lo * q_hi))7616// * either q_lo or q_hi7617// * linear interpolation between low and high fc7618// * log interpolation between low and high fc7619static fluid_real_t7620calc_awe32_filter_q(int data, fluid_real_t* fc)7621{7622typedef struct7623{7624fluid_real_t fc_lo;7625fluid_real_t fc_hi;7626fluid_real_t q_lo;7627fluid_real_t q_hi;7628fluid_real_t dc_atten;7629} awe32_q;76307631// Q in dB7632static const awe32_q awe32_q_table[] =7633{7634{92, 22000, 5.f, 0.f, -0.0f }, /* coef 0 */7635{93, 8500, 6.f, 0.5f, -0.5f }, /* coef 1 */7636{94, 8300, 8.f, 1.f, -1.2f }, /* coef 2 */7637{95, 8200, 10.f, 2.f, -1.8f }, /* coef 3 */7638{96, 8100, 11.f, 3.f, -2.5f }, /* coef 4 */7639{97, 8000, 13.f, 4.f, -3.3f }, /* coef 5 */7640{98, 7900, 14.f, 5.f, -4.1f }, /* coef 6 */7641{99, 7800, 16.f, 6.f, -5.5f}, /* coef 7 */7642{100, 7700, 17.f, 7.f, -6.0f }, /* coef 8 */7643{100, 7500, 19.f, 9.f, -6.6f }, /* coef 9 */7644{100, 7400, 20.f, 10.f, -7.2f }, /* coef 10 */7645{100, 7300, 22.f, 11.f, -7.9f }, /* coef 11 */7646{100, 7200, 23.f, 13.f, -8.5f }, /* coef 12 */7647{100, 7100, 25.f, 15.f, -9.3f }, /* coef 13 */7648{100, 7100, 26.f, 16.f, -10.1f },/* coef 14 */7649{100, 7000, 28.f, 18.f, -11.0f}, /* coef 15 */7650};76517652const awe32_q* tab;7653fluid_real_t alpha;76547655fluid_clip(data, 0, 127);7656data /= 8;7657tab = &awe32_q_table[data];76587659fluid_clip(*fc, tab->fc_lo, tab->fc_hi);76607661alpha = (*fc - tab->fc_lo) / (tab->fc_hi - tab->fc_lo);76627663// linearly interpolate between high and low Q7664return 10 * /* cB */ (tab->q_lo * (1.0f - alpha) + tab->q_hi * alpha);76657666// alternatively: log interpolation7667// return 10 * /* cB */ FLUID_POW(tab->q_hi, alpha) * FLUID_POW(tab->q_lo, 1.0f - alpha);7668}76697670/**7671* This implementation is based on "Frequently Asked Questions for SB AWE32" http://archive.gamedev.net/archive/reference/articles/article445.html7672* as well as on the "SB AWE32 Developer's Information Pack" https://github.com/user-attachments/files/15757220/adip301.pdf7673*7674* @param gen the AWE32 effect or generator to manipulate7675* @param data the composed value of DATA_MSB and DATA_LSB7676*/7677static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data, int data_lsb)7678{7679static const enum fluid_gen_type awe32_to_sf2_gen[] =7680{7681// assuming LFO1 maps to MODLFO and LFO2 maps to VIBLFO7682// observe how nicely most of the AWE32 generators here match up with the order of SF2 generators in fluid_gen_type7683GEN_MODLFODELAY, /**< Modulation LFO delay */7684GEN_MODLFOFREQ, /**< Modulation LFO frequency */7685GEN_VIBLFODELAY, /**< Vibrato LFO delay */7686GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */7687GEN_MODENVDELAY, /**< Modulation envelope delay */7688GEN_MODENVATTACK, /**< Modulation envelope attack */7689GEN_MODENVHOLD, /**< Modulation envelope hold */7690GEN_MODENVDECAY, /**< Modulation envelope decay */7691GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */7692GEN_MODENVRELEASE, /**< Modulation envelope release */7693GEN_VOLENVDELAY, /**< Volume envelope delay */7694GEN_VOLENVATTACK, /**< Volume envelope attack */7695GEN_VOLENVHOLD, /**< Volume envelope hold */7696GEN_VOLENVDECAY, /**< Volume envelope decay */7697GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */7698GEN_VOLENVRELEASE, /**< Volume envelope release */7699GEN_PITCH, /**< Initial Pitch */7700GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */7701GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */7702GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */7703GEN_MODLFOTOVOL, /**< Modulation LFO to volume */7704GEN_FILTERFC, /**< Filter cutoff */7705GEN_FILTERQ, /**< Filter Q */7706GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */7707GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */7708GEN_CHORUSSEND, /**< Chorus send amount */7709GEN_REVERBSEND, /**< Reverb send amount */7710};77117712enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen];7713int is_realtime = FALSE, i, coef;7714fluid_real_t converted_sf2_generator_value, q;77157716// The AWE32 NRPN docs say that a value of 8192 is considered to be the middle, i.e. zero.7717// However, it looks like for those generators which work in range [0,127], the AWE32 only inspects the DATA_LSB, i.e. and not doing this subtraction. Found while investigating Uplift.mid.7718data -= 8192;77197720switch(sf2_gen)7721{7722case GEN_MODLFODELAY:7723case GEN_VIBLFODELAY:7724case GEN_MODENVDELAY:7725case GEN_VOLENVDELAY:7726fluid_clip(data, 0, 5900);7727converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0));7728break;77297730case GEN_MODLFOFREQ:7731case GEN_VIBLFOFREQ:7732fluid_clip(data_lsb, 0, 127);7733converted_sf2_generator_value = fluid_hz2ct(data_lsb * (fluid_real_t)0.084 /* Hz */);7734is_realtime = TRUE;7735break;77367737case GEN_MODENVATTACK:7738case GEN_VOLENVATTACK:7739fluid_clip(data, 0, 5940);7740converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0));7741break;77427743case GEN_MODENVHOLD:7744case GEN_VOLENVHOLD:7745fluid_clip(data, 0, 8191);7746converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0));7747break;77487749case GEN_MODENVDECAY:7750case GEN_MODENVRELEASE:7751case GEN_VOLENVDECAY:7752case GEN_VOLENVRELEASE:7753fluid_clip(data, 0, 5940);7754converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0));7755break;77567757case GEN_MODENVSUSTAIN:7758case GEN_VOLENVSUSTAIN:7759fluid_clip(data_lsb, 0, 127);7760converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.75 /* dB */ * 10) /* cB */;7761break;77627763case GEN_PITCH:7764converted_sf2_generator_value = data + 8192;7765// This has the side effect of manipulating the modulation state of the channel's pitchwheel, but7766// I'll buy it, since pitch bend is not a regular SF2 generator and we do a bit of magic there to7767// make it work7768fluid_synth_pitch_bend(synth, chan, converted_sf2_generator_value);7769return;77707771case GEN_MODLFOTOPITCH:7772case GEN_VIBLFOTOPITCH:7773is_realtime = TRUE;7774/* fallthrough */7775case GEN_MODENVTOPITCH:7776fluid_clip(data, -127, 127);7777converted_sf2_generator_value = data * (fluid_real_t)9.375 /* cents */;7778break;77797780case GEN_MODLFOTOVOL:7781fluid_clip(data_lsb, 0, 127);7782converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.1875 /* dB */ * 10.0) /* cB */;7783is_realtime = TRUE;7784break;77857786case GEN_FILTERFC:7787fluid_clip(data_lsb, 0, 127);7788// Yes, DO NOT use data here, Uplift.mid doesn't set MSB=64, therefore we would always get a negative value after subtracting 8192.7789// Since Uplift.mid sounds fine on hardware though, it seems like AWE32 only inspects DATA_LSB in this case.7790// conversion continues below!7791converted_sf2_generator_value = (data_lsb * 62 /* Hz */);7792FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value);7793is_realtime = TRUE;7794break;77957796case GEN_FILTERQ:7797FLUID_LOG(FLUID_DBG, "AWE32 IIR Q Tab: %d",data_lsb);7798synth->channel[chan]->awe32_filter_coeff = data_lsb;7799return;78007801case GEN_MODLFOTOFILTERFC:7802fluid_clip(data, -64, 63);7803converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */;7804FLUID_LOG(FLUID_DBG, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value);7805is_realtime = TRUE;7806// not supported, as this modulates the "phase" rather than the filters cutoff frequency7807return;78087809case GEN_MODENVTOFILTERFC:7810fluid_clip(data, -127, 127);7811converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */;7812FLUID_LOG(FLUID_DBG, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value);7813// not supported, as this modulates the "phase" rather than the filters cutoff frequency7814return;78157816case GEN_REVERBSEND:7817fluid_clip(data, 0, 255);7818/* transform the input value */7819converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod.flags1, 256);7820FLUID_LOG(FLUID_DBG, "AWE32 Reverb: %f", converted_sf2_generator_value);7821converted_sf2_generator_value*= fluid_mod_get_amount(&default_reverb_mod);7822break;78237824case GEN_CHORUSSEND:7825fluid_clip(data, 0, 255);7826/* transform the input value */7827converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod.flags1, 256);7828FLUID_LOG(FLUID_DBG, "AWE32 Chorus: %f", converted_sf2_generator_value);7829converted_sf2_generator_value*= fluid_mod_get_amount(&default_chorus_mod);7830break;78317832default:7833// should not happen7834FLUID_LOG(FLUID_WARN, "AWE32 NPRN %d conversion not implemented", gen);7835return;7836}78377838coef = synth->channel[chan]->awe32_filter_coeff;7839if(sf2_gen == GEN_FILTERFC)7840{7841// The cutoff at fc seems to be very steep for SoundBlaster! hardware. Listening tests have shown that lowering the cutoff frequency by 1000Hz gives a closer signal to the SB! hardware filter...7842converted_sf2_generator_value -= 1000;7843q = calc_awe32_filter_q(coef, &converted_sf2_generator_value);7844FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value);7845FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q);78467847converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */);78487849// Safe the "true initial Q"7850fluid_channel_set_override_gen_default(synth->channel[chan], GEN_FILTERQ, q);7851}78527853fluid_channel_set_override_gen_default(synth->channel[chan], sf2_gen, converted_sf2_generator_value);78547855for (i = 0; is_realtime && i < synth->polyphony; i++)7856{7857fluid_voice_t* voice = synth->voice[i];78587859if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan)7860{7861// sets the adjusted generator7862fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value);7863fluid_voice_update_param(voice, sf2_gen);78647865FLUID_LOG(FLUID_DBG, "AWE32 Realtime: adjusting voice id %d, generator %d, chan %d", fluid_voice_get_id(voice), sf2_gen, chan);7866if(sf2_gen == GEN_FILTERFC)7867{7868// also sets the calculated Q7869fluid_voice_gen_set(voice, GEN_FILTERQ, q);7870fluid_voice_update_param(voice, GEN_FILTERQ);7871}7872}7873}7874}78757876/**7877* Retrieve the generator NRPN offset assigned to a MIDI channel.7878*7879* The value returned is in native units of the generator. By default, the offset is zero.7880* @param synth FluidSynth instance7881* @param chan MIDI channel number (0 to MIDI channel count - 1)7882* @param param SoundFont generator ID (#fluid_gen_type)7883* @return Current NRPN generator offset value assigned to the MIDI channel7884*/7885float7886fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param)7887{7888float result;7889fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);7890FLUID_API_ENTRY_CHAN(FLUID_FAILED);78917892result = fluid_channel_get_gen(synth->channel[chan], param);7893FLUID_API_RETURN(result);7894}78957896/**7897* Handle MIDI event from MIDI router, used as a callback function.7898* @param data FluidSynth instance7899* @param event MIDI event to handle7900* @return #FLUID_OK on success, #FLUID_FAILED otherwise7901*/7902int7903fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event)7904{7905fluid_synth_t *synth = (fluid_synth_t *) data;7906int type = fluid_midi_event_get_type(event);7907int chan = fluid_midi_event_get_channel(event);79087909switch(type)7910{7911case NOTE_ON:7912return fluid_synth_noteon(synth, chan,7913fluid_midi_event_get_key(event),7914fluid_midi_event_get_velocity(event));79157916case NOTE_OFF:7917return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event));79187919case CONTROL_CHANGE:7920return fluid_synth_cc(synth, chan,7921fluid_midi_event_get_control(event),7922fluid_midi_event_get_value(event));79237924case PROGRAM_CHANGE:7925return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event));79267927case CHANNEL_PRESSURE:7928return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event));79297930case KEY_PRESSURE:7931return fluid_synth_key_pressure(synth, chan,7932fluid_midi_event_get_key(event),7933fluid_midi_event_get_value(event));79347935case PITCH_BEND:7936return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));79377938case MIDI_SYSTEM_RESET:7939return fluid_synth_system_reset(synth);79407941case MIDI_SYSEX:7942return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE);79437944case MIDI_TEXT:7945case MIDI_LYRIC:7946case MIDI_SET_TEMPO:7947return FLUID_OK;7948}79497950return FLUID_FAILED;7951}79527953/**7954* Create and start voices using an arbitrary preset and a MIDI note on event.7955*7956* Using this function is only supported when the setting @c synth.dynamic-sample-loading is false!7957* @param synth FluidSynth instance7958* @param id Voice group ID to use (can be used with fluid_synth_stop()).7959* @param preset Preset to synthesize7960* @param audio_chan Unused currently, set to 07961* @param chan MIDI channel number (0 to MIDI channel count - 1)7962* @param key MIDI note number (0-127)7963* @param vel MIDI velocity number (1-127)7964* @return #FLUID_OK on success, #FLUID_FAILED otherwise7965*7966* @note Should only be called from within synthesis thread, which includes7967* SoundFont loader preset noteon method.7968*/7969int7970fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset,7971int audio_chan, int chan, int key, int vel)7972{7973int result, dynamic_samples;7974fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);7975fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);7976fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED);7977FLUID_API_ENTRY_CHAN(FLUID_FAILED);79787979fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples);7980if(dynamic_samples)7981{7982// The preset might not be currently used, thus its sample data may not be loaded.7983// This guard is to avoid a NULL deref in rvoice_write().7984FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported.");7985// Although we would be able to select the preset (and load it's samples) we have no way to7986// unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended7987// to be used only when presets have been selected on a MIDI channel.7988// Note that even if the preset is currently selected on a channel, it could be unselected at7989// any time. And we would end up with a NULL sample->data again, because we are not referencing7990// the preset here. Thus failure is our only option.7991result = FLUID_FAILED;7992}7993else7994{7995synth->storeid = id;7996result = fluid_preset_noteon(preset, synth, chan, key, vel);7997}79987999FLUID_API_RETURN(result);8000}80018002/**8003* Stop notes for a given note event voice ID.8004* @param synth FluidSynth instance8005* @param id Voice note event ID8006* @return #FLUID_OK on success, #FLUID_FAILED otherwise8007*8008* @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned8009* if no matching voice note event ID was found. Versions after 1.1.0 only8010* return #FLUID_FAILED if an error occurs.8011*/8012int8013fluid_synth_stop(fluid_synth_t *synth, unsigned int id)8014{8015int result;8016fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8017fluid_synth_api_enter(synth);8018fluid_synth_stop_LOCAL(synth, id);8019result = FLUID_OK;8020FLUID_API_RETURN(result);8021}80228023/* Local synthesis thread variant of fluid_synth_stop */8024static void8025fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id)8026{8027fluid_voice_t *voice;8028int i;80298030for(i = 0; i < synth->polyphony; i++)8031{8032voice = synth->voice[i];80338034if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id))8035{8036fluid_voice_noteoff(voice);8037}8038}8039}80408041/**8042* Offset the bank numbers of a loaded SoundFont, i.e.\ subtract8043* \c offset from any bank number when assigning instruments.8044*8045* @param synth FluidSynth instance8046* @param sfont_id ID of a loaded SoundFont8047* @param offset Bank offset value to apply to all instruments8048* @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise8049*/8050int8051fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset)8052{8053fluid_sfont_t *sfont;8054fluid_list_t *list;80558056fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8057fluid_synth_api_enter(synth);80588059for(list = synth->sfont; list; list = fluid_list_next(list))8060{8061sfont = fluid_list_get(list);80628063if(fluid_sfont_get_id(sfont) == sfont_id)8064{8065sfont->bankofs = offset;8066break;8067}8068}80698070if(!list)8071{8072FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);8073FLUID_API_RETURN(FLUID_FAILED);8074}80758076FLUID_API_RETURN(FLUID_OK);8077}80788079/**8080* Get bank offset of a loaded SoundFont.8081* @param synth FluidSynth instance8082* @param sfont_id ID of a loaded SoundFont8083* @return SoundFont bank offset value8084*/8085int8086fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id)8087{8088fluid_sfont_t *sfont;8089fluid_list_t *list;8090int offset = 0;80918092fluid_return_val_if_fail(synth != NULL, 0);8093fluid_synth_api_enter(synth);80948095for(list = synth->sfont; list; list = fluid_list_next(list))8096{8097sfont = fluid_list_get(list);80988099if(fluid_sfont_get_id(sfont) == sfont_id)8100{8101offset = sfont->bankofs;8102break;8103}8104}81058106if(!list)8107{8108FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);8109FLUID_API_RETURN(0);8110}81118112FLUID_API_RETURN(offset);8113}81148115void8116fluid_synth_api_enter(fluid_synth_t *synth)8117{8118if(synth->use_mutex)8119{8120fluid_rec_mutex_lock(synth->mutex);8121}81228123if(!synth->public_api_count)8124{8125fluid_synth_check_finished_voices(synth);8126}81278128synth->public_api_count++;8129}81308131void fluid_synth_api_exit(fluid_synth_t *synth)8132{8133synth->public_api_count--;81348135if(!synth->public_api_count)8136{8137fluid_rvoice_eventhandler_flush(synth->eventhandler);8138}81398140if(synth->use_mutex)8141{8142fluid_rec_mutex_unlock(synth->mutex);8143}81448145}81468147/**8148* Set midi channel type8149* @param synth FluidSynth instance8150* @param chan MIDI channel number (0 to MIDI channel count - 1)8151* @param type MIDI channel type (#fluid_midi_channel_type)8152* @return #FLUID_OK on success, #FLUID_FAILED otherwise8153* @since 1.1.48154*/8155int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)8156{8157fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED);8158FLUID_API_ENTRY_CHAN(FLUID_FAILED);81598160synth->channel[chan]->channel_type = type;81618162FLUID_API_RETURN(FLUID_OK);8163}81648165/**8166* Return the LADSPA effects instance used by FluidSynth8167*8168* @param synth FluidSynth instance8169* @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active8170*/8171fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)8172{8173fluid_return_val_if_fail(synth != NULL, NULL);81748175return synth->ladspa_fx;8176}81778178/**8179* Configure a general-purpose IIR biquad filter.8180*8181* @param synth FluidSynth instance8182* @param type Type of the IIR filter to use (see #fluid_iir_filter_type)8183* @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags)8184* @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED8185*8186* This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard.8187* By default this filter is off (#FLUID_IIR_DISABLED).8188*/8189int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags)8190{8191int i;8192fluid_voice_t *voice;81938194fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8195fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED);81968197fluid_synth_api_enter(synth);81988199synth->custom_filter_type = type;8200synth->custom_filter_flags = flags;82018202for(i = 0; i < synth->polyphony; i++)8203{8204voice = synth->voice[i];82058206fluid_voice_set_custom_filter(voice, type, flags);8207}82088209FLUID_API_RETURN(FLUID_OK);8210}82118212/**8213* Set the important channels for voice overflow priority calculation.8214*8215* @param synth FluidSynth instance8216* @param channels comma-separated list of channel numbers8217* @return #FLUID_OK on success, otherwise #FLUID_FAILED8218*/8219static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels)8220{8221int i;8222int retval = FLUID_FAILED;8223int *values = NULL;8224int num_values;8225fluid_overflow_prio_t *scores;82268227fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);82288229scores = &synth->overflow;82308231if(scores->num_important_channels < synth->midi_channels)8232{8233scores->important_channels = FLUID_REALLOC(scores->important_channels,8234sizeof(*scores->important_channels) * synth->midi_channels);82358236if(scores->important_channels == NULL)8237{8238FLUID_LOG(FLUID_ERR, "Out of memory");8239goto exit;8240}82418242scores->num_important_channels = synth->midi_channels;8243}82448245FLUID_MEMSET(scores->important_channels, FALSE,8246sizeof(*scores->important_channels) * scores->num_important_channels);82478248if(channels != NULL)8249{8250values = FLUID_ARRAY(int, synth->midi_channels);82518252if(values == NULL)8253{8254FLUID_LOG(FLUID_ERR, "Out of memory");8255goto exit;8256}82578258/* Every channel given in the comma-separated list of channel numbers8259* is set to TRUE, i.e. flagging it as "important". Channel numbers are8260* 1-based. */8261num_values = fluid_settings_split_csv(channels, values, synth->midi_channels);82628263for(i = 0; i < num_values; i++)8264{8265if(values[i] > 0 && values[i] <= synth->midi_channels)8266{8267scores->important_channels[values[i] - 1] = TRUE;8268}8269}8270}82718272retval = FLUID_OK;82738274exit:8275FLUID_FREE(values);8276return retval;8277}82788279/*8280* Handler for synth.overflow.important-channels setting.8281*/8282static void fluid_synth_handle_important_channels(void *data, const char *name,8283const char *value)8284{8285fluid_synth_t *synth = (fluid_synth_t *)data;82868287fluid_synth_api_enter(synth);8288fluid_synth_set_important_channels(synth, value);8289fluid_synth_api_exit(synth);8290}829182928293/* API legato mode *********************************************************/82948295/**8296* Sets the legato mode of a channel.8297*8298* @param synth the synth instance.8299* @param chan MIDI channel number (0 to MIDI channel count - 1).8300* @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.8301*8302* @return8303* - #FLUID_OK on success.8304* - #FLUID_FAILED8305* - \a synth is NULL.8306* - \a chan is outside MIDI channel count.8307* - \a legatomode is invalid.8308*/8309int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode)8310{8311/* checks parameters first */8312fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED);8313fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED);8314FLUID_API_ENTRY_CHAN(FLUID_FAILED);8315/**/8316synth->channel[chan]->legatomode = legatomode;8317/**/8318FLUID_API_RETURN(FLUID_OK);8319}83208321/**8322* Gets the legato mode of a channel.8323*8324* @param synth the synth instance.8325* @param chan MIDI channel number (0 to MIDI channel count - 1).8326* @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.8327*8328* @return8329* - #FLUID_OK on success.8330* - #FLUID_FAILED8331* - \a synth is NULL.8332* - \a chan is outside MIDI channel count.8333* - \a legatomode is NULL.8334*/8335int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode)8336{8337/* checks parameters first */8338fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED);8339FLUID_API_ENTRY_CHAN(FLUID_FAILED);8340/**/8341* legatomode = synth->channel[chan]->legatomode;8342/**/8343FLUID_API_RETURN(FLUID_OK);8344}83458346/* API portamento mode *********************************************************/83478348/**8349* Sets the portamento mode of a channel.8350*8351* @param synth the synth instance.8352* @param chan MIDI channel number (0 to MIDI channel count - 1).8353* @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode.8354* @return8355* - #FLUID_OK on success.8356* - #FLUID_FAILED8357* - \a synth is NULL.8358* - \a chan is outside MIDI channel count.8359* - \a portamentomode is invalid.8360*/8361int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan,8362int portamentomode)8363{8364/* checks parameters first */8365fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED);8366fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED);8367FLUID_API_ENTRY_CHAN(FLUID_FAILED);8368/**/8369synth->channel[chan]->portamentomode = portamentomode;8370/**/8371FLUID_API_RETURN(FLUID_OK);8372}83738374/**8375* Gets the portamento mode of a channel.8376*8377* @param synth the synth instance.8378* @param chan MIDI channel number (0 to MIDI channel count - 1).8379* @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode.8380* @return8381* - #FLUID_OK on success.8382* - #FLUID_FAILED8383* - \a synth is NULL.8384* - \a chan is outside MIDI channel count.8385* - \a portamentomode is NULL.8386*/8387int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan,8388int *portamentomode)8389{8390/* checks parameters first */8391fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED);8392FLUID_API_ENTRY_CHAN(FLUID_FAILED);8393/**/8394* portamentomode = synth->channel[chan]->portamentomode;8395/**/8396FLUID_API_RETURN(FLUID_OK);8397}83988399/* API breath mode *********************************************************/84008401/**8402* Sets the breath mode of a channel.8403*8404* @param synth the synth instance.8405* @param chan MIDI channel number (0 to MIDI channel count - 1).8406* @param breathmode The breath mode as indicated by #fluid_channel_breath_flags.8407*8408* @return8409* - #FLUID_OK on success.8410* - #FLUID_FAILED8411* - \a synth is NULL.8412* - \a chan is outside MIDI channel count.8413*/8414int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode)8415{8416/* checks parameters first */8417FLUID_API_ENTRY_CHAN(FLUID_FAILED);8418/**/8419fluid_channel_set_breath_info(synth->channel[chan], breathmode);8420/**/8421FLUID_API_RETURN(FLUID_OK);8422}84238424/**8425* Gets the breath mode of a channel.8426*8427* @param synth the synth instance.8428* @param chan MIDI channel number (0 to MIDI channel count - 1).8429* @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags.8430*8431* @return8432* - #FLUID_OK on success.8433* - #FLUID_FAILED8434* - \a synth is NULL.8435* - \a chan is outside MIDI channel count.8436* - \a breathmode is NULL.8437*/8438int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode)8439{8440/* checks parameters first */8441fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED);8442FLUID_API_ENTRY_CHAN(FLUID_FAILED);8443/**/8444* breathmode = fluid_channel_get_breath_info(synth->channel[chan]);8445/**/8446FLUID_API_RETURN(FLUID_OK);8447}84488449/** API Poly/mono mode ******************************************************/84508451/*8452* Resets a basic channel group of MIDI channels.8453* @param synth the synth instance.8454* @param chan the beginning channel of the group.8455* @param nbr_chan the number of channel in the group.8456*/8457static void8458fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan)8459{8460int i;84618462for(i = chan; i < chan + nbr_chan; i++)8463{8464fluid_channel_reset_basic_channel_info(synth->channel[i]);8465synth->channel[i]->mode_val = 0;8466}8467}84688469/**8470* Disables and unassigns all channels from a basic channel group.8471*8472* @param synth The synth instance.8473* @param chan The basic channel of the group to reset or -1 to reset all channels.8474* @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset())8475* a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY.8476* All other channels belong to this basic channel group. Make sure to call this function before8477* setting any custom basic channel setup.8478*8479* @return8480* - #FLUID_OK on success.8481* - #FLUID_FAILED8482* - \a synth is NULL.8483* - \a chan is outside MIDI channel count.8484* - \a chan isn't a basic channel.8485*/8486int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan)8487{8488int nbr_chan;84898490/* checks parameters first */8491if(chan < 0)8492{8493fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8494fluid_synth_api_enter(synth);8495/* The range is all MIDI channels from 0 to MIDI channel count -1 */8496chan = 0; /* beginning chan */8497nbr_chan = synth->midi_channels; /* MIDI Channels number */8498}8499else8500{8501FLUID_API_ENTRY_CHAN(FLUID_FAILED);85028503/* checks if chan is a basic channel */8504if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC))8505{8506FLUID_API_RETURN(FLUID_FAILED);8507}85088509/* The range is all MIDI channels in the group from chan */8510nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */8511}85128513/* resets the range of MIDI channels */8514fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan);8515FLUID_API_RETURN(FLUID_OK);8516}85178518/**8519* Checks if a new basic channel group overlaps the next basic channel group.8520*8521* On success the function returns the possible number of channel for this8522* new basic channel group.8523* The function fails if the new group overlaps the next basic channel group.8524*8525* @param see fluid_synth_set_basic_channel.8526* @return8527* - On success, the effective number of channels for this new basic channel group,8528* #FLUID_FAILED otherwise.8529* - #FLUID_FAILED8530* - \a val has a number of channels overlapping next basic channel group or been8531* above MIDI channel count.8532*/8533static int8534fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val)8535{8536int i, n_chan = synth->midi_channels; /* MIDI Channels count */8537int real_val = val; /* real number of channels in the group */85388539/* adjusts val range */8540if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY)8541{8542real_val = 1; /* mode poly omnioff implies a group of only one channel.*/8543}8544else if(val == 0)8545{8546/* mode poly omnion (0), mono omnion (1), mono omni off (3) */8547/* value 0 means all possible channels from basicchan to MIDI channel count -1.*/8548real_val = n_chan - basicchan;8549}8550/* checks if val range is above MIDI channel count */8551else if(basicchan + val > n_chan)8552{8553return FLUID_FAILED;8554}85558556/* checks if this basic channel group overlaps next basic channel group */8557for(i = basicchan + 1; i < basicchan + real_val; i++)8558{8559if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC)8560{8561/* A value of 0 for val means all possible channels from basicchan to8562to the next basic channel -1 (if any).8563When i reaches the next basic channel group, real_val will be8564limited if it is possible */8565if(val == 0)8566{8567/* limitation of real_val */8568real_val = i - basicchan;8569break;8570}85718572/* overlap with the next basic channel group */8573return FLUID_FAILED;8574}8575}85768577return real_val;8578}85798580/**8581* Sets a new basic channel group only. The function doesn't allow to change an8582* existing basic channel.8583*8584* The function fails if any channel overlaps any existing basic channel group.8585* To make room if necessary, basic channel groups can be cleared using8586* fluid_synth_reset_basic_channel().8587*8588* @param synth the synth instance.8589* @param chan the basic Channel number (0 to MIDI channel count-1).8590* @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes).8591* @param val number of channels in the group.8592* @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY,8593* #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value8594* of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any)8595* or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY8596* as this mode implies a group of only one channel.8597* @return8598* - #FLUID_OK on success.8599* - #FLUID_FAILED8600* - \a synth is NULL.8601* - \a chan is outside MIDI channel count.8602* - \a mode is invalid.8603* - \a val has a number of channels overlapping another basic channel group or been8604* above MIDI channel count.8605* - When the function fails, any existing basic channels aren't modified.8606*/8607int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val)8608{8609/* check parameters */8610fluid_return_val_if_fail(mode >= 0, FLUID_FAILED);8611fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED);8612fluid_return_val_if_fail(val >= 0, FLUID_FAILED);8613FLUID_API_ENTRY_CHAN(FLUID_FAILED);86148615/**/8616if(val > 0 && chan + val > synth->midi_channels)8617{8618FLUID_API_RETURN(FLUID_FAILED);8619}86208621/* Checks if there is an overlap with the next basic channel */8622val = fluid_synth_check_next_basic_channel(synth, chan, mode, val);86238624if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)8625{8626/* overlap with the next or previous channel group */8627FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan);8628FLUID_API_RETURN(FLUID_FAILED);8629}86308631/* sets a new basic channel group */8632fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val);8633/**/8634FLUID_API_RETURN(FLUID_OK);8635}86368637/*8638* Local version of fluid_synth_set_basic_channel(), called internally:8639* - by fluid_synth_set_basic_channel() to set a new basic channel group.8640* - during creation new_fluid_synth() or on CC reset to set a default basic channel group.8641* - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group.8642*8643* @param see fluid_synth_set_basic_channel()8644*/8645static void8646fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val)8647{8648int i;86498650/* sets the basic channel group */8651for(i = basicchan; i < basicchan + val; i++)8652{8653int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */8654int new_val;8655/* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */8656fluid_synth_all_notes_off_LOCAL(synth, i);86578658if(i == basicchan)8659{8660new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */8661new_val = val; /* number of channels in the group */8662}8663else8664{8665new_val = 0; /* val is 0 for other channel than basic channel */8666}86678668/* Channel is enabled */8669new_mode |= FLUID_CHANNEL_ENABLED;8670/* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */8671fluid_channel_set_basic_channel_info(synth->channel[i], new_mode);8672synth->channel[i]->mode_val = new_val;8673}8674}86758676/**8677* Searches a previous basic channel starting from chan.8678*8679* @param synth the synth instance.8680* @param chan starting index of the search (including chan).8681* @return index of the basic channel if found , FLUID_FAILED otherwise.8682*/8683static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan)8684{8685for(; chan >= 0; chan--)8686{8687/* searches previous basic channel */8688if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)8689{8690/* chan is the previous basic channel */8691return chan;8692}8693}86948695return FLUID_FAILED;8696}86978698/**8699* Returns poly mono mode information of any MIDI channel.8700*8701* @param synth the synth instance8702* @param chan MIDI channel number (0 to MIDI channel count - 1)8703* @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled.8704* @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled.8705* @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled.8706* @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL8707* the corresponding information isn't returned.8708*8709* @return8710* - #FLUID_OK on success.8711* - #FLUID_FAILED8712* - \a synth is NULL.8713* - \a chan is outside MIDI channel count.8714*/8715int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan,8716int *basic_chan_out,8717int *mode_out,8718int *val_out)8719{8720int basic_chan = FLUID_FAILED;8721int mode = FLUID_FAILED;8722int val = FLUID_FAILED;87238724/* checks parameters first */8725FLUID_API_ENTRY_CHAN(FLUID_FAILED);87268727if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) &&8728/* chan is enabled , we search the basic channel chan belongs to */8729(basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED)8730{8731mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK;8732val = synth->channel[basic_chan]->mode_val;8733}87348735/* returns the information if they are requested */8736if(basic_chan_out)8737{8738* basic_chan_out = basic_chan;8739}87408741if(mode_out)8742{8743* mode_out = mode;8744}87458746if(val_out)8747{8748* val_out = val;8749}87508751FLUID_API_RETURN(FLUID_OK);8752}875387548755