Path: blob/master/libs/fluidsynth/src/synth/fluid_synth.c
8703 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);1749FLUID_LOG(FLUID_INFO, "Successfully configured channel group, from channel %d up to including chan %d to mode 0x%X", channum, channum + value, new_mode);1750break; /* FLUID_OK */1751}1752}1753else1754{1755static const char* poly_mono_str[] = {"OMNI_OFF","OMNI_ON","POLY_OFF","POLY_ON"};1756FLUID_LOG(FLUID_WARN, "Failed to set channel %d to %s: Operation illegal, because channel is currently no basic channel.", channum, poly_mono_str[num - OMNI_OFF]);1757}17581759return FLUID_FAILED;17601761case LEGATO_SWITCH: /* not allowed to modulate */1762/* handles Poly/mono commutation on Legato pedal On/Off.*/1763fluid_channel_cc_legato(chan, value);1764break;17651766case PORTAMENTO_SWITCH: /* not allowed to modulate */1767/* Special handling of the monophonic list */1768/* Invalids the most recent note played in a staccato manner */1769fluid_channel_invalid_prev_note_staccato(chan);1770break;17711772case SUSTAIN_SWITCH: /* not allowed to modulate */17731774/* Release voices if Sustain switch is released */1775if(value < 64) /* Sustain is released */1776{1777fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum);1778}17791780break;17811782case SOSTENUTO_SWITCH: /* not allowed to modulate */17831784/* Release voices if Sostetuno switch is released */1785if(value < 64) /* Sostenuto is released */1786{1787fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);1788}1789else /* Sostenuto is depressed */1790/* Update sostenuto order id when pedaling on Sostenuto */1791{1792chan->sostenuto_orderid = synth->noteid; /* future voice id value */1793}17941795break;17961797case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1798fluid_channel_set_bank_msb(chan, value & 0x7F);1799break;18001801case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1802fluid_channel_set_bank_lsb(chan, value & 0x7F);1803break;18041805case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1806fluid_synth_all_notes_off_LOCAL(synth, channum);1807break;18081809case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1810fluid_synth_all_sounds_off_LOCAL(synth, channum);1811break;18121813case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1814fluid_channel_init_ctrl(chan, 1);1815// the hold pedals have been reset, we maybe need to release voices1816fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum);1817fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);1818fluid_synth_modulate_voices_all_LOCAL(synth, channum);1819break;18201821case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1822case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1823{1824/* handle both because msb might come first */1825int lsb_value = fluid_channel_get_cc(chan, DATA_ENTRY_LSB);1826int msb_value = fluid_channel_get_cc(chan, DATA_ENTRY_MSB);1827int data = (msb_value << 7) + lsb_value;18281829if(chan->nrpn_active) /* NRPN is active? */1830{1831/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */1832if((fluid_channel_get_cc(chan, NRPN_MSB) == 120)1833&& (fluid_channel_get_cc(chan, NRPN_LSB) < 100))1834{1835nrpn_select = chan->nrpn_select;18361837if(nrpn_select < GEN_LAST)1838{1839float val = fluid_gen_scale_nrpn(nrpn_select, data);1840if(synth->verbose)1841{1842FLUID_LOG(FLUID_INFO, "NRPN\t%d\t%d\t%d\t%f", channum, nrpn_select, data, val);1843}1844fluid_synth_set_gen_LOCAL(synth, channum, nrpn_select, val);1845}18461847chan->nrpn_select = 0; /* Reset to 0 */1848}1849else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs1850{1851// ALTITUDE.MID also manipulates AWE32 NRPNs by only using DATA LSB events - seems to be legal1852if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs1853{1854int gen = fluid_channel_get_cc(chan, NRPN_LSB);1855if(synth->verbose)1856{1857FLUID_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);1858}1859if(gen <= 26) // Effect 26 (reverb) is the last effect to select1860{1861fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data, lsb_value);1862}1863else1864{1865FLUID_LOG(FLUID_INFO, "Ignoring unknown AWE32 NRPN targetting effect %d", gen);1866}1867}1868}1869}1870else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */1871{1872switch(fluid_channel_get_cc(chan, RPN_LSB))1873{1874case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones plus cents */1875fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], msb_value + lsb_value / 100.0f); /* 0-127 maps to 0-100 cents */1876fluid_synth_update_pitch_wheel_sens_LOCAL(synth, channum); /* Update bend range */1877break;18781879case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */1880fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE,1881(float)(data - 8192) * (100.0f / 8192.0f));1882break;18831884case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */1885fluid_synth_set_gen_LOCAL(synth, channum, GEN_COARSETUNE,1886msb_value - 64);1887break;18881889case RPN_TUNING_PROGRAM_CHANGE:1890fluid_channel_set_tuning_prog(chan, msb_value);1891fluid_synth_activate_tuning(synth, channum,1892fluid_channel_get_tuning_bank(chan),1893msb_value, TRUE);1894break;18951896case RPN_TUNING_BANK_SELECT:1897fluid_channel_set_tuning_bank(chan, msb_value);1898break;18991900case RPN_MODULATION_DEPTH_RANGE:1901break;1902}1903}19041905break;1906}19071908case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1909fluid_channel_set_cc(chan, NRPN_LSB, 0);1910chan->nrpn_select = 0;1911chan->nrpn_active = 1;1912break;19131914case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */19151916/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */1917if(fluid_channel_get_cc(chan, NRPN_MSB) == 120)1918{1919if(value == 100)1920{1921chan->nrpn_select += 100;1922}1923else if(value == 101)1924{1925chan->nrpn_select += 1000;1926}1927else if(value == 102)1928{1929chan->nrpn_select += 10000;1930}1931else if(value < 100)1932{1933chan->nrpn_select += value;1934}1935}19361937chan->nrpn_active = 1;1938break;19391940case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1941case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */1942chan->nrpn_active = 0;1943break;19441945case BREATH_MSB:1946/* handles CC Breath On/Off noteOn/noteOff mode */1947fluid_channel_cc_breath_note_on_off(chan, value);19481949/* fall-through */1950default:1951/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */1952/* However, as long fluidsynth will use only CC 7 bits resolution, it1953is safe to ignore these SF recommendations on CC receive. See1954explanations above */1955/* if (! (32 <= num && num <= 63)) */1956{1957return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num);1958}1959}19601961return FLUID_OK;1962}19631964/**1965* Get current MIDI controller value on a MIDI channel.1966* @param synth FluidSynth instance1967* @param chan MIDI channel number (0 to MIDI channel count - 1)1968* @param num MIDI controller number (0-127)1969* @param pval Location to store MIDI controller value (0-127)1970* @return #FLUID_OK on success, #FLUID_FAILED otherwise1971*/1972int1973fluid_synth_get_cc(fluid_synth_t *synth, int chan, int num, int *pval)1974{1975fluid_return_val_if_fail(num >= 0 && num < 128, FLUID_FAILED);1976fluid_return_val_if_fail(pval != NULL, FLUID_FAILED);19771978FLUID_API_ENTRY_CHAN(FLUID_FAILED);19791980/* Allowed only on MIDI channel enabled */1981FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);19821983*pval = fluid_channel_get_cc(synth->channel[chan], num);1984FLUID_API_RETURN(FLUID_OK);1985}19861987/*1988* Handler for synth.device-id setting.1989*/1990static void1991fluid_synth_handle_device_id(void *data, const char *name, int value)1992{1993fluid_synth_t *synth = (fluid_synth_t *)data;1994fluid_return_if_fail(synth != NULL);19951996fluid_synth_api_enter(synth);1997synth->device_id = value;1998fluid_synth_api_exit(synth);1999}20002001/**2002* Process a MIDI SYSEX (system exclusive) message.2003* @param synth FluidSynth instance2004* @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7)2005* @param len Length of data in buffer2006* @param response Buffer to store response to or NULL to ignore2007* @param response_len IN/OUT parameter, in: size of response buffer, out:2008* amount of data written to response buffer (if #FLUID_FAILED is returned and2009* this value is non-zero, it indicates the response buffer is too small)2010* @param handled Optional location to store boolean value if message was2011* recognized and handled or not (set to TRUE if it was handled)2012* @param dryrun TRUE to just do a dry run but not actually execute the SYSEX2013* command (useful for checking if a SYSEX message would be handled)2014* @return #FLUID_OK on success, #FLUID_FAILED otherwise2015* @since 1.1.02016* @note When Fluidsynth receives an XG System Mode ON message, it compares the @p synth 's deviceID2017* directly with the deviceID of the SysEx message. This is contrary to the XG spec (page 42), which2018* requires to only compare the lower nibble. However, following the XG spec seems to break drum channels2019* for a lot of MIDI files out there and therefore we've decided for this customization. If you rely on2020* XG System Mode ON messages, make sure to set the setting \ref settings_synth_device-id to match the2021* deviceID provided in the SysEx message (in most cases, this will be <code>deviceID=16</code>).2022*2023* @code2024* SYSEX format (0xF0 and 0xF7 bytes shall not be passed to this function):2025* Non-realtime: 0xF0 0x7E <DeviceId> [BODY] 0xF72026* Realtime: 0xF0 0x7F <DeviceId> [BODY] 0xF72027* Tuning messages: 0xF0 0x7E/0x7F <DeviceId> 0x08 <sub ID2> [BODY] <ChkSum> 0xF72028* GS DT1 messages: 0xF0 0x41 <DeviceId> 0x42 0x12 [ADDRESS (3 bytes)] [DATA] <ChkSum> 0xF72029* @endcode2030*/2031int2032fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,2033char *response, int *response_len, int *handled, int dryrun)2034{2035int avail_response = 0;20362037if(handled)2038{2039*handled = FALSE;2040}20412042if(response_len)2043{2044avail_response = *response_len;2045*response_len = 0;2046}20472048fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2049fluid_return_val_if_fail(data != NULL, FLUID_FAILED);2050fluid_return_val_if_fail(len > 0, FLUID_FAILED);2051fluid_return_val_if_fail(!response || response_len, FLUID_FAILED);20522053if(len < 4)2054{2055return FLUID_OK;2056}20572058/* MIDI tuning SYSEX message? */2059if((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME)2060&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2061&& data[2] == MIDI_SYSEX_MIDI_TUNING_ID)2062{2063int result;2064fluid_synth_api_enter(synth);2065result = fluid_synth_sysex_midi_tuning(synth, data, len, response,2066response_len, avail_response,2067handled, dryrun);20682069FLUID_API_RETURN(result);2070}20712072/* GM or GM2 system on */2073if(data[0] == MIDI_SYSEX_UNIV_NON_REALTIME2074&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2075&& data[2] == MIDI_SYSEX_GM_ID)2076{2077if(handled)2078{2079*handled = TRUE;2080}2081if(!dryrun && (data[3] == MIDI_SYSEX_GM_ON2082|| data[3] == MIDI_SYSEX_GM2_ON))2083{2084int result;2085fluid_synth_api_enter(synth);2086synth->bank_select = FLUID_BANK_STYLE_GM;2087result = fluid_synth_system_reset_LOCAL(synth);2088FLUID_API_RETURN(result);2089}2090return FLUID_OK;2091}20922093/* GS DT1 message */2094if(data[0] == MIDI_SYSEX_MANUF_ROLAND2095&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2096&& data[2] == MIDI_SYSEX_GS_ID2097&& data[3] == MIDI_SYSEX_GS_DT1)2098{2099int result;2100fluid_synth_api_enter(synth);2101result = fluid_synth_sysex_gs_dt1(synth, data, len, response,2102response_len, avail_response,2103handled, dryrun);2104FLUID_API_RETURN(result);2105}21062107/* XG message */2108if(data[0] == MIDI_SYSEX_MANUF_YAMAHA2109&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL)2110&& data[2] == MIDI_SYSEX_XG_ID)2111{2112int result;2113fluid_synth_api_enter(synth);2114result = fluid_synth_sysex_xg(synth, data, len, response,2115response_len, avail_response,2116handled, dryrun);2117FLUID_API_RETURN(result);2118}21192120return FLUID_OK;2121}21222123/* Handler for MIDI tuning SYSEX messages */2124static int2125fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,2126char *response, int *response_len, int avail_response,2127int *handled, int dryrun)2128{2129int realtime, msgid;2130int bank = 0, prog, channels;2131double tunedata[128];2132int keys[128];2133char name[17]={0};2134int note, frac, frac2;2135uint8_t chksum;2136int i, count, index;2137const char *dataptr;2138char *resptr;21392140realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME;2141msgid = data[3];21422143switch(msgid)2144{2145case MIDI_SYSEX_TUNING_BULK_DUMP_REQ:2146case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK:2147if(data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)2148{2149if(len != 5 || data[4] & 0x80 || !response)2150{2151return FLUID_OK;2152}21532154*response_len = 406;2155prog = data[4];2156}2157else2158{2159if(len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response)2160{2161return FLUID_OK;2162}21632164*response_len = 407;2165bank = data[4];2166prog = data[5];2167}21682169if(dryrun)2170{2171if(handled)2172{2173*handled = TRUE;2174}21752176return FLUID_OK;2177}21782179if(avail_response < *response_len)2180{2181return FLUID_FAILED;2182}21832184/* Get tuning data, return if tuning not found */2185if(fluid_synth_tuning_dump(synth, bank, prog, name, 17, tunedata) == FLUID_FAILED)2186{2187*response_len = 0;2188return FLUID_OK;2189}21902191resptr = response;21922193*resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME;2194*resptr++ = synth->device_id;2195*resptr++ = MIDI_SYSEX_MIDI_TUNING_ID;2196*resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP;21972198if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK)2199{2200*resptr++ = bank;2201}22022203*resptr++ = prog;2204/* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */2205FLUID_MEMCPY(resptr, name, 16);2206resptr += 16;22072208for(i = 0; i < 128; i++)2209{2210note = tunedata[i] / 100.0;2211fluid_clip(note, 0, 127);22122213frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0;2214fluid_clip(frac, 0, 16383);22152216*resptr++ = note;2217*resptr++ = frac >> 7;2218*resptr++ = frac & 0x7F;2219}22202221if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)2222{2223/* NOTE: Checksum is not as straight forward as the bank based messages */2224chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID2225^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog;22262227for(i = 21; i < 128 * 3 + 21; i++)2228{2229chksum ^= response[i];2230}2231}2232else2233{2234for(i = 1, chksum = 0; i < 406; i++)2235{2236chksum ^= response[i];2237}2238}22392240*resptr++ = chksum & 0x7F;22412242if(handled)2243{2244*handled = TRUE;2245}22462247break;22482249case MIDI_SYSEX_TUNING_NOTE_TUNE:2250case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK:2251dataptr = data + 4;22522253if(msgid == MIDI_SYSEX_TUNING_NOTE_TUNE)2254{2255if(len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6)2256{2257return FLUID_OK;2258}2259}2260else2261{2262if(len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x802263|| len != data[6] * 4 + 7)2264{2265return FLUID_OK;2266}22672268bank = *dataptr++;2269}22702271if(dryrun)2272{2273if(handled)2274{2275*handled = TRUE;2276}22772278return FLUID_OK;2279}22802281prog = *dataptr++;2282count = *dataptr++;22832284for(i = 0, index = 0; i < count; i++)2285{2286note = *dataptr++;22872288if(note & 0x80)2289{2290return FLUID_OK;2291}22922293keys[index] = note;22942295note = *dataptr++;2296frac = *dataptr++;2297frac2 = *dataptr++;22982299if(note & 0x80 || frac & 0x80 || frac2 & 0x80)2300{2301return FLUID_OK;2302}23032304frac = frac << 7 | frac2;23052306/* No change pitch value? Doesn't really make sense to send that, but.. */2307if(note == 0x7F && frac == 16383)2308{2309continue;2310}23112312tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0);2313index++;2314}23152316if(index > 0)2317{2318if(fluid_synth_tune_notes(synth, bank, prog, index, keys, tunedata,2319realtime) == FLUID_FAILED)2320{2321return FLUID_FAILED;2322}2323}23242325if(handled)2326{2327*handled = TRUE;2328}23292330break;23312332case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE:2333case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE:2334if((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19)2335|| (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31))2336{2337return FLUID_OK;2338}23392340if(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80)2341{2342return FLUID_OK;2343}23442345if(dryrun)2346{2347if(handled)2348{2349*handled = TRUE;2350}23512352return FLUID_OK;2353}23542355channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];23562357if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)2358{2359for(i = 0; i < 12; i++)2360{2361frac = data[i + 7];23622363if(frac & 0x80)2364{2365return FLUID_OK;2366}23672368tunedata[i] = (int)frac - 64;2369}2370}2371else2372{2373for(i = 0; i < 12; i++)2374{2375frac = data[i * 2 + 7];2376frac2 = data[i * 2 + 8];23772378if(frac & 0x80 || frac2 & 0x80)2379{2380return FLUID_OK;2381}23822383tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0);2384}2385}23862387if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX",2388tunedata, realtime) == FLUID_FAILED)2389{2390return FLUID_FAILED;2391}23922393if(channels)2394{2395for(i = 0; i < 16; i++)2396{2397if(channels & (1 << i))2398{2399fluid_synth_activate_tuning(synth, i, 0, 0, realtime);2400}2401}2402}24032404if(handled)2405{2406*handled = TRUE;2407}24082409break;2410}24112412return FLUID_OK;2413}24142415/* Handler for GS DT1 messages */2416static int2417fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len,2418char *response, int *response_len, int avail_response,2419int *handled, int dryrun)2420{2421int addr;2422int len_data;2423int checksum = 0, i;24242425if(len < 9) // at least one byte of data should be transmitted2426{2427FLUID_LOG(FLUID_INFO, "SysEx DT1: message too short, dropping it.");2428return FLUID_FAILED;2429}2430len_data = len - 8;2431addr = (data[4] << 16) | (data[5] << 8) | data[6];24322433for (i = 4; i < len - 1; ++i)2434{2435checksum += data[i];2436}2437checksum = 0x80 - (checksum & 0x7F);2438if (checksum != data[len - 1])2439{2440FLUID_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);2441return FLUID_FAILED;2442}24432444if (addr == 0x40007F) // Mode set2445{2446if (len_data > 1 || (data[7] != 0 && data[7] != 0x7f))2447{2448FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid mode set message");2449return FLUID_FAILED;2450}2451if (handled)2452{2453*handled = TRUE;2454}2455if (!dryrun)2456{2457if (data[7] == 0)2458{2459synth->bank_select = FLUID_BANK_STYLE_GS;2460}2461else2462{2463synth->bank_select = FLUID_BANK_STYLE_GM;2464}2465return fluid_synth_system_reset_LOCAL(synth);2466}2467return FLUID_OK;2468}24692470if (synth->bank_select != FLUID_BANK_STYLE_GS)2471{2472return FLUID_OK; // Silently ignore all other messages2473}24742475if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part2476{2477if (len_data > 1 || data[7] > 0x02)2478{2479FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid rhythm part message");2480return FLUID_FAILED;2481}2482if (handled)2483{2484*handled = TRUE;2485}2486if (!dryrun)2487{2488int chan = (addr >> 8) & 0x0F;2489//See the Patch Part parameters section in SC-88Pro/8850 owner's manual2490chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1);2491synth->channel[chan]->channel_type =2492data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM;24932494FLUID_LOG(FLUID_DBG, "SysEx DT1: setting MIDI channel %d to type %d", chan, (int)synth->channel[chan]->channel_type);2495//Roland synths seem to "remember" the last instrument a channel2496//used in the selected mode. This behavior is not replicated here.2497fluid_synth_program_change(synth, chan, 0);2498}2499return FLUID_OK;2500}25012502//silently ignore2503return FLUID_OK;2504}25052506/* Handler for XG messages */2507static int2508fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, int len,2509char *response, int *response_len, int avail_response,2510int *handled, int dryrun)2511{2512int addr;2513int len_data;25142515if(len < 7) // at least one byte of data should be transmitted2516{2517return FLUID_FAILED;2518}2519len_data = len - 6;2520addr = (data[3] << 16) | (data[4] << 8) | data[5];25212522if (addr == 0x00007E // Reset2523|| addr == 0x00007F) // Reset to factory2524{2525if (len_data > 1 || data[6] != 0)2526{2527return FLUID_FAILED;2528}2529if (handled)2530{2531*handled = TRUE;2532}2533if (!dryrun)2534{2535synth->bank_select = FLUID_BANK_STYLE_XG;2536return fluid_synth_system_reset_LOCAL(synth);2537}2538return FLUID_OK;2539}25402541/* No other messages handled yet2542if (synth->bank_select != FLUID_BANK_STYLE_XG)2543{2544return FLUID_OK; // Silently ignore all other messages2545}*/25462547//silently ignore2548return FLUID_OK;2549}25502551/**2552* Turn off all voices that are playing on the given MIDI channel, by putting them into release phase.2553* @param synth FluidSynth instance2554* @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)2555* @return #FLUID_OK on success, #FLUID_FAILED otherwise2556* @since 1.1.42557*/2558int2559fluid_synth_all_notes_off(fluid_synth_t *synth, int chan)2560{2561int result;25622563fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2564fluid_return_val_if_fail(chan >= -1, FLUID_FAILED);2565fluid_synth_api_enter(synth);25662567if(chan >= synth->midi_channels)2568{2569result = FLUID_FAILED;2570}2571else2572{2573/* Allowed (even for channel disabled) as chan = -1 selects all channels */2574result = fluid_synth_all_notes_off_LOCAL(synth, chan);2575}25762577FLUID_API_RETURN(result);2578}25792580/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */2581//static int2582int2583fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan)2584{2585fluid_voice_t *voice;2586int i;25872588for(i = 0; i < synth->polyphony; i++)2589{2590voice = synth->voice[i];25912592if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))2593{2594fluid_voice_noteoff(voice);2595}2596}25972598return FLUID_OK;2599}26002601/**2602* Immediately stop all voices on the given MIDI channel (skips release phase).2603* @param synth FluidSynth instance2604* @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)2605* @return #FLUID_OK on success, #FLUID_FAILED otherwise2606* @since 1.1.42607*/2608int2609fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan)2610{2611int result;26122613fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2614fluid_return_val_if_fail(chan >= -1, FLUID_FAILED);2615fluid_synth_api_enter(synth);26162617if(chan >= synth->midi_channels)2618{2619result = FLUID_FAILED;2620}2621else2622{2623/* Allowed (even for channel disabled) as chan = -1 selects all channels */2624result = fluid_synth_all_sounds_off_LOCAL(synth, chan);2625}26262627FLUID_API_RETURN(result);2628}26292630/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */2631static int2632fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan)2633{2634fluid_voice_t *voice;2635int i;26362637for(i = 0; i < synth->polyphony; i++)2638{2639voice = synth->voice[i];26402641if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))2642{2643fluid_voice_off(voice);2644}2645}26462647return FLUID_OK;2648}26492650/**2651* Reset reverb engine2652* @param synth FluidSynth instance2653* @return #FLUID_OK on success, #FLUID_FAILED otherwise2654*/2655int2656fluid_synth_reset_reverb(fluid_synth_t *synth)2657{2658fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2659fluid_synth_api_enter(synth);2660fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);2661FLUID_API_RETURN(FLUID_OK);2662}26632664/**2665* Reset chorus engine2666* @param synth FluidSynth instance2667* @return #FLUID_OK on success, #FLUID_FAILED otherwise2668*/2669int2670fluid_synth_reset_chorus(fluid_synth_t *synth)2671{2672fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2673fluid_synth_api_enter(synth);2674fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);2675FLUID_API_RETURN(FLUID_OK);2676}267726782679/**2680* Send MIDI system reset command (big red 'panic' button), turns off notes, resets2681* controllers and restores initial basic channel configuration.2682* @param synth FluidSynth instance2683* @return #FLUID_OK on success, #FLUID_FAILED otherwise2684*/2685int2686fluid_synth_system_reset(fluid_synth_t *synth)2687{2688int result;2689fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);2690fluid_synth_api_enter(synth);2691result = fluid_synth_system_reset_LOCAL(synth);2692FLUID_API_RETURN(result);2693}26942695/* Local variant of the system reset command */2696static int2697fluid_synth_system_reset_LOCAL(fluid_synth_t *synth)2698{2699int i;27002701if(synth->verbose)2702{2703FLUID_LOG(FLUID_INFO, "=== systemreset ===");2704}27052706fluid_synth_all_sounds_off_LOCAL(synth, -1);27072708for(i = 0; i < synth->midi_channels; i++)2709{2710fluid_channel_reset(synth->channel[i]);2711}27122713/* Basic channel 0, Mode Omni On Poly */2714fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,2715synth->midi_channels);27162717fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);2718fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);27192720return FLUID_OK;2721}27222723/**2724* Update voices on a MIDI channel after a MIDI control change.2725* @param synth FluidSynth instance2726* @param chan MIDI channel number (0 to MIDI channel count - 1)2727* @param is_cc Boolean value indicating if ctrl is a CC controller or not2728* @param ctrl MIDI controller value2729* @return #FLUID_OK on success, #FLUID_FAILED otherwise2730*/2731static int2732fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl)2733{2734fluid_voice_t *voice;2735int i;27362737for(i = 0; i < synth->polyphony; i++)2738{2739voice = synth->voice[i];27402741if(fluid_voice_get_channel(voice) == chan)2742{2743fluid_voice_modulate(voice, is_cc, ctrl);2744}2745}27462747return FLUID_OK;2748}27492750/**2751* Update voices on a MIDI channel after all MIDI controllers have been changed.2752* @param synth FluidSynth instance2753* @param chan MIDI channel number (0 to MIDI channel count - 1)2754* @return #FLUID_OK on success, #FLUID_FAILED otherwise2755*/2756static int2757fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan)2758{2759fluid_voice_t *voice;2760int i;27612762for(i = 0; i < synth->polyphony; i++)2763{2764voice = synth->voice[i];27652766if(fluid_voice_get_channel(voice) == chan)2767{2768fluid_voice_modulate_all(voice);2769}2770}27712772return FLUID_OK;2773}27742775/**2776* Set the MIDI channel pressure controller value.2777* @param synth FluidSynth instance2778* @param chan MIDI channel number (0 to MIDI channel count - 1)2779* @param val MIDI channel pressure value (0-127)2780* @return #FLUID_OK on success, #FLUID_FAILED otherwise2781*/2782int2783fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val)2784{2785int result;2786fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);27872788FLUID_API_ENTRY_CHAN(FLUID_FAILED);27892790/* Allowed only on MIDI channel enabled */2791FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);27922793if(synth->verbose)2794{2795FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);2796}27972798fluid_channel_set_channel_pressure(synth->channel[chan], val);2799result = fluid_synth_update_channel_pressure_LOCAL(synth, chan);28002801FLUID_API_RETURN(result);2802}28032804/* Updates channel pressure from within synthesis thread */2805static int2806fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int chan)2807{2808return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_CHANNELPRESSURE);2809}28102811/**2812* Set the MIDI polyphonic key pressure controller value.2813* @param synth FluidSynth instance2814* @param chan MIDI channel number (0 to MIDI channel count - 1)2815* @param key MIDI key number (0-127)2816* @param val MIDI key pressure value (0-127)2817* @return #FLUID_OK on success, #FLUID_FAILED otherwise2818* @since 2.0.02819*/2820int2821fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val)2822{2823int result;2824fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);2825fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);28262827FLUID_API_ENTRY_CHAN(FLUID_FAILED);28282829/* Allowed only on MIDI channel enabled */2830FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);28312832if(synth->verbose)2833{2834FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val);2835}28362837fluid_channel_set_key_pressure(synth->channel[chan], key, val);2838result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key);28392840FLUID_API_RETURN(result);2841}28422843/* Updates key pressure from within synthesis thread */2844static int2845fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key)2846{2847fluid_voice_t *voice;2848int i;2849int result = FLUID_OK;28502851for(i = 0; i < synth->polyphony; i++)2852{2853voice = synth->voice[i];28542855if(voice->chan == chan && voice->key == key)2856{2857result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE);28582859if(result != FLUID_OK)2860{2861return result;2862}2863}2864}28652866return result;2867}28682869/**2870* Set the MIDI pitch bend controller value on a MIDI channel.2871* @param synth FluidSynth instance2872* @param chan MIDI channel number (0 to MIDI channel count - 1)2873* @param val MIDI pitch bend value (0-16383 with 8192 being center)2874* @return #FLUID_OK on success, #FLUID_FAILED otherwise2875*/2876int2877fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val)2878{2879int result;2880fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED);2881FLUID_API_ENTRY_CHAN(FLUID_FAILED);28822883/* Allowed only on MIDI channel enabled */2884FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);28852886if(synth->verbose)2887{2888FLUID_LOG(FLUID_INFO, "pitchb\t\t%d\t%d", chan, val);2889}28902891fluid_channel_set_pitch_bend(synth->channel[chan], val);2892result = fluid_synth_update_pitch_bend_LOCAL(synth, chan);28932894FLUID_API_RETURN(result);2895}28962897/* Local synthesis thread variant of pitch bend */2898static int2899fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan)2900{2901return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL);2902}29032904/**2905* Get the MIDI pitch bend controller value on a MIDI channel.2906* @param synth FluidSynth instance2907* @param chan MIDI channel number (0 to MIDI channel count - 1)2908* @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with2909* 8192 being center)2910* @return #FLUID_OK on success, #FLUID_FAILED otherwise2911*/2912int2913fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend)2914{2915int result;2916fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED);2917FLUID_API_ENTRY_CHAN(FLUID_FAILED);29182919/* Allowed only on MIDI channel enabled */2920FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29212922*ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]);2923result = FLUID_OK;29242925FLUID_API_RETURN(result);2926}29272928/**2929* Set MIDI pitch wheel sensitivity on a MIDI channel.2930* @param synth FluidSynth instance2931* @param chan MIDI channel number (0 to MIDI channel count - 1)2932* @param val Pitch wheel sensitivity value in semitones2933* @return #FLUID_OK on success, #FLUID_FAILED otherwise2934*/2935int2936fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val)2937{2938int result;2939fluid_return_val_if_fail(val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */2940FLUID_API_ENTRY_CHAN(FLUID_FAILED);29412942/* Allowed only on MIDI channel enabled */2943FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29442945if(synth->verbose)2946{2947FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val);2948}29492950fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val);2951result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan);29522953FLUID_API_RETURN(result);2954}29552956/* Local synthesis thread variant of set pitch wheel sensitivity */2957static int2958fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan)2959{2960return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS);2961}29622963/**2964* Get MIDI pitch wheel sensitivity on a MIDI channel.2965* @param synth FluidSynth instance2966* @param chan MIDI channel number (0 to MIDI channel count - 1)2967* @param pval Location to store pitch wheel sensitivity value in semitones2968* @return #FLUID_OK on success, #FLUID_FAILED otherwise2969* @since Sometime AFTER v1.0 API freeze.2970*/2971int2972fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval)2973{2974int result;2975fluid_return_val_if_fail(pval != NULL, FLUID_FAILED);2976FLUID_API_ENTRY_CHAN(FLUID_FAILED);29772978/* Allowed only on MIDI channel enabled */2979FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);29802981*pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]);2982result = FLUID_OK;29832984FLUID_API_RETURN(result);2985}29862987/**2988* Assign a preset to a MIDI channel.2989* @param synth FluidSynth instance2990* @param chan MIDI channel number (0 to MIDI channel count - 1)2991* @param preset Preset to assign to channel or NULL to clear (ownership is taken over)2992* @return #FLUID_OK on success, #FLUID_FAILED otherwise2993*/2994static int2995fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset)2996{2997fluid_channel_t *channel;29982999fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3000fluid_return_val_if_fail(chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);30013002channel = synth->channel[chan];30033004return fluid_channel_set_preset(channel, preset);3005}30063007/* Get a preset by SoundFont, bank and program numbers.3008* Returns preset pointer or NULL.3009*/3010static fluid_preset_t *3011fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum,3012int banknum, int prognum)3013{3014fluid_sfont_t *sfont;3015fluid_list_t *list;30163017/* 128 indicates an "unset" operation" */3018if(prognum == FLUID_UNSET_PROGRAM)3019{3020return NULL;3021}30223023for(list = synth->sfont; list; list = fluid_list_next(list))3024{3025sfont = fluid_list_get(list);30263027if(fluid_sfont_get_id(sfont) == sfontnum)3028{3029return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);3030}3031}30323033return NULL;3034}30353036/* Get a preset by SoundFont name, bank and program.3037* Returns preset pointer or NULL.3038*/3039static fluid_preset_t *3040fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname,3041int banknum, int prognum)3042{3043fluid_sfont_t *sfont;3044fluid_list_t *list;30453046for(list = synth->sfont; list; list = fluid_list_next(list))3047{3048sfont = fluid_list_get(list);30493050if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0)3051{3052return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);3053}3054}30553056return NULL;3057}30583059/* Find a preset by bank and program numbers.3060* Returns preset pointer or NULL.3061*/3062fluid_preset_t *3063fluid_synth_find_preset(fluid_synth_t *synth, int banknum,3064int prognum)3065{3066fluid_preset_t *preset;3067fluid_sfont_t *sfont;3068fluid_list_t *list;30693070for(list = synth->sfont; list; list = fluid_list_next(list))3071{3072sfont = fluid_list_get(list);30733074preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);30753076if(preset)3077{3078return preset;3079}3080}30813082return NULL;3083}30843085/**3086* Send a program change event on a MIDI channel.3087* @param synth FluidSynth instance3088* @param chan MIDI channel number (0 to MIDI channel count - 1)3089* @param prognum MIDI program number (0-127)3090* @return #FLUID_OK on success, #FLUID_FAILED otherwise3091*/3092/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented3093* since fluid_synth_unset_program() should be used instead. */3094int3095fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum)3096{3097fluid_preset_t *preset = NULL;3098fluid_channel_t *channel;3099int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED;31003101fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED);3102FLUID_API_ENTRY_CHAN(FLUID_FAILED);31033104/* Allowed only on MIDI channel enabled */3105FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);31063107channel = synth->channel[chan];31083109if(channel->channel_type == CHANNEL_TYPE_DRUM)3110{3111banknum = DRUM_INST_BANK;3112}3113else3114{3115fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL);3116}31173118if(synth->verbose)3119{3120FLUID_LOG(FLUID_INFO, "prog\t\t%d\t%d\t%d", chan, banknum, prognum);3121}31223123/* I think this is a hack for MIDI files that do bank changes in GM mode.3124* Proper way to handle this would probably be to ignore bank changes when in3125* GM mode. - JG3126* This is now possible by setting synth.midi-bank-select=gm, but let the hack3127* stay for the time being. - DH3128*/3129if(prognum != FLUID_UNSET_PROGRAM)3130{3131subst_bank = banknum;3132subst_prog = prognum;31333134preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);31353136/* Fallback to another preset if not found */3137if(!preset)3138{3139/* Percussion: Fallback to preset 0 in percussion bank */3140if(channel->channel_type == CHANNEL_TYPE_DRUM)3141{3142subst_prog = 0;3143subst_bank = DRUM_INST_BANK;3144preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);3145}3146/* Melodic instrument */3147else3148{3149/* Fallback first to bank 0:prognum */3150subst_bank = 0;3151preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);31523153/* Fallback to first preset in bank 0 (usually piano...) */3154if(!preset)3155{3156subst_prog = 0;3157preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);3158}3159}31603161if(preset)3162{3163FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]",3164chan, banknum, prognum, subst_bank, subst_prog);3165}3166else3167{3168FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum);3169}3170}3171}31723173/* Assign the SoundFont ID and program number to the channel */3174fluid_channel_set_sfont_bank_prog(channel, preset ? fluid_sfont_get_id(preset->sfont) : 0,3175-1, prognum);3176result = fluid_synth_set_preset(synth, chan, preset);31773178FLUID_API_RETURN(result);3179}31803181/**3182* Set instrument bank number on a MIDI channel.3183* @param synth FluidSynth instance3184* @param chan MIDI channel number (0 to MIDI channel count - 1)3185* @param bank MIDI bank number3186* @return #FLUID_OK on success, #FLUID_FAILED otherwise3187* @note This function does not change the instrument currently assigned to \c chan,3188* as it is usually called prior to fluid_synth_program_change(). If you still want3189* instrument changes to take effect immediately, call fluid_synth_program_reset()3190* after having set up the bank configuration.3191*3192*/3193int3194fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank)3195{3196int result;3197fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED);3198fluid_return_val_if_fail(bank >= 0, FLUID_FAILED);3199FLUID_API_ENTRY_CHAN(FLUID_FAILED);32003201/* Allowed only on MIDI channel enabled */3202FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);32033204fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1);3205result = FLUID_OK;32063207FLUID_API_RETURN(result);3208}32093210/**3211* Set SoundFont ID on a MIDI channel.3212* @param synth FluidSynth instance3213* @param chan MIDI channel number (0 to MIDI channel count - 1)3214* @param sfont_id ID of a loaded SoundFont3215* @return #FLUID_OK on success, #FLUID_FAILED otherwise3216* @note This function does not change the instrument currently assigned to \c chan,3217* as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change().3218* If you still want instrument changes to take effect immediately, call fluid_synth_program_reset()3219* after having selected the soundfont.3220*/3221int3222fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id)3223{3224int result;3225FLUID_API_ENTRY_CHAN(FLUID_FAILED);32263227/* Allowed only on MIDI channel enabled */3228FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);32293230fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);3231result = FLUID_OK;32323233FLUID_API_RETURN(result);3234}32353236/**3237* Set the preset of a MIDI channel to an unassigned state.3238* @param synth FluidSynth instance3239* @param chan MIDI channel number (0 to MIDI channel count - 1)3240* @return #FLUID_OK on success, #FLUID_FAILED otherwise3241* @since 1.1.13242*3243* @note Channel retains its SoundFont ID and bank numbers, while the program3244* number is set to an "unset" state. MIDI program changes may re-assign a3245* preset if one matches.3246*/3247int3248fluid_synth_unset_program(fluid_synth_t *synth, int chan)3249{3250FLUID_API_ENTRY_CHAN(FLUID_FAILED);3251FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM));3252}32533254/**3255* Get current SoundFont ID, bank number and program number for a MIDI channel.3256* @param synth FluidSynth instance3257* @param chan MIDI channel number (0 to MIDI channel count - 1)3258* @param sfont_id Location to store SoundFont ID3259* @param bank_num Location to store MIDI bank number3260* @param preset_num Location to store MIDI program number3261* @return #FLUID_OK on success, #FLUID_FAILED otherwise3262*/3263int3264fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id,3265int *bank_num, int *preset_num)3266{3267int result;3268fluid_channel_t *channel;32693270fluid_return_val_if_fail(sfont_id != NULL, FLUID_FAILED);3271fluid_return_val_if_fail(bank_num != NULL, FLUID_FAILED);3272fluid_return_val_if_fail(preset_num != NULL, FLUID_FAILED);3273FLUID_API_ENTRY_CHAN(FLUID_FAILED);32743275/* Allowed only on MIDI channel enabled */3276FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);32773278channel = synth->channel[chan];3279fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, preset_num);32803281/* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */3282if(*preset_num == FLUID_UNSET_PROGRAM)3283{3284*preset_num = 0;3285}32863287result = FLUID_OK;32883289FLUID_API_RETURN(result);3290}32913292/**3293* Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers.3294* @param synth FluidSynth instance3295* @param chan MIDI channel number (0 to MIDI channel count - 1)3296* @param sfont_id ID of a loaded SoundFont3297* @param bank_num MIDI bank number3298* @param preset_num MIDI program number3299* @return #FLUID_OK on success, #FLUID_FAILED otherwise3300*/3301int3302fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id,3303int bank_num, int preset_num)3304{3305fluid_preset_t *preset = NULL;3306fluid_channel_t *channel;3307int result;3308fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3309fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);33103311FLUID_API_ENTRY_CHAN(FLUID_FAILED);33123313/* Allowed only on MIDI channel enabled */3314FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);33153316channel = synth->channel[chan];33173318preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);33193320if(preset == NULL)3321{3322FLUID_LOG(FLUID_ERR,3323"There is no preset with bank number %d and preset number %d in SoundFont %d",3324bank_num, preset_num, sfont_id);3325FLUID_API_RETURN(FLUID_FAILED);3326}33273328/* Assign the new SoundFont ID, bank and program number to the channel */3329fluid_channel_set_sfont_bank_prog(channel, sfont_id, bank_num, preset_num);3330result = fluid_synth_set_preset(synth, chan, preset);33313332FLUID_API_RETURN(result);3333}33343335/**3336* Pins all samples of the given preset.3337*3338* @param synth FluidSynth instance3339* @param sfont_id ID of a loaded SoundFont3340* @param bank_num MIDI bank number3341* @param preset_num MIDI program number3342* @return #FLUID_OK if the preset was found, pinned and loaded3343* into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK3344* is returned, even if <code>synth.dynamic-sample-loading</code> is disabled or3345* the preset doesn't support dynamic-sample-loading.3346*3347* This function will attempt to pin all samples of the given preset and3348* load them into memory, if they are currently unloaded. "To pin" in this3349* context means preventing them from being unloaded by an upcoming channel3350* prog change.3351*3352* @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled.3353* By default, dynamic-sample-loading is disabled and all samples are kept in memory.3354* Furthermore, this is only useful for presets which support dynamic-sample-loading (currently,3355* only preset loaded with the default soundfont loader do).3356*3357* @since 2.2.03358*/3359int3360fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num)3361{3362int ret;3363fluid_preset_t *preset;33643365fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3366fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3367fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);33683369fluid_synth_api_enter(synth);33703371preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);33723373if(preset == NULL)3374{3375FLUID_LOG(FLUID_ERR,3376"There is no preset with bank number %d and preset number %d in SoundFont %d",3377bank_num, preset_num, sfont_id);3378FLUID_API_RETURN(FLUID_FAILED);3379}33803381ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages33823383FLUID_API_RETURN(ret);3384}33853386/**3387* Unpin all samples of the given preset.3388*3389* @param synth FluidSynth instance3390* @param sfont_id ID of a loaded SoundFont3391* @param bank_num MIDI bank number3392* @param preset_num MIDI program number3393* @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise3394*3395* This function undoes the effect of fluid_synth_pin_preset(). If the preset is3396* not currently used, its samples will be unloaded.3397*3398* @note Only useful for presets loaded with the default soundfont loader and3399* only if \ref settings_synth_dynamic-sample-loading is enabled.3400*3401* @since 2.2.03402*/3403int3404fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num)3405{3406int ret;3407fluid_preset_t *preset;34083409fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3410fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);3411fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);34123413fluid_synth_api_enter(synth);34143415preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num);34163417if(preset == NULL)3418{3419FLUID_LOG(FLUID_ERR,3420"There is no preset with bank number %d and preset number %d in SoundFont %d",3421bank_num, preset_num, sfont_id);3422FLUID_API_RETURN(FLUID_FAILED);3423}34243425ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages34263427FLUID_API_RETURN(ret);3428}34293430/**3431* Select an instrument on a MIDI channel by SoundFont name, bank and program numbers.3432* @param synth FluidSynth instance3433* @param chan MIDI channel number (0 to MIDI channel count - 1)3434* @param sfont_name Name of a loaded SoundFont3435* @param bank_num MIDI bank number3436* @param preset_num MIDI program number3437* @return #FLUID_OK on success, #FLUID_FAILED otherwise3438* @since 1.1.03439*/3440int3441fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan,3442const char *sfont_name, int bank_num,3443int preset_num)3444{3445fluid_preset_t *preset = NULL;3446fluid_channel_t *channel;3447int result;3448fluid_return_val_if_fail(sfont_name != NULL, FLUID_FAILED);3449FLUID_API_ENTRY_CHAN(FLUID_FAILED);34503451/* Allowed only on MIDI channel enabled */3452FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);34533454channel = synth->channel[chan];34553456preset = fluid_synth_get_preset_by_sfont_name(synth, sfont_name, bank_num,3457preset_num);34583459if(preset == NULL)3460{3461FLUID_LOG(FLUID_ERR,3462"There is no preset with bank number %d and preset number %d in SoundFont %s",3463bank_num, preset_num, sfont_name);3464FLUID_API_RETURN(FLUID_FAILED);3465}34663467/* Assign the new SoundFont ID, bank and program number to the channel */3468fluid_channel_set_sfont_bank_prog(channel, fluid_sfont_get_id(preset->sfont),3469bank_num, preset_num);3470result = fluid_synth_set_preset(synth, chan, preset);34713472FLUID_API_RETURN(result);3473}34743475/*3476* This function assures that every MIDI channel has a valid preset3477* (NULL is okay). This function is called after a SoundFont is3478* unloaded or reloaded.3479*/3480static void3481fluid_synth_update_presets(fluid_synth_t *synth)3482{3483fluid_channel_t *channel;3484fluid_preset_t *preset;3485int sfont, bank, prog;3486int chan;34873488for(chan = 0; chan < synth->midi_channels; chan++)3489{3490channel = synth->channel[chan];3491fluid_channel_get_sfont_bank_prog(channel, &sfont, &bank, &prog);3492preset = fluid_synth_get_preset(synth, sfont, bank, prog);3493fluid_synth_set_preset(synth, chan, preset);3494}3495}34963497static void3498fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate)3499{3500int i;3501fluid_clip(sample_rate, 8000.0f, 96000.0f);3502synth->sample_rate = sample_rate;35033504synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth);35053506for(i = 0; i < synth->polyphony; i++)3507{3508fluid_voice_set_output_rate(synth->voice[i], sample_rate);3509}3510}35113512/**3513* Set up an event to change the sample-rate of the synth during the next rendering call.3514* @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth.3515* @deprecated As of fluidsynth 2.1.0 this function has been deprecated.3516* 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).3517* The sample-rate change may also require memory allocation deep down in the effect units.3518* 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.3519* This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block.3520* 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.3521* Long story short: don't use it.3522* @code{.cpp}3523fluid_synth_t* synth; // assume initialized3524// [...]3525// sample-rate change needed? Delete the audio driver, if any.3526delete_fluid_audio_driver(adriver);3527// then delete the synth3528delete_fluid_synth(synth);3529// update the sample-rate3530fluid_settings_setnum(settings, "synth.sample-rate", 22050.0);3531// and re-create objects3532synth = new_fluid_synth(settings);3533adriver = new_fluid_audio_driver(settings, synth);3534* @endcode3535* @param synth FluidSynth instance3536* @param sample_rate New sample-rate (Hz)3537* @since 1.1.23538*/3539void3540fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate)3541{3542fluid_return_if_fail(synth != NULL);3543fluid_synth_api_enter(synth);35443545fluid_synth_set_sample_rate_LOCAL(synth, sample_rate);35463547fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate,35480, synth->sample_rate);3549fluid_synth_api_exit(synth);3550}35513552// internal sample rate change function for the jack driver3553// executes immediately, therefore, make sure no rendering call is running!3554void3555fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate)3556{3557fluid_rvoice_param_t param[MAX_EVENT_PARAMS];3558fluid_return_if_fail(synth != NULL);3559fluid_synth_api_enter(synth);35603561fluid_synth_set_sample_rate_LOCAL(synth, sample_rate);35623563param[0].i = 0;3564param[1].real = synth->sample_rate;3565fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param);35663567fluid_synth_api_exit(synth);3568}356935703571/* Handler for synth.gain setting. */3572static void3573fluid_synth_handle_gain(void *data, const char *name, double value)3574{3575fluid_synth_t *synth = (fluid_synth_t *)data;3576fluid_synth_set_gain(synth, (float) value);3577}35783579/**3580* Set synth output gain value.3581* @param synth FluidSynth instance3582* @param gain Gain value (function clamps value to the range 0.0 to 10.0)3583*/3584void3585fluid_synth_set_gain(fluid_synth_t *synth, float gain)3586{3587fluid_return_if_fail(synth != NULL);3588fluid_synth_api_enter(synth);35893590fluid_clip(gain, 0.0f, 10.0f);35913592synth->gain = gain;3593fluid_synth_update_gain_LOCAL(synth);3594fluid_synth_api_exit(synth);3595}35963597/* Called by synthesis thread to update the gain in all voices */3598static void3599fluid_synth_update_gain_LOCAL(fluid_synth_t *synth)3600{3601fluid_voice_t *voice;3602float gain;3603int i;36043605gain = synth->gain;36063607for(i = 0; i < synth->polyphony; i++)3608{3609voice = synth->voice[i];36103611if(fluid_voice_is_playing(voice))3612{3613fluid_voice_set_gain(voice, gain);3614}3615}3616}36173618/**3619* Get synth output gain value.3620* @param synth FluidSynth instance3621* @return Synth gain value (0.0 to 10.0)3622*/3623float3624fluid_synth_get_gain(fluid_synth_t *synth)3625{3626float result;3627fluid_return_val_if_fail(synth != NULL, 0.0);3628fluid_synth_api_enter(synth);36293630result = synth->gain;3631FLUID_API_RETURN(result);3632}36333634/*3635* Handler for synth.polyphony setting.3636*/3637static void3638fluid_synth_handle_polyphony(void *data, const char *name, int value)3639{3640fluid_synth_t *synth = (fluid_synth_t *)data;3641fluid_synth_set_polyphony(synth, value);3642}36433644/**3645* Set synthesizer polyphony (max number of voices).3646* @param synth FluidSynth instance3647* @param polyphony Polyphony to assign3648* @return #FLUID_OK on success, #FLUID_FAILED otherwise3649* @since 1.0.63650*/3651int3652fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony)3653{3654int result;3655fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3656fluid_return_val_if_fail(polyphony >= 1 && polyphony <= 65535, FLUID_FAILED);3657fluid_synth_api_enter(synth);36583659result = fluid_synth_update_polyphony_LOCAL(synth, polyphony);36603661FLUID_API_RETURN(result);3662}36633664/* Called by synthesis thread to update the polyphony value */3665static int3666fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony)3667{3668fluid_voice_t *voice;3669int i;36703671if(new_polyphony > synth->nvoice)3672{3673/* Create more voices */3674fluid_voice_t **new_voices = FLUID_REALLOC(synth->voice,3675sizeof(fluid_voice_t *) * new_polyphony);36763677if(new_voices == NULL)3678{3679return FLUID_FAILED;3680}36813682synth->voice = new_voices;36833684for(i = synth->nvoice; i < new_polyphony; i++)3685{3686synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate);36873688if(synth->voice[i] == NULL)3689{3690return FLUID_FAILED;3691}36923693fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags);3694}36953696synth->nvoice = new_polyphony;3697}36983699synth->polyphony = new_polyphony;37003701/* turn off any voices above the new limit */3702for(i = synth->polyphony; i < synth->nvoice; i++)3703{3704voice = synth->voice[i];37053706if(fluid_voice_is_playing(voice))3707{3708fluid_voice_off(voice);3709}3710}37113712fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,3713synth->polyphony, 0.0f);37143715return FLUID_OK;3716}37173718/**3719* Get current synthesizer polyphony (max number of voices).3720* @param synth FluidSynth instance3721* @return Synth polyphony value.3722* @since 1.0.63723*/3724int3725fluid_synth_get_polyphony(fluid_synth_t *synth)3726{3727int result;3728fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3729fluid_synth_api_enter(synth);37303731result = synth->polyphony;3732FLUID_API_RETURN(result);3733}37343735/**3736* @brief Get current number of active voices.3737*3738* I.e. the no. of voices that have been3739* started and have not yet finished. Unless called from synthesis context,3740* this number does not necessarily have to be equal to the number of voices3741* currently processed by the DSP loop, see below.3742* @param synth FluidSynth instance3743* @return Number of currently active voices.3744* @since 1.1.03745*3746* @note To generate accurate continuous statistics of the voice count, caller3747* should ensure this function is called synchronously with the audio synthesis3748* process. This can be done in the new_fluid_audio_driver2() audio callback3749* function for example. Otherwise every call to this function may return different3750* voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by3751* e.g. an audio driver or the applications audio rendering thread.3752*/3753int3754fluid_synth_get_active_voice_count(fluid_synth_t *synth)3755{3756int result;3757fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3758fluid_synth_api_enter(synth);37593760result = synth->active_voice_count;3761FLUID_API_RETURN(result);3762}37633764/**3765* Get the internal synthesis buffer size value.3766* @param synth FluidSynth instance3767* @return Internal buffer size in audio frames.3768*3769* Audio is synthesized at this number of frames at a time. Defaults to 64 frames. I.e. the synth can only react to notes,3770* control changes, and other audio affecting events after having processed 64 audio frames.3771*/3772int3773fluid_synth_get_internal_bufsize(fluid_synth_t *synth)3774{3775return FLUID_BUFSIZE;3776}37773778/**3779* Resend a bank select and a program change for every channel and assign corresponding instruments.3780* @param synth FluidSynth instance3781* @return #FLUID_OK on success, #FLUID_FAILED otherwise3782*3783* This function is called mainly after a SoundFont has been loaded,3784* unloaded or reloaded.3785*/3786int3787fluid_synth_program_reset(fluid_synth_t *synth)3788{3789int i, prog;3790fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3791fluid_synth_api_enter(synth);37923793/* try to set the correct presets */3794for(i = 0; i < synth->midi_channels; i++)3795{3796fluid_channel_get_sfont_bank_prog(synth->channel[i], NULL, NULL, &prog);3797fluid_synth_program_change(synth, i, prog);3798}37993800FLUID_API_RETURN(FLUID_OK);3801}38023803/**3804* Synthesize a block of floating point audio to separate audio buffers (multi-channel rendering).3805*3806* @param synth FluidSynth instance3807* @param len Count of audio frames to synthesize3808* @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)3809* @param right Array of float buffers to store right channel of planar audio (size: dito)3810* @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)3811* @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito)3812* @return #FLUID_OK on success, #FLUID_FAILED otherwise3813*3814* First effect channel used by reverb, second for chorus.3815*3816* @note Should only be called from synthesis thread.3817*3818* @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release.3819* It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more3820* powerful and flexible fluid_synth_process().3821*3822* Usage example:3823* @code{.cpp}3824const int FramesToRender = 64;3825int channels;3826// retrieve number of stereo audio channels3827fluid_settings_getint(settings, "synth.audio-channels", &channels);38283829// we need twice as many (mono-)buffers3830channels *= 2;38313832// fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16:3833// each midi channel gets rendered to its own stereo buffer, rather than having3834// one buffer and interleaved PCM3835float** mix_buf = new float*[channels];3836for(int i = 0; i < channels; i++)3837{3838mix_buf[i] = new float[FramesToRender];3839}38403841// retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan)3842// and chrous (second chan))3843fluid_settings_getint(settings, "synth.effects-channels", &channels);3844channels *= 2;38453846float** fx_buf = new float*[channels];3847for(int i = 0; i < channels; i++)3848{3849fx_buf[i] = new float[FramesToRender];3850}38513852float** mix_buf_l = mix_buf;3853float** mix_buf_r = &mix_buf[channels/2];38543855float** fx_buf_l = fx_buf;3856float** fx_buf_r = &fx_buf[channels/2];38573858fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r)3859* @endcode3860*/3861int3862fluid_synth_nwrite_float(fluid_synth_t *synth, int len,3863float **left, float **right,3864float **fx_left, float **fx_right)3865{3866fluid_real_t *left_in, *fx_left_in;3867fluid_real_t *right_in, *fx_right_in;3868double time = fluid_utime();3869int i, num, available, count;3870#ifdef WITH_FLOAT3871int bytes;3872#endif3873float cpu_load;38743875fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);3876fluid_return_val_if_fail(left != NULL, FLUID_FAILED);3877fluid_return_val_if_fail(right != NULL, FLUID_FAILED);3878fluid_return_val_if_fail(len >= 0, FLUID_FAILED);3879fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below38803881/* First, take what's still available in the buffer */3882count = 0;3883num = synth->cur;38843885if(synth->cur < FLUID_BUFSIZE)3886{3887available = FLUID_BUFSIZE - synth->cur;3888fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);3889fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);38903891num = (available > len) ? len : available;3892#ifdef WITH_FLOAT3893bytes = num * sizeof(float);3894#endif38953896for(i = 0; i < synth->audio_channels; i++)3897{3898#ifdef WITH_FLOAT3899FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3900FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3901#else //WITH_FLOAT3902int j;39033904for(j = 0; j < num; j++)3905{3906left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3907right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3908}39093910#endif //WITH_FLOAT3911}39123913for(i = 0; i < synth->effects_channels; i++)3914{3915#ifdef WITH_FLOAT39163917if(fx_left != NULL)3918{3919FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3920}39213922if(fx_right != NULL)3923{3924FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);3925}39263927#else //WITH_FLOAT3928int j;39293930if(fx_left != NULL)3931{3932for(j = 0; j < num; j++)3933{3934fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3935}3936}39373938if(fx_right != NULL)3939{3940for(j = 0; j < num; j++)3941{3942fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];3943}3944}39453946#endif //WITH_FLOAT3947}39483949count += num;3950num += synth->cur; /* if we're now done, num becomes the new synth->cur below */3951}39523953/* Then, run one_block() and copy till we have 'len' samples */3954while(count < len)3955{3956fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0);3957fluid_synth_render_blocks(synth, 1); // TODO:3958fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);3959fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);39603961num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE;3962#ifdef WITH_FLOAT3963bytes = num * sizeof(float);3964#endif39653966for(i = 0; i < synth->audio_channels; i++)3967{3968#ifdef WITH_FLOAT3969FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3970FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3971#else //WITH_FLOAT3972int j;39733974for(j = 0; j < num; j++)3975{3976left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];3977right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];3978}39793980#endif //WITH_FLOAT3981}39823983for(i = 0; i < synth->effects_channels; i++)3984{3985#ifdef WITH_FLOAT39863987if(fx_left != NULL)3988{3989FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3990}39913992if(fx_right != NULL)3993{3994FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);3995}39963997#else //WITH_FLOAT3998int j;39994000if(fx_left != NULL)4001{4002for(j = 0; j < num; j++)4003{4004fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];4005}4006}40074008if(fx_right != NULL)4009{4010for(j = 0; j < num; j++)4011{4012fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];4013}4014}40154016#endif //WITH_FLOAT4017}40184019count += num;4020}40214022synth->cur = num;40234024time = fluid_utime() - time;4025cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4026fluid_atomic_float_set(&synth->cpu_load, cpu_load);40274028return FLUID_OK;4029}40304031/**4032* mixes the samples of \p in to \p out4033*4034* @param out the output sample buffer to mix to4035* @param ooff sample offset in \p out4036* @param in the rvoice_mixer input sample buffer to mix from4037* @param ioff sample offset in \p in4038* @param buf_idx the sample buffer index of \p in to mix from4039* @param num number of samples to mix4040*/4041static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out,4042int ooff,4043const fluid_real_t *FLUID_RESTRICT in,4044int ioff,4045int buf_idx,4046int num)4047{4048if(out != NULL)4049{4050int j;40514052for(j = 0; j < num; j++)4053{4054out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];4055}4056}4057}40584059/**4060* Synthesize floating point audio to stereo audio channels4061* (implements the default interface #fluid_audio_func_t).4062*4063* @param synth FluidSynth instance4064*4065* @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx.4066* Zero value is permitted, the function does nothing and return FLUID_OK.4067*4068* @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo)4069* and in the range <code>0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.4070* Note that zero value is valid and allows to skip mixing effects in all fx output buffers.4071*4072* @param fx Array of buffers to store effects audio to. Buffers may4073* alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer.4074*4075* @param nout Count of arrays in \c out. Must be a multiple of 24076* (because of stereo) and in the range <code>0 <= nout/2 <= fluid_synth_count_audio_channels()</code>.4077* Note that zero value is valid and allows to skip mixing dry audio in all out output buffers.4078*4079* @param out Array of buffers to store (dry) audio to. Buffers may4080* alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer.4081*4082* @return #FLUID_OK on success,4083* #FLUID_FAILED otherwise,4084* - <code>fx == NULL</code> while <code>nfx > 0</code>, or <code>out == NULL</code> while <code>nout > 0</code>.4085* - \c nfx or \c nout not multiple of 2.4086* - <code>len < 0</code>.4087* - \c nfx or \c nout exceed the range explained above.4088*4089* Synthesize and <strong>mix</strong> audio to a given number of planar audio buffers.4090* Therefore pass <code>nout = N*2</code> float buffers to \p out in order to render4091* the synthesized audio to \p N stereo channels. Each float buffer must be4092* able to hold \p len elements.4093*4094* \p out contains an array of planar buffers for normal, dry, stereo4095* audio (alternating left and right). Like:4096@code{.cpp}4097out[0] = left_buffer_audio_channel_04098out[1] = right_buffer_audio_channel_04099out[2] = left_buffer_audio_channel_14100out[3] = right_buffer_audio_channel_14101...4102out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i4103out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i4104@endcode4105*4106* for zero-based channel index \p i.4107* The buffer layout of \p fx used for storing effects4108* like reverb and chorus looks similar:4109@code{.cpp}4110fx[0] = left_buffer_channel_of_reverb_unit_04111fx[1] = right_buffer_channel_of_reverb_unit_04112fx[2] = left_buffer_channel_of_chorus_unit_04113fx[3] = right_buffer_channel_of_chorus_unit_04114fx[4] = left_buffer_channel_of_reverb_unit_14115fx[5] = right_buffer_channel_of_reverb_unit_14116fx[6] = left_buffer_channel_of_chorus_unit_14117fx[7] = right_buffer_channel_of_chorus_unit_14118fx[8] = left_buffer_channel_of_reverb_unit_24119...4120fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k4121fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k4122@endcode4123* where <code>0 <= k < fluid_synth_count_effects_groups()</code> is a zero-based index denoting the effects unit and4124* <code>0 <= j < fluid_synth_count_effects_channels()</code> is a zero-based index denoting the effect channel within4125* unit \p k.4126*4127* Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the4128* zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is4129* going to be rendered to use:4130*4131* <code>i = chan % fluid_synth_count_audio_groups()</code>4132*4133* <code>k = chan % fluid_synth_count_effects_groups()</code>4134*4135* @parblock4136* @note The owner of the sample buffers must zero them out before calling this4137* function, because any synthesized audio is mixed (i.e. added) to the buffers.4138* E.g. if fluid_synth_process() is called from a custom audio driver process function4139* (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers.4140* @endparblock4141*4142* @parblock4143* @note No matter how many buffers you pass in, fluid_synth_process()4144* will always render all audio channels to the4145* buffers in \c out and all effects channels to the4146* buffers in \c fx, provided that <code>nout > 0</code> and <code>nfx > 0</code> respectively. If4147* <code>nout/2 < fluid_synth_count_audio_channels()</code> it will wrap around. Same4148* is true for effects audio if <code>nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.4149* See usage examples below.4150* @endparblock4151*4152* @parblock4153* @note Should only be called from synthesis thread.4154* @endparblock4155*/4156int4157fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],4158int nout, float *out[])4159{4160return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks);4161}41624163/* declared public (instead of static) for testing purpose */4164int4165fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],4166int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int))4167{4168fluid_real_t *left_in, *fx_left_in;4169fluid_real_t *right_in, *fx_right_in;4170int nfxchan, nfxunits, naudchan;41714172double time = fluid_utime();4173int i, f, num, count, buffered_blocks;41744175float cpu_load;41764177fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);41784179/* fx NULL while nfx > 0 is invalid */4180fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED);4181/* nfx must be multiple of 2. Note that 0 value is valid and4182allows to skip mixing in fx output buffers4183*/4184fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);41854186/* out NULL while nout > 0 is invalid */4187fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED);4188/* nout must be multiple of 2. Note that 0 value is valid and4189allows to skip mixing in out output buffers4190*/4191fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);41924193/* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK.4194*/4195fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4196fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below41974198nfxchan = synth->effects_channels;4199nfxunits = synth->effects_groups;4200naudchan = synth->audio_channels;42014202fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED);4203fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED);42044205/* get internal mixer audio dry buffer's pointer (left and right channel) */4206fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4207/* get internal mixer audio effect buffer's pointer (left and right channel) */4208fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);42094210/* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one4211stereo output) we don't want rendered audio effect mixed in internal audio dry buffers.4212FALSE instructs the mixer that internal audio effects will be mixed in respective internal4213audio effects buffers.4214*/4215fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE);421642174218/* First, take what's still available in the buffer */4219count = 0;4220/* synth->cur indicates if available samples are still in internal mixer buffer */4221num = synth->cur;42224223buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4224if(synth->cur < buffered_blocks * FLUID_BUFSIZE)4225{4226/* yes, available sample are in internal mixer buffer */4227int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur;4228num = (available > len) ? len : available;42294230/* mixing dry samples (or skip if requested by the caller) */4231if(nout != 0)4232{4233for(i = 0; i < naudchan; i++)4234{4235/* mix num left samples from input mixer buffer (left_in) at input offset4236synth->cur to output buffer (out_buf) at offset 0 */4237float *out_buf = out[(i * 2) % nout];4238fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num);42394240/* mix num right samples from input mixer buffer (right_in) at input offset4241synth->cur to output buffer (out_buf) at offset 0 */4242out_buf = out[(i * 2 + 1) % nout];4243fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num);4244}4245}42464247/* mixing effects samples (or skip if requested by the caller) */4248if(nfx != 0)4249{4250// loop over all effects units4251for(f = 0; f < nfxunits; f++)4252{4253// write out all effects (i.e. reverb and chorus)4254for(i = 0; i < nfxchan; i++)4255{4256int buf_idx = f * nfxchan + i;42574258/* mix num left samples from input mixer buffer (fx_left_in) at input offset4259synth->cur to output buffer (out_buf) at offset 0 */4260float *out_buf = fx[(buf_idx * 2) % nfx];4261fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);42624263/* mix num right samples from input mixer buffer (fx_right_in) at input offset4264synth->cur to output buffer (out_buf) at offset 0 */4265out_buf = fx[(buf_idx * 2 + 1) % nfx];4266fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num);4267}4268}4269}42704271count += num;4272num += synth->cur; /* if we're now done, num becomes the new synth->cur below */4273}42744275/* Then, render blocks and copy till we have 'len' samples */4276while(count < len)4277{4278/* always render full bloc multiple of FLUID_BUFSIZE */4279int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4280/* render audio (dry and effect) to respective internal dry and effect buffers */4281int blockcount = block_render_func(synth, blocksleft);42824283num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE;42844285/* mixing dry samples (or skip if requested by the caller) */4286if(nout != 0)4287{4288for(i = 0; i < naudchan; i++)4289{4290/* mix num left samples from input mixer buffer (left_in) at input offset42910 to output buffer (out_buf) at offset count */4292float *out_buf = out[(i * 2) % nout];4293fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num);42944295/* mix num right samples from input mixer buffer (right_in) at input offset42960 to output buffer (out_buf) at offset count */4297out_buf = out[(i * 2 + 1) % nout];4298fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num);4299}4300}43014302/* mixing effects samples (or skip if requested by the caller) */4303if(nfx != 0)4304{4305// loop over all effects units4306for(f = 0; f < nfxunits; f++)4307{4308// write out all effects (i.e. reverb and chorus)4309for(i = 0; i < nfxchan; i++)4310{4311int buf_idx = f * nfxchan + i;43124313/* mix num left samples from input mixer buffer (fx_left_in) at input offset43140 to output buffer (out_buf) at offset count */4315float *out_buf = fx[(buf_idx * 2) % nfx];4316fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);43174318/* mix num right samples from input mixer buffer (fx_right_in) at input offset43190 to output buffer (out_buf) at offset count */4320out_buf = fx[(buf_idx * 2 + 1) % nfx];4321fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);4322}4323}4324}43254326count += num;4327}43284329synth->cur = num;43304331time = fluid_utime() - time;4332cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4333fluid_atomic_float_set(&synth->cpu_load, cpu_load);43344335return FLUID_OK;4336}433743384339/**4340* Synthesize a block of floating point audio samples to audio buffers.4341* @param synth FluidSynth instance4342* @param len Count of audio frames to synthesize4343* @param lout Array of floats to store left channel of audio4344* @param loff Offset index in 'lout' for first sample4345* @param lincr Increment between samples stored to 'lout'4346* @param rout Array of floats to store right channel of audio4347* @param roff Offset index in 'rout' for first sample4348* @param rincr Increment between samples stored to 'rout'4349* @return #FLUID_OK on success, #FLUID_FAILED otherwise4350*4351* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,4352* lincr = 2, rincr = 2).4353*4354* @note Should only be called from synthesis thread.4355* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4356*/4357int4358fluid_synth_write_float(fluid_synth_t *synth, int len,4359void *lout, int loff, int lincr,4360void *rout, int roff, int rincr)4361{4362void *channels_out[2] = {lout, rout};4363int channels_off[2] = {loff, roff };4364int channels_incr[2] = {lincr, rincr };43654366return fluid_synth_write_float_channels(synth, len, 2, channels_out,4367channels_off, channels_incr);4368}43694370/**4371* Synthesize a block of float audio samples channels to audio buffers.4372* The function is convenient for audio driver to render multiple stereo4373* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).4374*4375* @param synth FluidSynth instance.4376* @param len Count of audio frames to synthesize.4377* @param channels_count Count of channels in a frame.4378* must be multiple of 2 and channel_count/2 must not exceed the number4379* of internal mixer buffers (synth->audio_groups)4380* @param channels_out Array of channels_count pointers on 16 bit words to4381* store sample channels. Modified on return.4382* @param channels_off Array of channels_count offset index to add to respective pointer4383* in channels_out for first sample.4384* @param channels_incr Array of channels_count increment between consecutive4385* samples channels.4386* @return #FLUID_OK on success, #FLUID_FAILED otherwise.4387*4388* Useful for storing:4389* - interleaved channels in a unique buffer.4390* - non interleaved channels in an unique buffer (or in distinct buffers).4391*4392* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)4393* in a unique buffer:4394* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,...4395* sn:c1, sn:c2, sn:c3, sn:c4 }.4396*4397* @note Should only be called from synthesis thread.4398* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4399*/4400int4401fluid_synth_write_float_channels(fluid_synth_t *synth, int len,4402int channels_count,4403void *channels_out[], int channels_off[],4404int channels_incr[])4405{4406return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count,4407channels_out, channels_off, channels_incr,4408fluid_synth_render_blocks);4409}44104411int4412fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len,4413int channels_count,4414void *channels_out[], int channels_off[],4415int channels_incr[],4416int (*block_render_func)(fluid_synth_t *, int))4417{4418float **chan_out = (float **)channels_out;4419int n, cur, size;44204421/* pointers on first input mixer buffer */4422fluid_real_t *left_in;4423fluid_real_t *right_in;4424int bufs_in_count; /* number of stereo input buffers */4425int i;44264427/* start average cpu load probe */4428double time = fluid_utime();4429float cpu_load;44304431/* start profiling duration probe (if profiling is enabled) */4432fluid_profile_ref_var(prof_ref);44334434/* check parameters */4435fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);44364437fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4438fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below44394440/* check for valid channel_count: must be multiple of 2 and4441channel_count/2 must not exceed the number of internal mixer buffers4442(synth->audio_groups)4443*/4444fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */4445&& channels_count >= 2, FLUID_FAILED);44464447bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */4448fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);44494450fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);4451fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);4452fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);44534454/* initialize output channels buffers on first sample position */4455i = channels_count;4456do4457{4458i--;4459chan_out[i] += channels_off[i];4460}4461while(i);44624463/* Conversely to fluid_synth_process(),4464we want rendered audio effect mixed in internal audio dry buffers.4465TRUE instructs the mixer that internal audio effects will be mixed in internal4466audio dry buffers.4467*/4468fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);44694470/* get first internal mixer audio dry buffer's pointer (left and right channel) */4471fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);44724473size = len;44744475/* synth->cur indicates if available samples are still in internal mixer buffer */4476cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */44774478do4479{4480/* fill up the buffers as needed */4481if(cur >= synth->curmax)4482{4483/* render audio (dry and effect) to internal dry buffers */4484/* always render full blocs multiple of FLUID_BUFSIZE */4485int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4486synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);44874488/* get first internal mixer audio dry buffer's pointer (left and right channel) */4489fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4490cur = 0;4491}44924493/* calculate amount of available samples */4494n = synth->curmax - cur;44954496/* keep track of emitted samples */4497if(n > size)4498{4499n = size;4500}45014502size -= n;45034504/* update pointers to current position */4505left_in += cur + n;4506right_in += cur + n;45074508/* set final cursor position */4509cur += n;45104511/* reverse index */4512n = 0 - n;45134514do4515{4516i = bufs_in_count;4517do4518{4519/* input sample index in stereo buffer i */4520int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;4521int c = i << 1; /* channel index c to write */45224523/* write left input sample to channel sample */4524*chan_out[c] = (float) left_in[in_idx];45254526/* write right input sample to next channel sample */4527*chan_out[c+1] = (float) right_in[in_idx];45284529/* advance output pointers */4530chan_out[c] += channels_incr[c];4531chan_out[c+1] += channels_incr[c+1];4532}4533while(i);4534}4535while(++n < 0);4536}4537while(size);45384539synth->cur = cur; /* save current sample position. It will be used on next call */45404541/* save average cpu load, use by API for real time cpu load meter */4542time = fluid_utime() - time;4543cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4544fluid_atomic_float_set(&synth->cpu_load, cpu_load);45454546/* stop duration probe and save performance measurement (if profiling is enabled) */4547fluid_profile_write(FLUID_PROF_WRITE, prof_ref,4548fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4549len);4550return FLUID_OK;4551}45524553/* for testing purpose */4554int4555fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,4556void *lout, int loff, int lincr,4557void *rout, int roff, int rincr,4558int (*block_render_func)(fluid_synth_t *, int)4559)4560{4561void *channels_out[2] = {lout, rout};4562int channels_off[2] = {loff, roff };4563int channels_incr[2] = {lincr, rincr };45644565return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out,4566channels_off, channels_incr,4567block_render_func);4568}456945704571#define DITHER_SIZE 480004572#define DITHER_CHANNELS 245734574static float rand_table[DITHER_CHANNELS][DITHER_SIZE];45754576/* Init dither table */4577static void4578init_dither(void)4579{4580float d, dp;4581int c, i;45824583for(c = 0; c < DITHER_CHANNELS; c++)4584{4585dp = 0;45864587for(i = 0; i < DITHER_SIZE - 1; i++)4588{4589d = rand() / (float)RAND_MAX - 0.5f;4590rand_table[c][i] = d - dp;4591dp = d;4592}45934594rand_table[c][DITHER_SIZE - 1] = 0 - dp;4595}4596}45974598/* A portable replacement for roundf(), seems it may actually be faster too! */4599static FLUID_INLINE int16_t4600round_clip_to_i16(float x)4601{4602long i;46034604if(x >= 0.0f)4605{4606i = (long)(x + 0.5f);46074608if(FLUID_UNLIKELY(i > 32767))4609{4610i = 32767;4611}4612}4613else4614{4615i = (long)(x - 0.5f);46164617if(FLUID_UNLIKELY(i < -32768))4618{4619i = -32768;4620}4621}46224623return (int16_t)i;4624}46254626/**4627* Synthesize a block of 16 bit audio samples to audio buffers.4628* @param synth FluidSynth instance4629* @param len Count of audio frames to synthesize4630* @param lout Array of 16 bit words to store left channel of audio4631* @param loff Offset index in 'lout' for first sample4632* @param lincr Increment between samples stored to 'lout'4633* @param rout Array of 16 bit words to store right channel of audio4634* @param roff Offset index in 'rout' for first sample4635* @param rincr Increment between samples stored to 'rout'4636* @return #FLUID_OK on success, #FLUID_FAILED otherwise4637*4638* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,4639* lincr = 2, rincr = 2).4640*4641* @note Should only be called from synthesis thread.4642* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4643* @note Dithering is performed when converting from internal floating point to4644* 16 bit audio.4645*/4646int4647fluid_synth_write_s16(fluid_synth_t *synth, int len,4648void *lout, int loff, int lincr,4649void *rout, int roff, int rincr)4650{4651void *channels_out[2] = {lout, rout};4652int channels_off[2] = {loff, roff };4653int channels_incr[2] = {lincr, rincr };46544655return fluid_synth_write_s16_channels(synth, len, 2, channels_out,4656channels_off, channels_incr);4657}46584659/**4660* Synthesize a block of 16 bit audio samples channels to audio buffers.4661* The function is convenient for audio driver to render multiple stereo4662* channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels).4663*4664* @param synth FluidSynth instance.4665* @param len Count of audio frames to synthesize.4666* @param channels_count Count of channels in a frame.4667* must be multiple of 2 and channel_count/2 must not exceed the number4668* of internal mixer buffers (synth->audio_groups)4669* @param channels_out Array of channels_count pointers on 16 bit words to4670* store sample channels. Modified on return.4671* @param channels_off Array of channels_count offset index to add to respective pointer4672* in channels_out for first sample.4673* @param channels_incr Array of channels_count increment between consecutive4674* samples channels.4675* @return #FLUID_OK on success, #FLUID_FAILED otherwise.4676*4677* Useful for storing:4678* - interleaved channels in a unique buffer.4679* - non interleaved channels in an unique buffer (or in distinct buffers).4680*4681* Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn)4682* in a unique buffer:4683* { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, ....4684* sn:c1, sn:c2, sn:c3, sn:c4 }.4685*4686* @note Should only be called from synthesis thread.4687* @note Reverb and Chorus are mixed to \c lout resp. \c rout.4688* @note Dithering is performed when converting from internal floating point to4689* 16 bit audio.4690*/4691int4692fluid_synth_write_s16_channels(fluid_synth_t *synth, int len,4693int channels_count,4694void *channels_out[], int channels_off[],4695int channels_incr[])4696{4697int16_t **chan_out = (int16_t **)channels_out;4698int di, n, cur, size;46994700/* pointers on first input mixer buffer */4701fluid_real_t *left_in;4702fluid_real_t *right_in;4703int bufs_in_count; /* number of stereo input buffers */4704int i;47054706/* start average cpu load probe */4707double time = fluid_utime();4708float cpu_load;47094710/* start profiling duration probe (if profiling is enabled) */4711fluid_profile_ref_var(prof_ref);47124713/* check parameters */4714fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);47154716fluid_return_val_if_fail(len >= 0, FLUID_FAILED);4717fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below47184719/* check for valid channel_count: must be multiple of 2 and4720channel_count/2 must not exceed the number of internal mixer buffers4721(synth->audio_groups)4722*/4723fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */4724&& channels_count >= 2, FLUID_FAILED);47254726bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */4727fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED);47284729fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED);4730fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED);4731fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED);47324733/* initialize output channels buffers on first sample position */4734i = channels_count;4735do4736{4737i--;4738chan_out[i] += channels_off[i];4739}4740while(i);47414742/* Conversely to fluid_synth_process(),4743we want rendered audio effect mixed in internal audio dry buffers.4744TRUE instructs the mixer that internal audio effects will be mixed in internal4745audio dry buffers.4746*/4747fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE);4748/* get first internal mixer audio dry buffer's pointer (left and right channel) */4749fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);47504751size = len;4752/* synth->cur indicates if available samples are still in internal mixer buffer */4753cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */4754di = synth->dither_index;47554756do4757{4758/* fill up the buffers as needed */4759if(cur >= synth->curmax)4760{4761/* render audio (dry and effect) to internal dry buffers */4762/* always render full blocs multiple of FLUID_BUFSIZE */4763int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;4764synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);47654766/* get first internal mixer audio dry buffer's pointer (left and right channel) */4767fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);4768cur = 0;4769}47704771/* calculate amount of available samples */4772n = synth->curmax - cur;47734774/* keep track of emitted samples */4775if(n > size)4776{4777n = size;4778}47794780size -= n;47814782/* update pointers to current position */4783left_in += cur + n;4784right_in += cur + n;47854786/* set final cursor position */4787cur += n;47884789/* reverse index */4790n = 0 - n;47914792do4793{4794i = bufs_in_count;4795do4796{4797/* input sample index in stereo buffer i */4798int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n;4799int c = i << 1; /* channel index c to write */48004801/* write left input sample to channel sample */4802*chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f +4803rand_table[0][di]);48044805/* write right input sample to next channel sample */4806*chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f +4807rand_table[1][di]);4808/* advance output pointers */4809chan_out[c] += channels_incr[c];4810chan_out[c+1] += channels_incr[c+1];4811}4812while(i);48134814if(++di >= DITHER_SIZE)4815{4816di = 0;4817}4818}4819while(++n < 0);4820}4821while(size);48224823synth->cur = cur; /* save current sample position. It will be used on next call */4824synth->dither_index = di; /* keep dither buffer continuous */48254826/* save average cpu load, used by API for real time cpu load meter */4827time = fluid_utime() - time;4828cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);4829fluid_atomic_float_set(&synth->cpu_load, cpu_load);48304831/* stop duration probe and save performance measurement (if profiling is enabled) */4832fluid_profile_write(FLUID_PROF_WRITE, prof_ref,4833fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4834len);4835return FLUID_OK;4836}48374838/**4839* Converts stereo floating point sample data to signed 16 bit data with dithering.4840* @param dither_index Pointer to an integer which should be initialized to 04841* before the first call and passed unmodified to additional calls which are4842* part of the same synthesis output.4843* @param len Length in frames to convert4844* @param lin Buffer of left audio samples to convert from4845* @param rin Buffer of right audio samples to convert from4846* @param lout Array of 16 bit words to store left channel of audio4847* @param loff Offset index in 'lout' for first sample4848* @param lincr Increment between samples stored to 'lout'4849* @param rout Array of 16 bit words to store right channel of audio4850* @param roff Offset index in 'rout' for first sample4851* @param rincr Increment between samples stored to 'rout'4852*4853* @note Currently private to libfluidsynth.4854*/4855void4856fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin,4857void *lout, int loff, int lincr,4858void *rout, int roff, int rincr)4859{4860int i, j, k;4861int16_t *left_out = lout;4862int16_t *right_out = rout;4863int di = *dither_index;4864fluid_profile_ref_var(prof_ref);48654866for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr)4867{4868left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]);4869right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]);48704871if(++di >= DITHER_SIZE)4872{4873di = 0;4874}4875}48764877*dither_index = di; /* keep dither buffer continuous */48784879fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len);4880}48814882static void4883fluid_synth_check_finished_voices(fluid_synth_t *synth)4884{4885int j;4886fluid_rvoice_t *fv;48874888while(NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler)))4889{4890for(j = 0; j < synth->polyphony; j++)4891{4892if(synth->voice[j]->rvoice == fv)4893{4894fluid_voice_unlock_rvoice(synth->voice[j]);4895fluid_voice_stop(synth->voice[j]);4896break;4897}4898else if(synth->voice[j]->overflow_rvoice == fv)4899{4900/* Unlock the overflow_rvoice of the voice.4901Decrement the reference count of the sample owned by this4902rvoice.4903*/4904fluid_voice_overflow_rvoice_finished(synth->voice[j]);49054906/* Decrement synth active voice count. Must not be incorporated4907in fluid_voice_overflow_rvoice_finished() because4908fluid_voice_overflow_rvoice_finished() is called also4909at synth destruction and in this case the variable should be4910accessed via voice->channel->synth->active_voice_count.4911And for certain voices which are not playing, the field4912voice->channel is NULL.4913*/4914synth->active_voice_count--;4915break;4916}4917}4918}4919}49204921/**4922* Process all waiting events in the rvoice queue.4923* Make sure no (other) rendering is running in parallel when4924* you call this function!4925*/4926void fluid_synth_process_event_queue(fluid_synth_t *synth)4927{4928fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);4929}493049314932/**4933* Process blocks (FLUID_BUFSIZE) of audio.4934* Must be called from renderer thread only!4935* @return number of blocks rendered. Might (often) return less than requested4936*/4937static int4938fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount)4939{4940int i, maxblocks;4941fluid_profile_ref_var(prof_ref);49424943/* Assign ID of synthesis thread */4944// synth->synth_thread_id = fluid_thread_get_id ();49454946fluid_check_fpe("??? Just starting up ???");49474948fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);49494950/* do not render more blocks than we can store internally */4951maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer);49524953if(blockcount > maxblocks)4954{4955blockcount = maxblocks;4956}49574958for(i = 0; i < blockcount; i++)4959{4960fluid_sample_timer_process(synth);4961fluid_synth_add_ticks(synth, FLUID_BUFSIZE);49624963/* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all()4964* (should only happen with parallel render) stop processing and go for rendering4965*/4966if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler))4967{4968// Something has happened, we can't process more4969blockcount = i + 1;4970break;4971}4972}49734974fluid_check_fpe("fluid_sample_timer_process");49754976blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount);49774978/* Testcase, that provokes a denormal floating point error */4979#if 04980{4981float num = 1;49824983while(num != 0)4984{4985num *= 0.5;4986};4987};4988#endif4989fluid_check_fpe("??? Remainder of synth_one_block ???");4990fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref,4991fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),4992blockcount * FLUID_BUFSIZE);4993return blockcount;4994}49954996/*4997* Handler for synth.reverb.* and synth.chorus.* double settings.4998*/4999static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value)5000{5001fluid_synth_t *synth = (fluid_synth_t *)data;5002fluid_return_if_fail(synth != NULL);50035004if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0)5005{5006fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value);5007}5008else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0)5009{5010fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value);5011}5012else if(FLUID_STRCMP(name, "synth.reverb.width") == 0)5013{5014fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value);5015}5016else if(FLUID_STRCMP(name, "synth.reverb.level") == 0)5017{5018fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value);5019}5020else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0)5021{5022fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value);5023}5024else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0)5025{5026fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value);5027}5028else if(FLUID_STRCMP(name, "synth.chorus.level") == 0)5029{5030fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value);5031}5032}50335034/*5035* Handler for synth.reverb.* and synth.chorus.* integer settings.5036*/5037static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value)5038{5039fluid_synth_t *synth = (fluid_synth_t *)data;5040fluid_return_if_fail(synth != NULL);50415042if(FLUID_STRCMP(name, "synth.reverb.active") == 0)5043{5044fluid_synth_reverb_on(synth, -1, value);5045}5046else if(FLUID_STRCMP(name, "synth.chorus.active") == 0)5047{5048fluid_synth_chorus_on(synth, -1, value);5049}5050else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0)5051{5052fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value);5053}5054}50555056/*5057* Handler for synth.overflow.* settings.5058*/5059static void fluid_synth_handle_overflow(void *data, const char *name, double value)5060{5061fluid_synth_t *synth = (fluid_synth_t *)data;5062fluid_return_if_fail(synth != NULL);50635064fluid_synth_api_enter(synth);50655066if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0)5067{5068synth->overflow.percussion = value;5069}5070else if(FLUID_STRCMP(name, "synth.overflow.released") == 0)5071{5072synth->overflow.released = value;5073}5074else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0)5075{5076synth->overflow.sustained = value;5077}5078else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0)5079{5080synth->overflow.volume = value;5081}5082else if(FLUID_STRCMP(name, "synth.overflow.age") == 0)5083{5084synth->overflow.age = value;5085}5086else if(FLUID_STRCMP(name, "synth.overflow.important") == 0)5087{5088synth->overflow.important = value;5089}50905091fluid_synth_api_exit(synth);5092}50935094/* Selects a voice for killing. */5095static fluid_voice_t *5096fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth)5097{5098int i;5099float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1;5100float this_voice_prio;5101fluid_voice_t *voice;5102int best_voice_index = -1;5103unsigned int ticks = fluid_synth_get_ticks(synth);51045105for(i = 0; i < synth->polyphony; i++)5106{51075108voice = synth->voice[i];51095110/* safeguard against an available voice. */5111if(_AVAILABLE(voice))5112{5113return voice;5114}51155116this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow,5117ticks);51185119/* check if this voice has less priority than the previous candidate. */5120if(this_voice_prio < best_prio)5121{5122best_voice_index = i;5123best_prio = this_voice_prio;5124}5125}51265127if(best_voice_index < 0)5128{5129return NULL;5130}51315132voice = synth->voice[best_voice_index];5133FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ",5134fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice));5135fluid_voice_off(voice);51365137return voice;5138}513951405141/**5142* Allocate a synthesis voice.5143* @param synth FluidSynth instance5144* @param sample Sample to assign to the voice5145* @param chan MIDI channel number (0 to MIDI channel count - 1)5146* @param key MIDI note number for the voice (0-127)5147* @param vel MIDI velocity for the voice (0-127)5148* @return Allocated synthesis voice or NULL on error5149*5150* This function is called by a SoundFont's preset in response to a noteon event.5151* The returned voice comes with default modulators and generators.5152* A single noteon event may create any number of voices, when the preset is layered.5153*5154* @note Should only be called from within synthesis thread, which includes5155* SoundFont loader preset noteon method.5156*/5157fluid_voice_t *5158fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample,5159int chan, int key, int vel)5160{5161fluid_return_val_if_fail(sample != NULL, NULL);5162fluid_return_val_if_fail(sample->data != NULL, NULL);5163FLUID_API_ENTRY_CHAN(NULL);5164FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL));51655166}51675168fluid_voice_t *5169fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range)5170{5171int i, k;5172fluid_voice_t *voice = NULL;5173fluid_channel_t *channel = NULL;5174unsigned int ticks;51755176/* check if there's an available synthesis process */5177for(i = 0; i < synth->polyphony; i++)5178{5179if(_AVAILABLE(synth->voice[i]))5180{5181voice = synth->voice[i];5182break;5183}5184}51855186/* No success yet? Then stop a running voice. */5187if(voice == NULL)5188{5189FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice");5190voice = fluid_synth_free_voice_by_kill_LOCAL(synth);5191}51925193if(voice == NULL)5194{5195FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);5196return NULL;5197}51985199ticks = fluid_synth_get_ticks(synth);52005201if(synth->verbose)5202{5203k = 0;52045205for(i = 0; i < synth->polyphony; i++)5206{5207if(!_AVAILABLE(synth->voice[i]))5208{5209k++;5210}5211}52125213FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",5214chan, key, vel, synth->storeid,5215(float) ticks / 44100.0f,5216(fluid_curtime() - synth->start) / 1000.0f,52170.0f,5218k);5219}52205221channel = synth->channel[chan];52225223if(fluid_voice_init(voice, sample, zone_range, channel, key, vel,5224synth->storeid, ticks, synth->gain) != FLUID_OK)5225{5226FLUID_LOG(FLUID_WARN, "Failed to initialize voice");5227return NULL;5228}52295230/* add the default modulators to the synthesis process. */5231/* custom_breath2att_modulator is not a default modulator specified in SF5232it is intended to replace default_vel2att_mod for this channel on demand using5233API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel.5234*/5235{5236int mono = fluid_channel_is_playing_mono(channel);5237fluid_mod_t *default_mod = synth->default_mod;52385239while(default_mod != NULL)5240{5241if(5242/* See if default_mod is the velocity_to_attenuation modulator */5243fluid_mod_test_identity(default_mod, &default_vel2att_mod) &&5244// See if a replacement by custom_breath2att_modulator has been demanded5245// for this channel5246((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) ||5247(mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO)))5248)5249{5250// Replacement of default_vel2att modulator by custom_breath2att_modulator5251fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);5252}5253else5254{5255fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);5256}52575258// Next default modulator to add to the voice5259default_mod = default_mod->next;5260}5261}52625263return voice;5264}52655266/* Kill all voices on a given channel, which have the same exclusive class5267* generator as new_voice.5268*/5269static void5270fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth,5271fluid_voice_t *new_voice)5272{5273int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS);5274int i;52755276/* Excl. class 0: No exclusive class */5277if(excl_class == 0)5278{5279return;5280}52815282/* Kill all notes on the same channel with the same exclusive class */5283for(i = 0; i < synth->polyphony; i++)5284{5285fluid_voice_t *existing_voice = synth->voice[i];52865287/* If voice is playing, on the same channel, has same exclusive5288* class and is not part of the same noteon event (voice group), then kill it */52895290if(fluid_voice_is_playing(existing_voice)5291&& fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice)5292&& fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS) == excl_class5293&& fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice))5294{5295fluid_voice_kill_excl(existing_voice);5296}5297}5298}52995300/**5301* Activate a voice previously allocated with fluid_synth_alloc_voice().5302* @param synth FluidSynth instance5303* @param voice Voice to activate5304*5305* This function is called by a SoundFont's preset in response to a noteon5306* event. Exclusive classes are processed here.5307*5308* @note Should only be called from within synthesis thread, which includes5309* SoundFont loader preset noteon method.5310*/5311void5312fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice)5313{5314fluid_return_if_fail(synth != NULL);5315fluid_return_if_fail(voice != NULL);5316// fluid_return_if_fail (fluid_synth_is_synth_thread (synth));5317fluid_synth_api_enter(synth);53185319/* Find the exclusive class of this voice. If set, kill all voices5320* that match the exclusive class and are younger than the first5321* voice process created by this noteon event. */5322fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);53235324fluid_voice_start(voice); /* Start the new voice */5325fluid_voice_lock_rvoice(voice);5326fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);5327fluid_synth_api_exit(synth);5328}53295330/**5331* Add a SoundFont loader to the synth. This function takes ownership of \c loader5332* and frees it automatically upon \c synth destruction.5333* @param synth FluidSynth instance5334* @param loader Loader API structure5335*5336* SoundFont loaders are used to add custom instrument loading to FluidSynth.5337* The caller supplied functions for loading files, allocating presets,5338* retrieving information on them and synthesizing note-on events. Using this5339* method even non SoundFont instruments can be synthesized, although limited5340* to the SoundFont synthesis model.5341*5342* @note Should only be called before any SoundFont files are loaded.5343*/5344void5345fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader)5346{5347fluid_return_if_fail(synth != NULL);5348fluid_return_if_fail(loader != NULL);5349fluid_synth_api_enter(synth);53505351/* Test if sfont is already loaded */5352if(synth->sfont == NULL)5353{5354synth->loaders = fluid_list_prepend(synth->loaders, loader);5355}53565357fluid_synth_api_exit(synth);5358}53595360/**5361* Load a SoundFont file (filename is interpreted by SoundFont loaders).5362* The newly loaded SoundFont will be put on top of the SoundFont5363* stack. Presets are searched starting from the SoundFont on the5364* top of the stack, working the way down the stack until a preset is found.5365*5366* @param synth FluidSynth instance5367* @param filename File to load5368* @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset())5369* @return SoundFont ID on success, #FLUID_FAILED on error5370*5371* @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it5372* to wide-char internally and then pass it to <code>_wfopen()</code>. Before FluidSynth 2.2.0, @c filename was treated as ANSI string5373* on Windows. All other platforms directly pass it to <code>fopen()</code> without any conversion (usually, UTF8 is accepted).5374*/5375int5376fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets)5377{5378fluid_sfont_t *sfont;5379fluid_list_t *list;5380fluid_sfloader_t *loader;5381int sfont_id;53825383fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5384fluid_return_val_if_fail(filename != NULL, FLUID_FAILED);5385fluid_synth_api_enter(synth);53865387sfont_id = synth->sfont_id;53885389if(++sfont_id != FLUID_FAILED)5390{5391/* MT NOTE: Loaders list should not change. */53925393for(list = synth->loaders; list; list = fluid_list_next(list))5394{5395loader = (fluid_sfloader_t *) fluid_list_get(list);53965397sfont = fluid_sfloader_load(loader, filename);53985399if(sfont != NULL)5400{5401sfont->refcount++;5402synth->sfont_id = sfont->id = sfont_id;54035404synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */54055406/* reset the presets for all channels if requested */5407if(reset_presets)5408{5409fluid_synth_program_reset(synth);5410}54115412FLUID_API_RETURN(sfont_id);5413}5414}5415}54165417FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);5418FLUID_API_RETURN(FLUID_FAILED);5419}54205421/**5422* Schedule a SoundFont for unloading.5423*5424* If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately.5425*5426* If any samples of the given SoundFont are still required by active voices,5427* the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing.5428* If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont5429* will be unloaded in any case.5430* Once this function returned, fluid_synth_sfcount() and similar functions will behave as if5431* the SoundFont has already been unloaded, even though the lazy-unloading is still pending.5432*5433* @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a5434* consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain5435* conditions. Calling delete_fluid_synth() does not recover this situation either.5436*5437* @param synth FluidSynth instance5438* @param id ID of SoundFont to unload5439* @param reset_presets TRUE to re-assign presets for all MIDI channels5440* @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise.5441*/5442int5443fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets)5444{5445fluid_sfont_t *sfont = NULL;5446fluid_list_t *list;54475448fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5449fluid_synth_api_enter(synth);54505451/* remove the SoundFont from the list */5452for(list = synth->sfont; list; list = fluid_list_next(list))5453{5454sfont = fluid_list_get(list);54555456if(fluid_sfont_get_id(sfont) == id)5457{5458synth->sfont = fluid_list_remove(synth->sfont, sfont);5459break;5460}5461}54625463if(!list)5464{5465FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);5466FLUID_API_RETURN(FLUID_FAILED);5467}54685469/* reset the presets for all channels (SoundFont will be freed when there are no more references) */5470if(reset_presets)5471{5472fluid_synth_program_reset(synth);5473}5474else5475{5476fluid_synth_update_presets(synth);5477}54785479/* -- Remove synth->sfont list's reference to SoundFont */5480fluid_synth_sfont_unref(synth, sfont);54815482FLUID_API_RETURN(FLUID_OK);5483}54845485/* Unref a SoundFont and destroy if no more references */5486void5487fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont)5488{5489fluid_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */54905491sfont->refcount--; /* -- Remove the sfont list's reference */54925493if(sfont->refcount == 0) /* No more references? - Attempt delete */5494{5495if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */5496{5497FLUID_LOG(FLUID_DBG, "Unloaded SoundFont");5498} /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */5499else5500{5501fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE);5502synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer);5503}5504}5505}55065507/* Callback to continually attempt to unload a SoundFont,5508* only if a SoundFont loader blocked the unload operation */5509static int5510fluid_synth_sfunload_callback(void *data, unsigned int msec)5511{5512fluid_sfont_t *sfont = data;55135514if(fluid_sfont_delete_internal(sfont) == 0)5515{5516FLUID_LOG(FLUID_DBG, "Unloaded SoundFont");5517return FALSE;5518}5519else5520{5521return TRUE;5522}5523}55245525/**5526* Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack.5527* @param synth FluidSynth instance5528* @param id ID of SoundFont to reload5529* @return SoundFont ID on success, #FLUID_FAILED on error5530*/5531int5532fluid_synth_sfreload(fluid_synth_t *synth, int id)5533{5534char *filename = NULL;5535fluid_sfont_t *sfont;5536fluid_sfloader_t *loader;5537fluid_list_t *list;5538int index, ret = FLUID_FAILED;55395540fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5541fluid_synth_api_enter(synth);55425543/* Search for SoundFont and get its index */5544for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++)5545{5546sfont = fluid_list_get(list);55475548if(fluid_sfont_get_id(sfont) == id)5549{5550break;5551}5552}55535554if(!list)5555{5556FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);5557goto exit;5558}55595560/* keep a copy of the SoundFont's filename */5561filename = FLUID_STRDUP(fluid_sfont_get_name(sfont));55625563if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK)5564{5565goto exit;5566}55675568/* MT Note: SoundFont loader list will not change */55695570for(list = synth->loaders; list; list = fluid_list_next(list))5571{5572loader = (fluid_sfloader_t *) fluid_list_get(list);55735574sfont = fluid_sfloader_load(loader, filename);55755576if(sfont != NULL)5577{5578sfont->id = id;5579sfont->refcount++;55805581synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */55825583/* reset the presets for all channels */5584fluid_synth_update_presets(synth);5585ret = id;5586goto exit;5587}5588}55895590FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);55915592exit:5593FLUID_FREE(filename);5594FLUID_API_RETURN(ret);5595}55965597/**5598* Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack and ownership is transferred to @p synth.5599* @param synth FluidSynth instance5600* @param sfont SoundFont to add5601* @return New assigned SoundFont ID or #FLUID_FAILED on error5602*/5603int5604fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)5605{5606int sfont_id;56075608fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5609fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);5610fluid_synth_api_enter(synth);56115612sfont_id = synth->sfont_id;56135614if(++sfont_id != FLUID_FAILED)5615{5616synth->sfont_id = sfont->id = sfont_id;5617synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */56185619/* reset the presets for all channels */5620fluid_synth_program_reset(synth);5621}56225623FLUID_API_RETURN(sfont_id);5624}56255626/**5627* Remove a SoundFont from the SoundFont stack without deleting it.5628* @param synth FluidSynth instance5629* @param sfont SoundFont to remove5630* @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise5631*5632* SoundFont is not freed and is left as the responsibility of the caller.5633*5634* @note The SoundFont should only be freed after there are no presets5635* referencing it. This can only be ensured by the SoundFont loader and5636* therefore this function should not normally be used.5637*/5638int5639fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)5640{5641fluid_sfont_t *sfont_tmp;5642fluid_list_t *list;5643int ret = FLUID_FAILED;56445645fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);5646fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);5647fluid_synth_api_enter(synth);56485649/* remove the SoundFont from the list */5650for(list = synth->sfont; list; list = fluid_list_next(list))5651{5652sfont_tmp = fluid_list_get(list);56535654if(sfont_tmp == sfont)5655{5656synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp);5657ret = FLUID_OK;5658break;5659}5660}56615662/* reset the presets for all channels */5663fluid_synth_program_reset(synth);56645665FLUID_API_RETURN(ret);5666}56675668/**5669* Count number of loaded SoundFont files.5670* @param synth FluidSynth instance5671* @return Count of loaded SoundFont files.5672*/5673int5674fluid_synth_sfcount(fluid_synth_t *synth)5675{5676int count;56775678fluid_return_val_if_fail(synth != NULL, 0);5679fluid_synth_api_enter(synth);5680count = fluid_list_size(synth->sfont);5681FLUID_API_RETURN(count);5682}56835684/**5685* Get SoundFont by index.5686* @param synth FluidSynth instance5687* @param num SoundFont index on the stack (starting from 0 for top of stack).5688* @return SoundFont instance or NULL if invalid index5689*5690* @note Caller should be certain that SoundFont is not deleted (unloaded) for5691* the duration of use of the returned pointer.5692*/5693fluid_sfont_t *5694fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num)5695{5696fluid_sfont_t *sfont = NULL;5697fluid_list_t *list;56985699fluid_return_val_if_fail(synth != NULL, NULL);5700fluid_synth_api_enter(synth);5701list = fluid_list_nth(synth->sfont, num);57025703if(list)5704{5705sfont = fluid_list_get(list);5706}57075708FLUID_API_RETURN(sfont);5709}57105711/**5712* Get SoundFont by ID.5713* @param synth FluidSynth instance5714* @param id SoundFont ID5715* @return SoundFont instance or NULL if invalid ID5716*5717* @note Caller should be certain that SoundFont is not deleted (unloaded) for5718* the duration of use of the returned pointer.5719*/5720fluid_sfont_t *5721fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id)5722{5723fluid_sfont_t *sfont = NULL;5724fluid_list_t *list;57255726fluid_return_val_if_fail(synth != NULL, NULL);5727fluid_synth_api_enter(synth);57285729for(list = synth->sfont; list; list = fluid_list_next(list))5730{5731sfont = fluid_list_get(list);57325733if(fluid_sfont_get_id(sfont) == id)5734{5735break;5736}5737}57385739FLUID_API_RETURN(list ? sfont : NULL);5740}57415742/**5743* Get SoundFont by name.5744* @param synth FluidSynth instance5745* @param name Name of SoundFont5746* @return SoundFont instance or NULL if invalid name5747* @since 1.1.05748*5749* @note Caller should be certain that SoundFont is not deleted (unloaded) for5750* the duration of use of the returned pointer.5751*/5752fluid_sfont_t *5753fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name)5754{5755fluid_sfont_t *sfont = NULL;5756fluid_list_t *list;57575758fluid_return_val_if_fail(synth != NULL, NULL);5759fluid_return_val_if_fail(name != NULL, NULL);5760fluid_synth_api_enter(synth);57615762for(list = synth->sfont; list; list = fluid_list_next(list))5763{5764sfont = fluid_list_get(list);57655766if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0)5767{5768break;5769}5770}57715772FLUID_API_RETURN(list ? sfont : NULL);5773}57745775/**5776* Get active preset on a MIDI channel.5777* @param synth FluidSynth instance5778* @param chan MIDI channel number (0 to MIDI channel count - 1)5779* @return Preset or NULL if no preset active on \c chan5780*5781* @note Should only be called from within synthesis thread, which includes5782* SoundFont loader preset noteon methods. Not thread safe otherwise.5783*/5784fluid_preset_t *5785fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan)5786{5787fluid_preset_t *result;5788fluid_channel_t *channel;5789FLUID_API_ENTRY_CHAN(NULL);57905791channel = synth->channel[chan];5792result = channel->preset;5793fluid_synth_api_exit(synth);5794return result;5795}57965797/**5798* Get list of currently playing voices.5799* @param synth FluidSynth instance5800* @param buf Array to store voices to (NULL terminated if not filled completely)5801* @param bufsize Count of indexes in buf5802* @param id Voice ID to search for or < 0 to return list of all playing voices5803*5804* @note Should only be called from within synthesis thread, which includes5805* SoundFont loader preset noteon methods. Voices are only guaranteed to remain5806* unchanged until next synthesis process iteration.5807*/5808void5809fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize,5810int id)5811{5812int count = 0;5813int i;58145815fluid_return_if_fail(synth != NULL);5816fluid_return_if_fail(buf != NULL);5817fluid_synth_api_enter(synth);58185819for(i = 0; i < synth->polyphony && count < bufsize; i++)5820{5821fluid_voice_t *voice = synth->voice[i];58225823if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id))5824{5825buf[count++] = voice;5826}5827}58285829if(count < bufsize)5830{5831buf[count] = NULL;5832}58335834fluid_synth_api_exit(synth);5835}58365837/**5838* Enable or disable reverb effect.5839* @param synth FluidSynth instance5840* @param on TRUE to enable chorus, FALSE to disable5841* @deprecated Use fluid_synth_reverb_on() instead.5842*/5843void5844fluid_synth_set_reverb_on(fluid_synth_t *synth, int on)5845{5846fluid_return_if_fail(synth != NULL);5847fluid_synth_api_enter(synth);58485849synth->with_reverb = (on != 0);5850fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled,5851on != 0, 0.0f);5852fluid_synth_api_exit(synth);5853}58545855/**5856* Enable or disable reverb on one fx group unit.5857* @param synth FluidSynth instance5858* @param fx_group Index of the fx group.5859* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the5860* parameter will be applied to all fx groups.5861* @param on TRUE to enable reverb, FALSE to disable5862* @return #FLUID_OK on success, #FLUID_FAILED otherwise5863*/5864int5865fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on)5866{5867int ret;5868fluid_rvoice_param_t param[MAX_EVENT_PARAMS];5869fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);58705871fluid_synth_api_enter(synth);58725873if(fx_group < -1 || fx_group >= synth->effects_groups)5874{5875FLUID_API_RETURN(FLUID_FAILED);5876}58775878if(fx_group < 0 )5879{5880synth->with_reverb = (on != 0);5881}58825883param[0].i = fx_group;5884param[1].i = on;5885ret = fluid_rvoice_eventhandler_push(synth->eventhandler,5886fluid_rvoice_mixer_reverb_enable,5887synth->eventhandler->mixer,5888param);58895890FLUID_API_RETURN(ret);5891}58925893/**5894* Activate a reverb preset.5895* @param synth FluidSynth instance5896* @param num Reverb preset number5897* @return #FLUID_OK on success, #FLUID_FAILED otherwise5898*5899* @note Currently private to libfluidsynth.5900*/5901int5902fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num)5903{5904double values[FLUID_REVERB_PARAM_LAST];59055906fluid_return_val_if_fail(5907num < FLUID_N_ELEMENTS(revmodel_preset),5908FLUID_FAILED5909);59105911values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize;5912values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp;5913values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width;5914values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level;5915fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values);5916return FLUID_OK;5917}59185919/**5920* Set reverb parameters to all groups.5921*5922* @param synth FluidSynth instance5923* @param roomsize Reverb room size value (0.0-1.0)5924* @param damping Reverb damping value (0.0-1.0)5925* @param width Reverb width value (0.0-100.0)5926* @param level Reverb level value (0.0-1.0)5927* @return #FLUID_OK on success, #FLUID_FAILED otherwise5928* @deprecated Use the individual reverb setter functions in new code instead.5929*/5930int5931fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping,5932double width, double level)5933{5934double values[FLUID_REVERB_PARAM_LAST];59355936fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);59375938values[FLUID_REVERB_ROOMSIZE] = roomsize;5939values[FLUID_REVERB_DAMP] = damping;5940values[FLUID_REVERB_WIDTH] = width;5941values[FLUID_REVERB_LEVEL] = level;5942return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values);5943}59445945/**5946* Set reverb roomsize of all groups.5947*5948* @param synth FluidSynth instance5949* @param roomsize Reverb room size value (0.0-1.0)5950* @return #FLUID_OK on success, #FLUID_FAILED otherwise5951* @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead.5952*/5953int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize)5954{5955return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize);5956}59575958/**5959* Set reverb damping of all groups.5960*5961* @param synth FluidSynth instance5962* @param damping Reverb damping value (0.0-1.0)5963* @return #FLUID_OK on success, #FLUID_FAILED otherwise5964* @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead.5965*/5966int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping)5967{5968return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping);5969}59705971/**5972* Set reverb width of all groups.5973*5974* @param synth FluidSynth instance5975* @param width Reverb width value (0.0-100.0)5976* @return #FLUID_OK on success, #FLUID_FAILED otherwise5977* @deprecated Use fluid_synth_set_reverb_group_width() in new code instead.5978*/5979int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width)5980{5981return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width);5982}59835984/**5985* Set reverb level of all groups.5986*5987* @param synth FluidSynth instance5988* @param level Reverb level value (0.0-1.0)5989* @return #FLUID_OK on success, #FLUID_FAILED otherwise5990* @deprecated Use fluid_synth_set_reverb_group_level() in new code instead.5991*/5992int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level)5993{5994return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, level);5995}59965997/**5998* Set reverb roomsize to one or all fx groups.5999* @param synth FluidSynth instance.6000* @param fx_group Index of the fx group.6001* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6002* parameter will be applied to all fx groups.6003* @param roomsize roomsize value to set. Must be in the range indicated by6004* synth.reverb.room-size setting.6005* @return #FLUID_OK on success, #FLUID_FAILED otherwise6006*/6007int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group,6008double roomsize)6009{6010return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize);6011}60126013/**6014* Set reverb damp to one or all fx groups.6015* @param synth FluidSynth instance.6016* @param fx_group Index of the fx group.6017* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6018* parameter will be applied to all fx groups.6019* @param damping damping value to set. Must be in the range indicated by6020* synth.reverb.damp setting.6021* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6022*/6023int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group,6024double damping)6025{6026return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping);6027}60286029/**6030* Set reverb width to one or all fx groups.6031* @param synth FluidSynth instance.6032* @param fx_group Index of the fx group.6033* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6034* parameter will be applied to all fx groups.6035* @param width width value to set. Must be in the range indicated by6036* synth.reverb.width setting.6037* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6038*/6039int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group,6040double width)6041{6042return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width);6043}60446045/**6046* Set reverb level to one or all fx groups.6047* @param synth FluidSynth instance.6048* @param fx_group Index of the fx group.6049* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6050* parameter will be applied to all fx groups.6051* @param level output level to set. Must be in the range indicated by6052* synth.reverb.level setting.6053* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6054*/6055int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group,6056double level)6057{6058return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level);6059}60606061/**6062* Set one reverb parameter to one fx groups.6063* @param synth FluidSynth instance.6064* @param fx_group Index of the fx group.6065* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6066* parameter will be applied to all fx groups.6067* @param enum indicating the parameter to set (#fluid_reverb_param).6068* FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0)6069* FLUID_REVERB_DAMP, reverb damping value (0.0-1.0)6070* FLUID_REVERB_WIDTH, reverb width value (0.0-100.0)6071* FLUID_REVERB_LEVEL, reverb level value (0.0-1.0)6072* @param value, parameter value6073* @return #FLUID_OK on success, #FLUID_FAILED otherwise6074*/6075int6076fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group,6077int param, double value)6078{6079int ret;6080double values[FLUID_REVERB_PARAM_LAST] = {0.0};6081static const char *name[FLUID_REVERB_PARAM_LAST] =6082{6083"synth.reverb.room-size", "synth.reverb.damp",6084"synth.reverb.width", "synth.reverb.level"6085};60866087double min; /* minimum value */6088double max; /* maximum value */60896090/* check parameters */6091fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6092fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED);6093fluid_synth_api_enter(synth);60946095if(fx_group < -1 || fx_group >= synth->effects_groups)6096{6097FLUID_API_RETURN(FLUID_FAILED);6098}60996100/* check if reverb value is in max min range */6101fluid_settings_getnum_range(synth->settings, name[param], &min, &max);6102if(value < min || value > max)6103{6104FLUID_API_RETURN(FLUID_FAILED);6105}61066107/* set the value */6108values[param] = value;6109ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values);6110FLUID_API_RETURN(ret);6111}61126113int6114fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set,6115const double values[])6116{6117fluid_rvoice_param_t param[MAX_EVENT_PARAMS];61186119/* if non of the flags is set, fail */6120fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED);61216122/* fx group shadow values are set here so that they will be returned if queried */6123fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set,6124values);61256126/* Synth shadow values are set here so that they will be returned if queried */6127if (fx_group < 0)6128{6129int i;6130for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++)6131{6132if(set & FLUID_REVPARAM_TO_SETFLAG(i))6133{6134synth->reverb_param[i] = values[i];6135}6136}6137}61386139param[0].i = fx_group;6140param[1].i = set;6141param[2].real = values[FLUID_REVERB_ROOMSIZE];6142param[3].real = values[FLUID_REVERB_DAMP];6143param[4].real = values[FLUID_REVERB_WIDTH];6144param[5].real = values[FLUID_REVERB_LEVEL];6145/* finally enqueue an rvoice event to the mixer to actual update reverb */6146return fluid_rvoice_eventhandler_push(synth->eventhandler,6147fluid_rvoice_mixer_set_reverb_params,6148synth->eventhandler->mixer,6149param);6150}61516152/**6153* Get reverb room size of all fx groups.6154* @param synth FluidSynth instance6155* @return Reverb room size (0.0-1.2)6156* @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead.6157*/6158double6159fluid_synth_get_reverb_roomsize(fluid_synth_t *synth)6160{6161double roomsize = 0.0;6162fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize);6163return roomsize;6164}61656166/**6167* Get reverb damping of all fx groups.6168* @param synth FluidSynth instance6169* @return Reverb damping value (0.0-1.0)6170* @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead.6171*/6172double6173fluid_synth_get_reverb_damp(fluid_synth_t *synth)6174{6175double damp = 0.0;6176fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp);6177return damp;6178}61796180/**6181* Get reverb level of all fx groups.6182* @param synth FluidSynth instance6183* @return Reverb level value (0.0-1.0)6184* @deprecated Use fluid_synth_get_reverb_group_level() in new code instead.6185*/6186double6187fluid_synth_get_reverb_level(fluid_synth_t *synth)6188{6189double level = 0.0;6190fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level);6191return level;6192}61936194/**6195* Get reverb width of all fx groups.6196* @param synth FluidSynth instance6197* @return Reverb width value (0.0-100.0)6198* @deprecated Use fluid_synth_get_reverb_group_width() in new code instead.6199*/6200double6201fluid_synth_get_reverb_width(fluid_synth_t *synth)6202{6203double width = 0.0;6204fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width);6205return width;6206}62076208/**6209* get reverb roomsize of one or all groups.6210* @param synth FluidSynth instance.6211* @param fx_group Index of the fx group.6212* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6213* parameter common to all fx groups is fetched.6214* @param roomsize valid pointer on the value to return.6215* @return #FLUID_OK on success, #FLUID_FAILED otherwise6216*/6217int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group,6218double *roomsize)6219{6220return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize);6221}62226223/**6224* get reverb damp of one or all groups.6225* @param synth FluidSynth instance.6226* @param fx_group Index of the fx group.6227* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6228* parameter common to all fx groups is fetched.6229* @param damping valid pointer on the value to return.6230* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6231*/6232int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group,6233double *damping)6234{6235return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping);6236}62376238/**6239* get reverb width of one or all groups6240* @param synth FluidSynth instance.6241* @param fx_group Index of the fx group.6242* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6243* parameter common to all fx groups is fetched.6244* @param width valid pointer on the value to return.6245* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6246*/6247int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group,6248double *width)6249{6250return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width);6251}62526253/**6254* get reverb level of one or all groups.6255* @param synth FluidSynth instance.6256* @param fx_group Index of the fx group.6257* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6258* parameter common to all fx groups is fetched.6259* @param level valid pointer on the value to return.6260* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6261*/6262int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group,6263double *level)6264{6265return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level);6266}626762686269/**6270* Get one reverb parameter value of one fx groups.6271* @param synth FluidSynth instance6272* @param fx_group index of the fx group to get parameter value from.6273* Must be in the range -1 to synth->effects_groups-1. If -1 get the6274* parameter common to all fx groups.6275* @param enum indicating the parameter to get (#fluid_reverb_param).6276* FLUID_REVERB_ROOMSIZE, reverb room size value.6277* FLUID_REVERB_DAMP, reverb damping value.6278* FLUID_REVERB_WIDTH, reverb width value.6279* FLUID_REVERB_LEVEL, reverb level value.6280* @param value pointer on the value to return.6281* @return FLUID_OK if success, FLUID_FAILED otherwise.6282*/6283static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group,6284int param, double *value)6285{6286fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6287fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED);6288fluid_return_val_if_fail(value != NULL, FLUID_FAILED);6289fluid_synth_api_enter(synth);62906291if(fx_group < -1 || fx_group >= synth->effects_groups)6292{6293FLUID_API_RETURN(FLUID_FAILED);6294}62956296if (fx_group < 0)6297{6298/* return reverb param common to all fx groups */6299*value = synth->reverb_param[param];6300}6301else6302{6303/* return reverb param of fx group at index fx_group */6304*value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer,6305fx_group, param);6306}63076308FLUID_API_RETURN(FLUID_OK);6309}63106311/**6312* Enable or disable all chorus groups.6313* @param synth FluidSynth instance6314* @param on TRUE to enable chorus, FALSE to disable6315* @deprecated Use fluid_synth_chorus_on() in new code instead.6316*/6317void6318fluid_synth_set_chorus_on(fluid_synth_t *synth, int on)6319{6320fluid_return_if_fail(synth != NULL);6321fluid_synth_api_enter(synth);63226323synth->with_chorus = (on != 0);6324fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled,6325on != 0, 0.0f);6326fluid_synth_api_exit(synth);6327}63286329/**6330* Enable or disable chorus on one or all groups.6331* @param synth FluidSynth instance6332* @param fx_group Index of the fx group.6333* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6334* parameter will be applied to all fx groups.6335* @param on TRUE to enable chorus, FALSE to disable6336* @return #FLUID_OK on success, #FLUID_FAILED otherwise6337*/6338int6339fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on)6340{6341int ret;6342fluid_rvoice_param_t param[MAX_EVENT_PARAMS];6343fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);63446345fluid_synth_api_enter(synth);63466347if(fx_group < -1 || fx_group >= synth->effects_groups)6348{6349FLUID_API_RETURN(FLUID_FAILED);6350}63516352if(fx_group < 0 )6353{6354synth->with_chorus = (on != 0);6355}63566357param[0].i = fx_group;6358param[1].i = on;6359ret = fluid_rvoice_eventhandler_push(synth->eventhandler,6360fluid_rvoice_mixer_chorus_enable,6361synth->eventhandler->mixer,6362param);63636364FLUID_API_RETURN(ret);6365}63666367/**6368* Set chorus parameters to all fx groups.6369* Keep in mind, that the needed CPU time is proportional to 'nr'.6370* @param synth FluidSynth instance6371* @param nr Chorus voice count (0-99, CPU time consumption proportional to6372* this value)6373* @param level Chorus level (0.0-10.0)6374* @param speed Chorus speed in Hz (0.1-5.0)6375* @param depth_ms Chorus depth (max value depends on synth sample-rate,6376* 0.0-21.0 is safe for sample-rate values up to 96KHz)6377* @param type Chorus waveform type (#fluid_chorus_mod)6378* @return #FLUID_OK on success, #FLUID_FAILED otherwise6379* @deprecated Use the individual chorus setter functions in new code instead.6380*6381* Keep in mind, that the needed CPU time is proportional to 'nr'.6382*/6383int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level,6384double speed, double depth_ms, int type)6385{6386double values[FLUID_CHORUS_PARAM_LAST];63876388fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);63896390values[FLUID_CHORUS_NR] = nr;6391values[FLUID_CHORUS_LEVEL] = level;6392values[FLUID_CHORUS_SPEED] = speed;6393values[FLUID_CHORUS_DEPTH] = depth_ms;6394values[FLUID_CHORUS_TYPE] = type;6395return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values);6396}63976398/**6399* Set the chorus voice count of all groups.6400*6401* @param synth FluidSynth instance6402* @param nr Chorus voice count (0-99, CPU time consumption proportional to6403* this value)6404* @return #FLUID_OK on success, #FLUID_FAILED otherwise6405* @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead.6406*/6407int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr)6408{6409return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr);6410}64116412/**6413* Set the chorus level of all groups.6414*6415* @param synth FluidSynth instance6416* @param level Chorus level (0.0-10.0)6417* @return #FLUID_OK on success, #FLUID_FAILED otherwise6418* @deprecated Use fluid_synth_set_chorus_group_level() in new code instead.6419*/6420int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level)6421{6422return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level);6423}64246425/**6426* Set the chorus speed of all groups.6427*6428* @param synth FluidSynth instance6429* @param speed Chorus speed in Hz (0.1-5.0)6430* @return #FLUID_OK on success, #FLUID_FAILED otherwise6431* @deprecated Use fluid_synth_set_chorus_group_speed() in new code instead.6432*/6433int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed)6434{6435return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed);6436}64376438/**6439* Set the chorus depth of all groups.6440*6441* @param synth FluidSynth instance6442* @param depth_ms Chorus depth (max value depends on synth sample-rate,6443* 0.0-21.0 is safe for sample-rate values up to 96KHz)6444* @return #FLUID_OK on success, #FLUID_FAILED otherwise6445* @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead.6446*/6447int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms)6448{6449return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms);6450}64516452/**6453* Set the chorus type of all groups.6454*6455* @param synth FluidSynth instance6456* @param type Chorus waveform type (#fluid_chorus_mod)6457* @return #FLUID_OK on success, #FLUID_FAILED otherwise6458* @deprecated Use fluid_synth_set_chorus_group_type() in new code instead.6459*/6460int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type)6461{6462return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type);6463}64646465/**6466* Set chorus voice count nr to one or all chorus groups.6467* @param synth FluidSynth instance.6468* @param fx_group Index of the fx group.6469* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6470* parameter will be applied to all groups.6471* @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr}6472* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6473*/6474int6475fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr)6476{6477return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr);6478}64796480/**6481* Set chorus output level to one or all chorus groups.6482* @param synth FluidSynth instance.6483* @param fx_group Index of the fx group.6484* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6485* parameter will be applied to all groups.6486* @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level}6487* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6488*/6489int6490fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level)6491{6492return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level);6493}64946495/**6496* Set chorus lfo speed to one or all chorus groups.6497* @param synth FluidSynth instance.6498* @param fx_group Index of the fx group.6499* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6500* parameter will be applied to all groups.6501* @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed}6502* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6503*/6504int6505fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed)6506{6507return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed);6508}65096510/**6511* Set chorus lfo depth to one or all chorus groups.6512* @param synth FluidSynth instance.6513* @param fx_group Index of the fx group.6514* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6515* parameter will be applied to all groups.6516* @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth}6517* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6518*/6519int6520fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms)6521{6522return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms);6523}65246525/**6526* Set chorus lfo waveform type to one or all chorus groups.6527* @param synth FluidSynth instance.6528* @param fx_group Index of the fx group.6529* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6530* parameter will be applied to all groups.6531* @param type Lfo waveform type to set. (#fluid_chorus_mod)6532* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6533*/6534int6535fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type)6536{6537return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type);6538}65396540/**6541* Set one chorus parameter to one fx groups.6542* @param synth FluidSynth instance.6543* @param fx_group Index of the fx group.6544* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6545* parameter will be applied to all groups.6546* @param enum indicating the parameter to set (#fluid_chorus_param).6547* FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to6548* this value).6549* FLUID_CHORUS_LEVEL, chorus level (0.0-10.0).6550* FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0).6551* FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate,6552* 0.0-21.0 is safe for sample-rate values up to 96KHz).6553* FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod)6554* @param value, parameter value6555* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6556*/6557int6558fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param,6559double value)6560{6561int ret;6562double values[FLUID_CHORUS_PARAM_LAST] = {0.0};65636564/* setting name (except lfo waveform type) */6565static const char *name[FLUID_CHORUS_PARAM_LAST-1] =6566{6567"synth.chorus.nr", "synth.chorus.level",6568"synth.chorus.speed", "synth.chorus.depth"6569};65706571/* check parameters */6572fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6573fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED);6574fluid_synth_api_enter(synth);65756576if(fx_group < -1 || fx_group >= synth->effects_groups)6577{6578FLUID_API_RETURN(FLUID_FAILED);6579}65806581/* check if chorus value is in max min range */6582if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */6583{6584int min = FLUID_CHORUS_MOD_SINE;6585int max = FLUID_CHORUS_MOD_TRIANGLE;6586if(param == FLUID_CHORUS_NR)6587{6588fluid_settings_getint_range(synth->settings, name[param], &min, &max);6589}6590if((int)value < min || (int)value > max)6591{6592FLUID_API_RETURN(FLUID_FAILED);6593}6594}6595else /* float value */6596{6597double min;6598double max;6599fluid_settings_getnum_range(synth->settings, name[param], &min, &max);6600if(value < min || value > max)6601{6602FLUID_API_RETURN(FLUID_FAILED);6603}6604}66056606/* set the value */6607values[param] = value;6608ret = fluid_synth_set_chorus_full(synth, fx_group,6609FLUID_CHORPARAM_TO_SETFLAG(param), values);6610FLUID_API_RETURN(ret);6611}66126613int6614fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set,6615const double values[])6616{6617fluid_rvoice_param_t param[MAX_EVENT_PARAMS];66186619/* if non of the flags is set, fail */6620fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED);66216622/* fx group shadow values are set here so that they will be returned if queried */6623fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group,6624set, values);66256626/* Synth shadow values are set here so that they will be returned if queried */6627if (fx_group < 0)6628{6629int i;6630for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++)6631{6632if(set & FLUID_CHORPARAM_TO_SETFLAG(i))6633{6634synth->chorus_param[i] = values[i];6635}6636}6637}66386639param[0].i = fx_group;6640param[1].i = set;6641param[2].i = (int)values[FLUID_CHORUS_NR];6642param[3].real = values[FLUID_CHORUS_LEVEL];6643param[4].real = values[FLUID_CHORUS_SPEED];6644param[5].real = values[FLUID_CHORUS_DEPTH];6645param[6].i = (int)values[FLUID_CHORUS_TYPE];6646return fluid_rvoice_eventhandler_push(synth->eventhandler,6647fluid_rvoice_mixer_set_chorus_params,6648synth->eventhandler->mixer,6649param);6650}66516652/**6653* Get chorus voice number (delay line count) value of all fx groups.6654* @param synth FluidSynth instance6655* @return Chorus voice count6656* @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead.6657*/6658int6659fluid_synth_get_chorus_nr(fluid_synth_t *synth)6660{6661double nr = 0.0;6662fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr);6663return (int)nr;6664}66656666/**6667* Get chorus level of all fx groups.6668* @param synth FluidSynth instance6669* @return Chorus level value6670* @deprecated Use fluid_synth_get_chorus_group_level() in new code instead.6671*/6672double6673fluid_synth_get_chorus_level(fluid_synth_t *synth)6674{6675double level = 0.0;6676fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level);6677return level;6678}66796680/**6681* Get chorus speed in Hz of all fx groups.6682* @param synth FluidSynth instance6683* @return Chorus speed in Hz6684* @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead.6685*/6686double6687fluid_synth_get_chorus_speed(fluid_synth_t *synth)6688{6689double speed = 0.0;6690fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed);6691return speed;6692}66936694/**6695* Get chorus depth of all fx groups.6696* @param synth FluidSynth instance6697* @return Chorus depth6698* @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead.6699*/6700double6701fluid_synth_get_chorus_depth(fluid_synth_t *synth)6702{6703double depth = 0.0;6704fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth);6705return depth;6706}67076708/**6709* Get chorus waveform type of all fx groups.6710* @param synth FluidSynth instance6711* @return Chorus waveform type (#fluid_chorus_mod)6712* @deprecated Use fluid_synth_get_chorus_group_type() in new code instead.6713*/6714int6715fluid_synth_get_chorus_type(fluid_synth_t *synth)6716{6717double type = 0.0;6718fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type);6719return (int)type;6720}67216722/**6723* Get chorus count nr of one or all fx groups.6724* @param synth FluidSynth instance.6725* @param fx_group Index of the fx group from which to fetch the chorus voice count.6726* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6727* parameter common to all fx groups is fetched.6728* @param nr valid pointer on value to return.6729* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6730*/6731int6732fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr)6733{6734double num_nr = 0.0;6735int status;6736status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr);6737*nr = (int)num_nr;6738return status;6739}67406741/**6742* Get chorus output level of one or all fx groups.6743* @param synth FluidSynth instance.6744* @param fx_group Index of the fx group from which chorus level to fetch.6745* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6746* parameter common to all fx groups is fetched.6747* @param level valid pointer on value to return.6748* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6749*/6750int6751fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level)6752{6753return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level);6754}67556756/**6757* Get chorus waveform lfo speed of one or all fx groups.6758* @param synth FluidSynth instance.6759* @param fx_group Index of the fx group from which lfo speed to fetch.6760* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6761* parameter common to all fx groups is fetched.6762* @param speed valid pointer on value to return.6763* @return #FLUID_OK on success, #FLUID_FAILED otherwise.6764*/6765int6766fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed)6767{6768return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed);6769}67706771/**6772* Get chorus lfo depth of one or all fx groups.6773* @param synth FluidSynth instance6774* @param fx_group Index of the fx group from which lfo depth to fetch.6775* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6776* parameter common to all fx groups is fetched.6777* @param depth_ms valid pointer on value to return.6778* @return #FLUID_OK on success, #FLUID_FAILED otherwise6779*/6780int6781fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms)6782{6783return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms);6784}67856786/**6787* Get chorus waveform type of one or all fx groups.6788* @param synth FluidSynth instance6789* @param fx_group Index of the fx group from which to fetch the waveform type.6790* Must be in the range <code>-1 to (fluid_synth_count_effects_groups()-1)</code>. If -1 the6791* parameter common to all fx groups is fetched.6792* @param type valid pointer on waveform type to return (#fluid_chorus_mod)6793* @return #FLUID_OK on success, #FLUID_FAILED otherwise6794*/6795int6796fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type)6797{6798double num_type = 0.0;6799int status;6800status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type);6801*type = (int)num_type;6802return status;6803}68046805/**6806* Get chorus parameter value of one or all fx groups.6807* @param synth FluidSynth instance6808* @param fx_group index of the fx group6809* @param enum indicating the parameter to get.6810* FLUID_CHORUS_NR, chorus voice count.6811* FLUID_CHORUS_LEVEL, chorus level.6812* FLUID_CHORUS_SPEED, chorus speed.6813* FLUID_CHORUS_DEPTH, chorus depth.6814* FLUID_CHORUS_TYPE, chorus waveform type.6815* @param value pointer on the value to return.6816* @return FLUID_OK if success, FLUID_FAILED otherwise.6817*/6818static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group,6819int param, double *value)6820{6821fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6822fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED);6823fluid_return_val_if_fail(value != NULL, FLUID_FAILED);6824fluid_synth_api_enter(synth);68256826if(fx_group < -1 || fx_group >= synth->effects_groups)6827{6828FLUID_API_RETURN(FLUID_FAILED);6829}68306831if (fx_group < 0)6832{6833/* return chorus param common to all fx groups */6834*value = synth->chorus_param[param];6835}6836else6837{6838/* return chorus param of fx group at index group */6839*value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer,6840fx_group, param);6841}68426843FLUID_API_RETURN(FLUID_OK);6844}68456846/*6847* If the same note is hit twice on the same channel, then the older6848* voice process is advanced to the release stage. Using a mechanical6849* MIDI controller, the only way this can happen is when the sustain6850* pedal is held. In this case the behaviour implemented here is6851* natural for many instruments. Note: One noteon event can trigger6852* several voice processes, for example a stereo sample. Don't6853* release those...6854*/6855void6856fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan,6857int key)6858{6859int i;6860fluid_voice_t *voice;68616862/* storeid is a parameter for fluid_voice_init() */6863synth->storeid = synth->noteid++;68646865/* for "monophonic playing" key is the previous sustained note6866if it exists (0 to 127) or INVALID_NOTE otherwise */6867if(key == INVALID_NOTE)6868{6869return;6870}68716872for(i = 0; i < synth->polyphony; i++)6873{6874voice = synth->voice[i];68756876if(fluid_voice_is_playing(voice)6877&& (fluid_voice_get_channel(voice) == chan)6878&& (fluid_voice_get_key(voice) == key)6879&& (fluid_voice_get_id(voice) != synth->noteid))6880{6881enum fluid_midi_channel_type type = synth->channel[chan]->channel_type;68826883/* Id of voices that was sustained by sostenuto */6884if(fluid_voice_is_sostenuto(voice))6885{6886synth->storeid = fluid_voice_get_id(voice);6887}68886889switch(type)6890{6891case CHANNEL_TYPE_DRUM:6892/* release the voice, this should make riding hi-hats or snares sound more6893* realistic (Discussion #1196) */6894fluid_voice_off(voice);6895break;6896case CHANNEL_TYPE_MELODIC:6897/* Force the voice into release stage except if pedaling (sostenuto or sustain) is active.6898* This gives a more realistic sound to pianos and possibly other instruments (see PR #905). */6899fluid_voice_noteoff(voice);6900break;6901default:6902FLUID_LOG(FLUID_ERR, "This should never happen: unknown channel type %d", (int)type);6903break;6904}6905}6906}6907}69086909/**6910* Set synthesis interpolation method on one or all MIDI channels.6911* @param synth FluidSynth instance6912* @param chan MIDI channel to set interpolation method on or -1 for all channels6913* @param interp_method Interpolation method (#fluid_interp)6914* @return #FLUID_OK on success, #FLUID_FAILED otherwise6915*/6916int6917fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method)6918{6919int i;69206921fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);6922fluid_synth_api_enter(synth);69236924if(chan < -1 || chan >= synth->midi_channels)6925{6926FLUID_API_RETURN(FLUID_FAILED);6927}69286929if(synth->channel[0] == NULL)6930{6931FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!");6932FLUID_API_RETURN(FLUID_FAILED);6933}69346935for(i = 0; i < synth->midi_channels; i++)6936{6937if(chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan)6938{6939fluid_channel_set_interp_method(synth->channel[i], interp_method);6940}6941}69426943FLUID_API_RETURN(FLUID_OK);6944};69456946/**6947* Get the total count of MIDI channels.6948* @param synth FluidSynth instance6949* @return Count of MIDI channels6950*/6951int6952fluid_synth_count_midi_channels(fluid_synth_t *synth)6953{6954int result;6955fluid_return_val_if_fail(synth != NULL, 0);6956fluid_synth_api_enter(synth);69576958result = synth->midi_channels;6959FLUID_API_RETURN(result);6960}69616962/**6963* Get the total count of audio channels.6964* @param synth FluidSynth instance6965* @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc)6966*/6967int6968fluid_synth_count_audio_channels(fluid_synth_t *synth)6969{6970int result;6971fluid_return_val_if_fail(synth != NULL, 0);6972fluid_synth_api_enter(synth);69736974result = synth->audio_channels;6975FLUID_API_RETURN(result);6976}69776978/**6979* Get the total number of allocated audio channels. Usually identical to the6980* number of audio channels. Can be employed by LADSPA effects subsystem.6981*6982* @param synth FluidSynth instance6983* @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc)6984*/6985int6986fluid_synth_count_audio_groups(fluid_synth_t *synth)6987{6988int result;6989fluid_return_val_if_fail(synth != NULL, 0);6990fluid_synth_api_enter(synth);69916992result = synth->audio_groups;6993FLUID_API_RETURN(result);6994}69956996/**6997* Get the total number of allocated effects channels.6998* @param synth FluidSynth instance6999* @return Count of allocated effects channels7000*/7001int7002fluid_synth_count_effects_channels(fluid_synth_t *synth)7003{7004int result;7005fluid_return_val_if_fail(synth != NULL, 0);7006fluid_synth_api_enter(synth);70077008result = synth->effects_channels;7009FLUID_API_RETURN(result);7010}70117012/**7013* Get the total number of allocated effects units.7014*7015* This is the same number as initially provided by the setting \setting{synth_effects-groups}.7016* @param synth FluidSynth instance7017* @return Count of allocated effects units7018*/7019int7020fluid_synth_count_effects_groups(fluid_synth_t *synth)7021{7022int result;7023fluid_return_val_if_fail(synth != NULL, 0);7024fluid_synth_api_enter(synth);70257026result = synth->effects_groups;7027FLUID_API_RETURN(result);7028}70297030/**7031* Get the synth CPU load value.7032* @param synth FluidSynth instance7033* @return Estimated CPU load value in percent (0-100)7034*/7035double7036fluid_synth_get_cpu_load(fluid_synth_t *synth)7037{7038fluid_return_val_if_fail(synth != NULL, 0);7039return fluid_atomic_float_get(&synth->cpu_load);7040}70417042/* Get tuning for a given bank:program */7043static fluid_tuning_t *7044fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog)7045{70467047if((synth->tuning == NULL) ||7048(synth->tuning[bank] == NULL) ||7049(synth->tuning[bank][prog] == NULL))7050{7051return NULL;7052}70537054return synth->tuning[bank][prog];7055}70567057/* Replace tuning on a given bank:program (need not already exist).7058* Synth mutex should already be locked by caller. */7059static int7060fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, fluid_tuning_t *tuning,7061int bank, int prog, int apply)7062{7063fluid_tuning_t *old_tuning;70647065if(synth->tuning == NULL)7066{7067synth->tuning = FLUID_ARRAY(fluid_tuning_t **, 128);70687069if(synth->tuning == NULL)7070{7071FLUID_LOG(FLUID_PANIC, "Out of memory");7072return FLUID_FAILED;7073}70747075FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t **));7076}70777078if(synth->tuning[bank] == NULL)7079{7080synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t *, 128);70817082if(synth->tuning[bank] == NULL)7083{7084FLUID_LOG(FLUID_PANIC, "Out of memory");7085return FLUID_FAILED;7086}70877088FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t *));7089}70907091old_tuning = synth->tuning[bank][prog];7092synth->tuning[bank][prog] = tuning;70937094if(old_tuning)7095{7096if(!fluid_tuning_unref(old_tuning, 1)) /* -- unref old tuning */7097{7098/* Replace old tuning if present */7099fluid_synth_replace_tuning_LOCAL(synth, old_tuning, tuning, apply, FALSE);7100}7101}71027103return FLUID_OK;7104}71057106/* Replace a tuning with a new one in all MIDI channels. new_tuning can be7107* NULL, in which case channels are reset to default equal tempered scale. */7108static void7109fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, fluid_tuning_t *old_tuning,7110fluid_tuning_t *new_tuning, int apply, int unref_new)7111{7112fluid_channel_t *channel;7113int old_tuning_unref = 0;7114int i;71157116for(i = 0; i < synth->midi_channels; i++)7117{7118channel = synth->channel[i];71197120if(fluid_channel_get_tuning(channel) == old_tuning)7121{7122old_tuning_unref++;71237124if(new_tuning)7125{7126fluid_tuning_ref(new_tuning); /* ++ ref new tuning for channel */7127}71287129fluid_channel_set_tuning(channel, new_tuning);71307131if(apply)7132{7133fluid_synth_update_voice_tuning_LOCAL(synth, channel);7134}7135}7136}71377138/* Send unref old tuning event if any unrefs */7139if(old_tuning && old_tuning_unref)7140{7141fluid_tuning_unref(old_tuning, old_tuning_unref);7142}71437144if(!unref_new || !new_tuning)7145{7146return;7147}71487149fluid_tuning_unref(new_tuning, 1);7150}71517152/* Update voice tunings in realtime */7153static void7154fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, fluid_channel_t *channel)7155{7156fluid_voice_t *voice;7157int i;71587159for(i = 0; i < synth->polyphony; i++)7160{7161voice = synth->voice[i];71627163if(fluid_voice_is_on(voice) && (voice->channel == channel))7164{7165fluid_voice_calculate_gen_pitch(voice);7166fluid_voice_update_param(voice, GEN_PITCH);7167}7168}7169}71707171/**7172* Set the tuning of the entire MIDI note scale.7173* @param synth FluidSynth instance7174* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7175* @param prog Tuning preset number (0-127), not related to MIDI instrument program7176* @param name Label name for this tuning7177* @param pitch Array of pitch values (length of 128, each value is number of7178* cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc).7179* Pass NULL to create a equal tempered (normal) scale.7180* @param apply TRUE to apply new tuning in realtime to existing notes which7181* are using the replaced tuning (if any), FALSE otherwise7182* @return #FLUID_OK on success, #FLUID_FAILED otherwise7183* @since 1.1.07184*/7185int7186fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog,7187const char *name, const double *pitch, int apply)7188{7189fluid_tuning_t *tuning;7190int retval = FLUID_OK;71917192fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7193fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7194fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7195fluid_return_val_if_fail(name != NULL, FLUID_FAILED);71967197fluid_synth_api_enter(synth);71987199tuning = new_fluid_tuning(name, bank, prog);72007201if(tuning)7202{7203if(pitch)7204{7205fluid_tuning_set_all(tuning, pitch);7206}72077208retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply);72097210if(retval == FLUID_FAILED)7211{7212fluid_tuning_unref(tuning, 1);7213}7214}7215else7216{7217retval = FLUID_FAILED;7218}72197220FLUID_API_RETURN(retval);7221}72227223/**7224* Activate an octave tuning on every octave in the MIDI note scale.7225* @param synth FluidSynth instance7226* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7227* @param prog Tuning preset number (0-127), not related to MIDI instrument program7228* @param name Label name for this tuning7229* @param pitch Array of pitch values (length of 12 for each note of an octave7230* starting at note C, values are number of offset cents to add to the normal7231* tuning amount)7232* @param apply TRUE to apply new tuning in realtime to existing notes which7233* are using the replaced tuning (if any), FALSE otherwise7234* @return #FLUID_OK on success, #FLUID_FAILED otherwise7235* @since 1.1.07236*/7237int7238fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog,7239const char *name, const double *pitch, int apply)7240{7241fluid_tuning_t *tuning;7242int retval = FLUID_OK;72437244fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7245fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7246fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7247fluid_return_val_if_fail(name != NULL, FLUID_FAILED);7248fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED);72497250fluid_synth_api_enter(synth);7251tuning = new_fluid_tuning(name, bank, prog);72527253if(tuning)7254{7255fluid_tuning_set_octave(tuning, pitch);7256retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply);72577258if(retval == FLUID_FAILED)7259{7260fluid_tuning_unref(tuning, 1);7261}7262}7263else7264{7265retval = FLUID_FAILED;7266}72677268FLUID_API_RETURN(retval);7269}72707271/**7272* Set tuning values for one or more MIDI notes for an existing tuning.7273* @param synth FluidSynth instance7274* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7275* @param prog Tuning preset number (0-127), not related to MIDI instrument program7276* @param len Number of MIDI notes to assign7277* @param key Array of MIDI key numbers (length of 'len', values 0-127)7278* @param pitch Array of pitch values (length of 'len', values are number of7279* cents from MIDI note 0)7280* @param apply TRUE to apply tuning change in realtime to existing notes using7281* the specified tuning, FALSE otherwise7282* @return #FLUID_OK on success, #FLUID_FAILED otherwise7283*7284* @note Prior to version 1.1.0 it was an error to specify a tuning that didn't7285* already exist. Starting with 1.1.0, the default equal tempered scale will be7286* used as a basis, if no tuning exists for the given bank and prog.7287*/7288int7289fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog,7290int len, const int *key, const double *pitch, int apply)7291{7292fluid_tuning_t *old_tuning, *new_tuning;7293int retval = FLUID_OK;7294int i;72957296fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7297fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7298fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);7299fluid_return_val_if_fail(len > 0, FLUID_FAILED);7300fluid_return_val_if_fail(key != NULL, FLUID_FAILED);7301fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED);73027303fluid_synth_api_enter(synth);73047305old_tuning = fluid_synth_get_tuning(synth, bank, prog);73067307if(old_tuning)7308{7309new_tuning = fluid_tuning_duplicate(old_tuning);7310}7311else7312{7313new_tuning = new_fluid_tuning("Unnamed", bank, prog);7314}73157316if(new_tuning)7317{7318for(i = 0; i < len; i++)7319{7320fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]);7321}73227323retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply);73247325if(retval == FLUID_FAILED)7326{7327fluid_tuning_unref(new_tuning, 1);7328}7329}7330else7331{7332retval = FLUID_FAILED;7333}73347335FLUID_API_RETURN(retval);7336}73377338/**7339* Activate a tuning scale on a MIDI channel.7340* @param synth FluidSynth instance7341* @param chan MIDI channel number (0 to MIDI channel count - 1)7342* @param bank Tuning bank number (0-127), not related to MIDI instrument bank7343* @param prog Tuning preset number (0-127), not related to MIDI instrument program7344* @param apply TRUE to apply tuning change to active notes, FALSE otherwise7345* @return #FLUID_OK on success, #FLUID_FAILED otherwise7346* @since 1.1.07347*7348* @note A default equal tempered scale will be created, if no tuning exists7349* on the given bank and prog.7350*/7351int7352fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog,7353int apply)7354{7355fluid_tuning_t *tuning;7356int retval = FLUID_OK;73577358//fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);7359//fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);7360fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED);7361fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED);73627363FLUID_API_ENTRY_CHAN(FLUID_FAILED);73647365tuning = fluid_synth_get_tuning(synth, bank, prog);73667367/* If no tuning exists, create a new default tuning. We do this, so that7368* it can be replaced later, if any changes are made. */7369if(!tuning)7370{7371tuning = new_fluid_tuning("Unnamed", bank, prog);73727373if(tuning)7374{7375fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE);7376}7377}73787379if(tuning)7380{7381fluid_tuning_ref(tuning); /* ++ ref for outside of lock */7382}73837384if(!tuning)7385{7386FLUID_API_RETURN(FLUID_FAILED);7387}73887389fluid_tuning_ref(tuning); /* ++ ref new tuning for following function */7390retval = fluid_synth_set_tuning_LOCAL(synth, chan, tuning, apply);73917392fluid_tuning_unref(tuning, 1); /* -- unref for outside of lock */73937394FLUID_API_RETURN(retval);7395}73967397/* Local synthesis thread set tuning function (takes over tuning reference) */7398static int7399fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan,7400fluid_tuning_t *tuning, int apply)7401{7402fluid_tuning_t *old_tuning;7403fluid_channel_t *channel;74047405channel = synth->channel[chan];74067407old_tuning = fluid_channel_get_tuning(channel);7408fluid_channel_set_tuning(channel, tuning); /* !! Takes over callers reference */74097410if(apply)7411{7412fluid_synth_update_voice_tuning_LOCAL(synth, channel);7413}74147415/* Send unref old tuning event */7416if(old_tuning)7417{7418fluid_tuning_unref(old_tuning, 1);7419}742074217422return FLUID_OK;7423}74247425/**7426* Clear tuning scale on a MIDI channel (use default equal tempered scale).7427* @param synth FluidSynth instance7428* @param chan MIDI channel number (0 to MIDI channel count - 1)7429* @param apply TRUE to apply tuning change to active notes, FALSE otherwise7430* @return #FLUID_OK on success, #FLUID_FAILED otherwise7431* @since 1.1.07432*/7433int7434fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply)7435{7436int retval = FLUID_OK;74377438FLUID_API_ENTRY_CHAN(FLUID_FAILED);74397440retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply);74417442FLUID_API_RETURN(retval);7443}74447445/**7446* Start tuning iteration.7447* @param synth FluidSynth instance7448*/7449void7450fluid_synth_tuning_iteration_start(fluid_synth_t *synth)7451{7452fluid_return_if_fail(synth != NULL);7453fluid_synth_api_enter(synth);7454fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER(0));7455fluid_synth_api_exit(synth);7456}74577458/**7459* Advance to next tuning.7460* @param synth FluidSynth instance7461* @param bank Location to store MIDI bank number of next tuning scale7462* @param prog Location to store MIDI program number of next tuning scale7463* @return 1 if tuning iteration advanced, 0 if no more tunings7464*/7465int7466fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog)7467{7468void *pval;7469int b = 0, p = 0;74707471fluid_return_val_if_fail(synth != NULL, 0);7472fluid_return_val_if_fail(bank != NULL, 0);7473fluid_return_val_if_fail(prog != NULL, 0);7474fluid_synth_api_enter(synth);74757476/* Current tuning iteration stored as: bank << 8 | program */7477pval = fluid_private_get(synth->tuning_iter);7478p = FLUID_POINTER_TO_INT(pval);7479b = (p >> 8) & 0xFF;7480p &= 0xFF;74817482if(!synth->tuning)7483{7484FLUID_API_RETURN(0);7485}74867487for(; b < 128; b++, p = 0)7488{7489if(synth->tuning[b] == NULL)7490{7491continue;7492}74937494for(; p < 128; p++)7495{7496if(synth->tuning[b][p] == NULL)7497{7498continue;7499}75007501*bank = b;7502*prog = p;75037504if(p < 127)7505{7506fluid_private_set(synth->tuning_iter,7507FLUID_INT_TO_POINTER(b << 8 | (p + 1)));7508}7509else7510{7511fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8));7512}75137514FLUID_API_RETURN(1);7515}7516}75177518FLUID_API_RETURN(0);7519}75207521/**7522* Get the entire note tuning for a given MIDI bank and program.7523* @param synth FluidSynth instance7524* @param bank MIDI bank number of tuning7525* @param prog MIDI program number of tuning7526* @param name Location to store tuning name or NULL to ignore7527* @param len Maximum number of chars to store to 'name' (including NULL byte)7528* @param pitch Array to store tuning scale to or NULL to ignore (len of 128)7529* @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise7530*/7531int7532fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog,7533char *name, int len, double *pitch)7534{7535fluid_tuning_t *tuning;75367537fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);7538fluid_synth_api_enter(synth);75397540tuning = fluid_synth_get_tuning(synth, bank, prog);75417542if(tuning)7543{7544if(name)7545{7546FLUID_SNPRINTF(name, len - 1, "%s", fluid_tuning_get_name(tuning));7547name[len - 1] = 0; /* make sure the string is null terminated */7548}75497550if(pitch)7551{7552FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double));7553}7554}75557556FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED);7557}75587559/**7560* Get settings assigned to a synth.7561* @param synth FluidSynth instance7562* @return FluidSynth settings which are assigned to the synth7563*/7564fluid_settings_t *7565fluid_synth_get_settings(fluid_synth_t *synth)7566{7567fluid_return_val_if_fail(synth != NULL, NULL);75687569return synth->settings;7570}75717572/**7573* Apply an offset to a SoundFont generator on a MIDI channel.7574*7575* This function allows to set an offset for the specified destination generator in real-time.7576* The offset will be applied immediately to all voices that are currently and subsequently playing7577* on the given MIDI channel. This functionality works equivalent to using NRPN MIDI messages to7578* manipulate synthesis parameters. See SoundFont spec, paragraph 8.1.3, for details on SoundFont7579* generator parameters and valid ranges, as well as paragraph 9.6 for details on NRPN messages.7580* @param synth FluidSynth instance7581* @param chan MIDI channel number (0 to MIDI channel count - 1)7582* @param param SoundFont generator ID (#fluid_gen_type)7583* @param value Offset value (in native units of the generator) to assign to the MIDI channel7584* @return #FLUID_OK on success, #FLUID_FAILED otherwise7585*/7586int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value)7587{7588fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);7589FLUID_API_ENTRY_CHAN(FLUID_FAILED);75907591fluid_synth_set_gen_LOCAL(synth, chan, param, value);75927593FLUID_API_RETURN(FLUID_OK);7594}75957596/* Synthesis thread local set gen function */7597static void7598fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value)7599{7600fluid_voice_t *voice;7601int i;76027603fluid_channel_set_gen(synth->channel[chan], param, value);76047605for(i = 0; i < synth->polyphony; i++)7606{7607voice = synth->voice[i];76087609if(fluid_voice_get_channel(voice) == chan)7610{7611fluid_voice_set_param(voice, param, value);7612}7613}7614}76157616// The "SB AWE32 Developer's Information Pack" provides a lookup table for the filter resonance.7617// Instead of a single Q value, a high and low Q value is given. This suggests a variable-Q filter design, which is7618// incompatible to fluidsynth's IIR filter. Therefore we need to somehow derive a single Q value.7619// Options are:7620// * mean7621// * geometric distance (sqrt(q_lo * q_hi))7622// * either q_lo or q_hi7623// * linear interpolation between low and high fc7624// * log interpolation between low and high fc7625static fluid_real_t7626calc_awe32_filter_q(int data, fluid_real_t* fc)7627{7628typedef struct7629{7630fluid_real_t fc_lo;7631fluid_real_t fc_hi;7632fluid_real_t q_lo;7633fluid_real_t q_hi;7634fluid_real_t dc_atten;7635} awe32_q;76367637// Q in dB7638static const awe32_q awe32_q_table[] =7639{7640{92, 22000, 5.f, 0.f, -0.0f }, /* coef 0 */7641{93, 8500, 6.f, 0.5f, -0.5f }, /* coef 1 */7642{94, 8300, 8.f, 1.f, -1.2f }, /* coef 2 */7643{95, 8200, 10.f, 2.f, -1.8f }, /* coef 3 */7644{96, 8100, 11.f, 3.f, -2.5f }, /* coef 4 */7645{97, 8000, 13.f, 4.f, -3.3f }, /* coef 5 */7646{98, 7900, 14.f, 5.f, -4.1f }, /* coef 6 */7647{99, 7800, 16.f, 6.f, -5.5f}, /* coef 7 */7648{100, 7700, 17.f, 7.f, -6.0f }, /* coef 8 */7649{100, 7500, 19.f, 9.f, -6.6f }, /* coef 9 */7650{100, 7400, 20.f, 10.f, -7.2f }, /* coef 10 */7651{100, 7300, 22.f, 11.f, -7.9f }, /* coef 11 */7652{100, 7200, 23.f, 13.f, -8.5f }, /* coef 12 */7653{100, 7100, 25.f, 15.f, -9.3f }, /* coef 13 */7654{100, 7100, 26.f, 16.f, -10.1f },/* coef 14 */7655{100, 7000, 28.f, 18.f, -11.0f}, /* coef 15 */7656};76577658const awe32_q* tab;7659fluid_real_t alpha;76607661fluid_clip(data, 0, 127);7662data /= 8;7663tab = &awe32_q_table[data];76647665fluid_clip(*fc, tab->fc_lo, tab->fc_hi);76667667alpha = (*fc - tab->fc_lo) / (tab->fc_hi - tab->fc_lo);76687669// linearly interpolate between high and low Q7670return 10 * /* cB */ (tab->q_lo * (1.0f - alpha) + tab->q_hi * alpha);76717672// alternatively: log interpolation7673// return 10 * /* cB */ FLUID_POW(tab->q_hi, alpha) * FLUID_POW(tab->q_lo, 1.0f - alpha);7674}76757676/**7677* This implementation is based on "Frequently Asked Questions for SB AWE32" http://archive.gamedev.net/archive/reference/articles/article445.html7678* as well as on the "SB AWE32 Developer's Information Pack" https://github.com/user-attachments/files/15757220/adip301.pdf7679*7680* @param gen the AWE32 effect or generator to manipulate7681* @param data the composed value of DATA_MSB and DATA_LSB7682*/7683static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data, int data_lsb)7684{7685static const enum fluid_gen_type awe32_to_sf2_gen[] =7686{7687// assuming LFO1 maps to MODLFO and LFO2 maps to VIBLFO7688// observe how nicely most of the AWE32 generators here match up with the order of SF2 generators in fluid_gen_type7689GEN_MODLFODELAY, /**< Modulation LFO delay */7690GEN_MODLFOFREQ, /**< Modulation LFO frequency */7691GEN_VIBLFODELAY, /**< Vibrato LFO delay */7692GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */7693GEN_MODENVDELAY, /**< Modulation envelope delay */7694GEN_MODENVATTACK, /**< Modulation envelope attack */7695GEN_MODENVHOLD, /**< Modulation envelope hold */7696GEN_MODENVDECAY, /**< Modulation envelope decay */7697GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */7698GEN_MODENVRELEASE, /**< Modulation envelope release */7699GEN_VOLENVDELAY, /**< Volume envelope delay */7700GEN_VOLENVATTACK, /**< Volume envelope attack */7701GEN_VOLENVHOLD, /**< Volume envelope hold */7702GEN_VOLENVDECAY, /**< Volume envelope decay */7703GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */7704GEN_VOLENVRELEASE, /**< Volume envelope release */7705GEN_PITCH, /**< Initial Pitch */7706GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */7707GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */7708GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */7709GEN_MODLFOTOVOL, /**< Modulation LFO to volume */7710GEN_FILTERFC, /**< Filter cutoff */7711GEN_FILTERQ, /**< Filter Q */7712GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */7713GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */7714GEN_CHORUSSEND, /**< Chorus send amount */7715GEN_REVERBSEND, /**< Reverb send amount */7716};77177718enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen];7719int is_realtime = FALSE, i;7720fluid_real_t converted_sf2_generator_value, q;77217722// The AWE32 NRPN docs say that a value of 8192 is considered to be the middle, i.e. zero.7723// 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.7724data -= 8192;77257726switch(sf2_gen)7727{7728case GEN_MODLFODELAY:7729case GEN_VIBLFODELAY:7730case GEN_MODENVDELAY:7731case GEN_VOLENVDELAY:7732fluid_clip(data, 0, 5900);7733converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0));7734break;77357736case GEN_MODLFOFREQ:7737case GEN_VIBLFOFREQ:7738fluid_clip(data_lsb, 0, 127);7739converted_sf2_generator_value = fluid_hz2ct(data_lsb * (fluid_real_t)0.084 /* Hz */);7740is_realtime = TRUE;7741break;77427743case GEN_MODENVATTACK:7744case GEN_VOLENVATTACK:7745fluid_clip(data, 0, 5940);7746converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0));7747break;77487749case GEN_MODENVHOLD:7750case GEN_VOLENVHOLD:7751fluid_clip(data, 0, 8191);7752converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0));7753break;77547755case GEN_MODENVDECAY:7756case GEN_MODENVRELEASE:7757case GEN_VOLENVDECAY:7758case GEN_VOLENVRELEASE:7759fluid_clip(data, 0, 5940);7760converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0));7761break;77627763case GEN_MODENVSUSTAIN:7764case GEN_VOLENVSUSTAIN:7765fluid_clip(data_lsb, 0, 127);7766converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.75 /* dB */ * 10) /* cB */;7767break;77687769case GEN_PITCH:7770converted_sf2_generator_value = data + 8192;7771// This has the side effect of manipulating the modulation state of the channel's pitchwheel, but7772// I'll buy it, since pitch bend is not a regular SF2 generator and we do a bit of magic there to7773// make it work7774fluid_synth_pitch_bend(synth, chan, converted_sf2_generator_value);7775return;77767777case GEN_MODLFOTOPITCH:7778case GEN_VIBLFOTOPITCH:7779is_realtime = TRUE;7780/* fallthrough */7781case GEN_MODENVTOPITCH:7782fluid_clip(data, -127, 127);7783converted_sf2_generator_value = data * (fluid_real_t)9.375 /* cents */;7784break;77857786case GEN_MODLFOTOVOL:7787fluid_clip(data_lsb, 0, 127);7788converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.1875 /* dB */ * 10.0) /* cB */;7789is_realtime = TRUE;7790break;77917792case GEN_FILTERFC:7793fluid_clip(data_lsb, 0, 127);7794// Yes, DO NOT use data here, Uplift.mid doesn't set DATA_MSB=64, therefore we would always get a negative value after subtracting 8192.7795// Since Uplift.mid sounds fine on hardware though, it seems like AWE32 only inspects DATA_LSB in this case.7796// conversion continues below!7797converted_sf2_generator_value = (data_lsb * 62 /* Hz */);7798FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value);7799is_realtime = TRUE;7800break;78017802case GEN_FILTERQ:7803FLUID_LOG(FLUID_DBG, "AWE32 IIR Q Tab: %d",data_lsb);7804synth->channel[chan]->awe32_filter_coeff = data_lsb;7805return;78067807// Note: The description in the official "SB AWE32 Developer's Information Pack" is probably wrong.7808// There it says: "Positive data value causes a positive phase (from 0 to maximum) filter modulation7809// [...] negative data value causes a negative phase [...]"7810// That doesn't make sense. A filter is a causual system - you cannot change its phase independently7811// of the rest. The text a few section above has it correct, there they speak of the filter's7812// cutoff frequency. In that sense, this is probably intended to behave similar to the logic in SF2.7813// PS: Same applies to the GEN_MODENVTOFILTERFC below!7814case GEN_MODLFOTOFILTERFC:7815fluid_clip(data, -64, 63);7816converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */;7817FLUID_LOG(FLUID_DBG, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value);7818is_realtime = TRUE;7819break;78207821case GEN_MODENVTOFILTERFC:7822fluid_clip(data, -127, 127);7823converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */;7824FLUID_LOG(FLUID_DBG, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value);7825break;78267827case GEN_REVERBSEND:7828fluid_clip(data, 0, 255);7829/* transform the input value */7830converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod.flags1, 256);7831FLUID_LOG(FLUID_DBG, "AWE32 Reverb: %f", converted_sf2_generator_value);7832converted_sf2_generator_value*= fluid_mod_get_amount(&default_reverb_mod);7833break;78347835case GEN_CHORUSSEND:7836fluid_clip(data, 0, 255);7837/* transform the input value */7838converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod.flags1, 256);7839FLUID_LOG(FLUID_DBG, "AWE32 Chorus: %f", converted_sf2_generator_value);7840converted_sf2_generator_value*= fluid_mod_get_amount(&default_chorus_mod);7841break;78427843default:7844// should not happen7845FLUID_LOG(FLUID_WARN, "AWE32 NPRN %d conversion not implemented", gen);7846return;7847}78487849if(sf2_gen == GEN_FILTERFC)7850{7851int coef = synth->channel[chan]->awe32_filter_coeff;7852// 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...7853converted_sf2_generator_value -= 1000;7854q = calc_awe32_filter_q(coef, &converted_sf2_generator_value);7855FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value);7856FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q);78577858converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */);78597860// Safe the "true initial Q"7861fluid_channel_set_override_gen_default(synth->channel[chan], GEN_FILTERQ, q);7862}78637864fluid_channel_set_override_gen_default(synth->channel[chan], sf2_gen, converted_sf2_generator_value);78657866for (i = 0; is_realtime && i < synth->polyphony; i++)7867{7868fluid_voice_t* voice = synth->voice[i];78697870if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan)7871{7872// sets the adjusted generator7873fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value);7874fluid_voice_update_param(voice, sf2_gen);78757876FLUID_LOG(FLUID_DBG, "AWE32 Realtime: adjusting voice id %d, generator %d, chan %d", fluid_voice_get_id(voice), sf2_gen, chan);7877if(sf2_gen == GEN_FILTERFC)7878{7879// also sets the calculated Q7880fluid_voice_gen_set(voice, GEN_FILTERQ, q);7881fluid_voice_update_param(voice, GEN_FILTERQ);7882}7883}7884}7885}78867887/**7888* Retrieve the generator NRPN offset assigned to a MIDI channel.7889*7890* The value returned is in native units of the generator. By default, the offset is zero.7891* @param synth FluidSynth instance7892* @param chan MIDI channel number (0 to MIDI channel count - 1)7893* @param param SoundFont generator ID (#fluid_gen_type)7894* @return Current NRPN generator offset value assigned to the MIDI channel7895*/7896float7897fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param)7898{7899float result;7900fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);7901FLUID_API_ENTRY_CHAN(FLUID_FAILED);79027903result = fluid_channel_get_gen(synth->channel[chan], param);7904FLUID_API_RETURN(result);7905}79067907/**7908* Handle MIDI event from MIDI router, used as a callback function.7909* @param data FluidSynth instance7910* @param event MIDI event to handle7911* @return #FLUID_OK on success, #FLUID_FAILED otherwise7912*/7913int7914fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event)7915{7916fluid_synth_t *synth = (fluid_synth_t *) data;7917int type = fluid_midi_event_get_type(event);7918int chan = fluid_midi_event_get_channel(event);79197920switch(type)7921{7922case NOTE_ON:7923return fluid_synth_noteon(synth, chan,7924fluid_midi_event_get_key(event),7925fluid_midi_event_get_velocity(event));79267927case NOTE_OFF:7928return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event));79297930case CONTROL_CHANGE:7931return fluid_synth_cc(synth, chan,7932fluid_midi_event_get_control(event),7933fluid_midi_event_get_value(event));79347935case PROGRAM_CHANGE:7936return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event));79377938case CHANNEL_PRESSURE:7939return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event));79407941case KEY_PRESSURE:7942return fluid_synth_key_pressure(synth, chan,7943fluid_midi_event_get_key(event),7944fluid_midi_event_get_value(event));79457946case PITCH_BEND:7947return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));79487949case MIDI_SYSTEM_RESET:7950return fluid_synth_system_reset(synth);79517952case MIDI_SYSEX:7953return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE);79547955case MIDI_TEXT:7956case MIDI_LYRIC:7957case MIDI_SET_TEMPO:7958return FLUID_OK;7959}79607961return FLUID_FAILED;7962}79637964/**7965* Create and start voices using an arbitrary preset and a MIDI note on event.7966*7967* Using this function is only supported when the setting @c synth.dynamic-sample-loading is false!7968* @param synth FluidSynth instance7969* @param id Voice group ID to use (can be used with fluid_synth_stop()).7970* @param preset Preset to synthesize7971* @param audio_chan Unused currently, set to 07972* @param chan MIDI channel number (0 to MIDI channel count - 1)7973* @param key MIDI note number (0-127)7974* @param vel MIDI velocity number (1-127)7975* @return #FLUID_OK on success, #FLUID_FAILED otherwise7976*7977* @note Should only be called from within synthesis thread, which includes7978* SoundFont loader preset noteon method.7979*/7980int7981fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset,7982int audio_chan, int chan, int key, int vel)7983{7984int result, dynamic_samples;7985fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);7986fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);7987fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED);7988FLUID_API_ENTRY_CHAN(FLUID_FAILED);79897990fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples);7991if(dynamic_samples)7992{7993// The preset might not be currently used, thus its sample data may not be loaded.7994// This guard is to avoid a NULL deref in rvoice_write().7995FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported.");7996// Although we would be able to select the preset (and load it's samples) we have no way to7997// unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended7998// to be used only when presets have been selected on a MIDI channel.7999// Note that even if the preset is currently selected on a channel, it could be unselected at8000// any time. And we would end up with a NULL sample->data again, because we are not referencing8001// the preset here. Thus failure is our only option.8002result = FLUID_FAILED;8003}8004else8005{8006synth->storeid = id;8007result = fluid_preset_noteon(preset, synth, chan, key, vel);8008}80098010FLUID_API_RETURN(result);8011}80128013/**8014* Stop notes for a given note event voice ID.8015* @param synth FluidSynth instance8016* @param id Voice note event ID8017* @return #FLUID_OK on success, #FLUID_FAILED otherwise8018*8019* @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned8020* if no matching voice note event ID was found. Versions after 1.1.0 only8021* return #FLUID_FAILED if an error occurs.8022*/8023int8024fluid_synth_stop(fluid_synth_t *synth, unsigned int id)8025{8026int result;8027fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8028fluid_synth_api_enter(synth);8029fluid_synth_stop_LOCAL(synth, id);8030result = FLUID_OK;8031FLUID_API_RETURN(result);8032}80338034/* Local synthesis thread variant of fluid_synth_stop */8035static void8036fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id)8037{8038fluid_voice_t *voice;8039int i;80408041for(i = 0; i < synth->polyphony; i++)8042{8043voice = synth->voice[i];80448045if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id))8046{8047fluid_voice_noteoff(voice);8048}8049}8050}80518052/**8053* Offset the bank numbers of a loaded SoundFont, i.e.\ subtract8054* \c offset from any bank number when assigning instruments.8055*8056* @param synth FluidSynth instance8057* @param sfont_id ID of a loaded SoundFont8058* @param offset Bank offset value to apply to all instruments8059* @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise8060*/8061int8062fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset)8063{8064fluid_sfont_t *sfont;8065fluid_list_t *list;80668067fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8068fluid_synth_api_enter(synth);80698070for(list = synth->sfont; list; list = fluid_list_next(list))8071{8072sfont = fluid_list_get(list);80738074if(fluid_sfont_get_id(sfont) == sfont_id)8075{8076sfont->bankofs = offset;8077break;8078}8079}80808081if(!list)8082{8083FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);8084FLUID_API_RETURN(FLUID_FAILED);8085}80868087FLUID_API_RETURN(FLUID_OK);8088}80898090/**8091* Get bank offset of a loaded SoundFont.8092* @param synth FluidSynth instance8093* @param sfont_id ID of a loaded SoundFont8094* @return SoundFont bank offset value8095*/8096int8097fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id)8098{8099fluid_sfont_t *sfont;8100fluid_list_t *list;8101int offset = 0;81028103fluid_return_val_if_fail(synth != NULL, 0);8104fluid_synth_api_enter(synth);81058106for(list = synth->sfont; list; list = fluid_list_next(list))8107{8108sfont = fluid_list_get(list);81098110if(fluid_sfont_get_id(sfont) == sfont_id)8111{8112offset = sfont->bankofs;8113break;8114}8115}81168117if(!list)8118{8119FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);8120FLUID_API_RETURN(0);8121}81228123FLUID_API_RETURN(offset);8124}81258126void8127fluid_synth_api_enter(fluid_synth_t *synth)8128{8129if(synth->use_mutex)8130{8131fluid_rec_mutex_lock(synth->mutex);8132}81338134if(!synth->public_api_count)8135{8136fluid_synth_check_finished_voices(synth);8137}81388139synth->public_api_count++;8140}81418142void fluid_synth_api_exit(fluid_synth_t *synth)8143{8144synth->public_api_count--;81458146if(!synth->public_api_count)8147{8148fluid_rvoice_eventhandler_flush(synth->eventhandler);8149}81508151if(synth->use_mutex)8152{8153fluid_rec_mutex_unlock(synth->mutex);8154}81558156}81578158/**8159* Set midi channel type8160* @param synth FluidSynth instance8161* @param chan MIDI channel number (0 to MIDI channel count - 1)8162* @param type MIDI channel type (#fluid_midi_channel_type)8163* @return #FLUID_OK on success, #FLUID_FAILED otherwise8164* @since 1.1.48165*/8166int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)8167{8168fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED);8169FLUID_API_ENTRY_CHAN(FLUID_FAILED);81708171synth->channel[chan]->channel_type = type;81728173FLUID_API_RETURN(FLUID_OK);8174}81758176/**8177* Return the LADSPA effects instance used by FluidSynth8178*8179* @param synth FluidSynth instance8180* @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active8181*/8182fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)8183{8184fluid_return_val_if_fail(synth != NULL, NULL);81858186return synth->ladspa_fx;8187}81888189/**8190* Configure a general-purpose IIR biquad filter.8191*8192* @param synth FluidSynth instance8193* @param type Type of the IIR filter to use (see #fluid_iir_filter_type)8194* @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags)8195* @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED8196*8197* This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard.8198* By default this filter is off (#FLUID_IIR_DISABLED).8199*/8200int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags)8201{8202int i;8203fluid_voice_t *voice;82048205fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8206fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED);82078208fluid_synth_api_enter(synth);82098210synth->custom_filter_type = type;8211synth->custom_filter_flags = flags;82128213for(i = 0; i < synth->polyphony; i++)8214{8215voice = synth->voice[i];82168217fluid_voice_set_custom_filter(voice, type, flags);8218}82198220FLUID_API_RETURN(FLUID_OK);8221}82228223/**8224* Set the important channels for voice overflow priority calculation.8225*8226* @param synth FluidSynth instance8227* @param channels comma-separated list of channel numbers8228* @return #FLUID_OK on success, otherwise #FLUID_FAILED8229*/8230static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels)8231{8232int i;8233int retval = FLUID_FAILED;8234int *values = NULL;8235int num_values;8236fluid_overflow_prio_t *scores;82378238fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);82398240scores = &synth->overflow;82418242if(scores->num_important_channels < synth->midi_channels)8243{8244scores->important_channels = FLUID_REALLOC(scores->important_channels,8245sizeof(*scores->important_channels) * synth->midi_channels);82468247if(scores->important_channels == NULL)8248{8249FLUID_LOG(FLUID_ERR, "Out of memory");8250goto exit;8251}82528253scores->num_important_channels = synth->midi_channels;8254}82558256FLUID_MEMSET(scores->important_channels, FALSE,8257sizeof(*scores->important_channels) * scores->num_important_channels);82588259if(channels != NULL)8260{8261values = FLUID_ARRAY(int, synth->midi_channels);82628263if(values == NULL)8264{8265FLUID_LOG(FLUID_ERR, "Out of memory");8266goto exit;8267}82688269/* Every channel given in the comma-separated list of channel numbers8270* is set to TRUE, i.e. flagging it as "important". Channel numbers are8271* 1-based. */8272num_values = fluid_settings_split_csv(channels, values, synth->midi_channels);82738274for(i = 0; i < num_values; i++)8275{8276if(values[i] > 0 && values[i] <= synth->midi_channels)8277{8278scores->important_channels[values[i] - 1] = TRUE;8279}8280}8281}82828283retval = FLUID_OK;82848285exit:8286FLUID_FREE(values);8287return retval;8288}82898290/*8291* Handler for synth.overflow.important-channels setting.8292*/8293static void fluid_synth_handle_important_channels(void *data, const char *name,8294const char *value)8295{8296fluid_synth_t *synth = (fluid_synth_t *)data;82978298fluid_synth_api_enter(synth);8299fluid_synth_set_important_channels(synth, value);8300fluid_synth_api_exit(synth);8301}830283038304/* API legato mode *********************************************************/83058306/**8307* Sets the legato mode of a channel.8308*8309* @param synth the synth instance.8310* @param chan MIDI channel number (0 to MIDI channel count - 1).8311* @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.8312*8313* @return8314* - #FLUID_OK on success.8315* - #FLUID_FAILED8316* - \a synth is NULL.8317* - \a chan is outside MIDI channel count.8318* - \a legatomode is invalid.8319*/8320int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode)8321{8322/* checks parameters first */8323fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED);8324fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED);8325FLUID_API_ENTRY_CHAN(FLUID_FAILED);8326/**/8327synth->channel[chan]->legatomode = legatomode;8328/**/8329FLUID_API_RETURN(FLUID_OK);8330}83318332/**8333* Gets the legato mode of a channel.8334*8335* @param synth the synth instance.8336* @param chan MIDI channel number (0 to MIDI channel count - 1).8337* @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.8338*8339* @return8340* - #FLUID_OK on success.8341* - #FLUID_FAILED8342* - \a synth is NULL.8343* - \a chan is outside MIDI channel count.8344* - \a legatomode is NULL.8345*/8346int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode)8347{8348/* checks parameters first */8349fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED);8350FLUID_API_ENTRY_CHAN(FLUID_FAILED);8351/**/8352* legatomode = synth->channel[chan]->legatomode;8353/**/8354FLUID_API_RETURN(FLUID_OK);8355}83568357/* API portamento mode *********************************************************/83588359/**8360* Sets the portamento mode of a channel.8361*8362* @param synth the synth instance.8363* @param chan MIDI channel number (0 to MIDI channel count - 1).8364* @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode.8365* @return8366* - #FLUID_OK on success.8367* - #FLUID_FAILED8368* - \a synth is NULL.8369* - \a chan is outside MIDI channel count.8370* - \a portamentomode is invalid.8371*/8372int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan,8373int portamentomode)8374{8375/* checks parameters first */8376fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED);8377fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED);8378FLUID_API_ENTRY_CHAN(FLUID_FAILED);8379/**/8380synth->channel[chan]->portamentomode = portamentomode;8381/**/8382FLUID_API_RETURN(FLUID_OK);8383}83848385/**8386* Gets the portamento mode of a channel.8387*8388* @param synth the synth instance.8389* @param chan MIDI channel number (0 to MIDI channel count - 1).8390* @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode.8391* @return8392* - #FLUID_OK on success.8393* - #FLUID_FAILED8394* - \a synth is NULL.8395* - \a chan is outside MIDI channel count.8396* - \a portamentomode is NULL.8397*/8398int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan,8399int *portamentomode)8400{8401/* checks parameters first */8402fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED);8403FLUID_API_ENTRY_CHAN(FLUID_FAILED);8404/**/8405* portamentomode = synth->channel[chan]->portamentomode;8406/**/8407FLUID_API_RETURN(FLUID_OK);8408}84098410/* API breath mode *********************************************************/84118412/**8413* Sets the breath mode of a channel.8414*8415* @param synth the synth instance.8416* @param chan MIDI channel number (0 to MIDI channel count - 1).8417* @param breathmode The breath mode as indicated by #fluid_channel_breath_flags.8418*8419* @return8420* - #FLUID_OK on success.8421* - #FLUID_FAILED8422* - \a synth is NULL.8423* - \a chan is outside MIDI channel count.8424*/8425int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode)8426{8427/* checks parameters first */8428FLUID_API_ENTRY_CHAN(FLUID_FAILED);8429/**/8430fluid_channel_set_breath_info(synth->channel[chan], breathmode);8431/**/8432FLUID_API_RETURN(FLUID_OK);8433}84348435/**8436* Gets the breath mode of a channel.8437*8438* @param synth the synth instance.8439* @param chan MIDI channel number (0 to MIDI channel count - 1).8440* @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags.8441*8442* @return8443* - #FLUID_OK on success.8444* - #FLUID_FAILED8445* - \a synth is NULL.8446* - \a chan is outside MIDI channel count.8447* - \a breathmode is NULL.8448*/8449int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode)8450{8451/* checks parameters first */8452fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED);8453FLUID_API_ENTRY_CHAN(FLUID_FAILED);8454/**/8455* breathmode = fluid_channel_get_breath_info(synth->channel[chan]);8456/**/8457FLUID_API_RETURN(FLUID_OK);8458}84598460/** API Poly/mono mode ******************************************************/84618462/*8463* Resets a basic channel group of MIDI channels.8464* @param synth the synth instance.8465* @param chan the beginning channel of the group.8466* @param nbr_chan the number of channel in the group.8467*/8468static void8469fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan)8470{8471int i;84728473for(i = chan; i < chan + nbr_chan; i++)8474{8475fluid_channel_reset_basic_channel_info(synth->channel[i]);8476synth->channel[i]->mode_val = 0;8477}8478}84798480/**8481* Disables and unassigns all channels from a basic channel group.8482*8483* @param synth The synth instance.8484* @param chan The basic channel of the group to reset or -1 to reset all channels.8485* @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset())8486* a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY.8487* All other channels belong to this basic channel group. Make sure to call this function before8488* setting any custom basic channel setup.8489*8490* @return8491* - #FLUID_OK on success.8492* - #FLUID_FAILED8493* - \a synth is NULL.8494* - \a chan is outside MIDI channel count.8495* - \a chan isn't a basic channel.8496*/8497int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan)8498{8499int nbr_chan;85008501/* checks parameters first */8502if(chan < 0)8503{8504fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);8505fluid_synth_api_enter(synth);8506/* The range is all MIDI channels from 0 to MIDI channel count -1 */8507chan = 0; /* beginning chan */8508nbr_chan = synth->midi_channels; /* MIDI Channels number */8509}8510else8511{8512FLUID_API_ENTRY_CHAN(FLUID_FAILED);85138514/* checks if chan is a basic channel */8515if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC))8516{8517FLUID_API_RETURN(FLUID_FAILED);8518}85198520/* The range is all MIDI channels in the group from chan */8521nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */8522}85238524/* resets the range of MIDI channels */8525fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan);8526FLUID_API_RETURN(FLUID_OK);8527}85288529/**8530* Checks if a new basic channel group overlaps the next basic channel group.8531*8532* On success the function returns the possible number of channel for this8533* new basic channel group.8534* The function fails if the new group overlaps the next basic channel group.8535*8536* @param see fluid_synth_set_basic_channel.8537* @return8538* - On success, the effective number of channels for this new basic channel group,8539* #FLUID_FAILED otherwise.8540* - #FLUID_FAILED8541* - \a val has a number of channels overlapping next basic channel group or been8542* above MIDI channel count.8543*/8544static int8545fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val)8546{8547int i, n_chan = synth->midi_channels; /* MIDI Channels count */8548int real_val = val; /* real number of channels in the group */85498550/* adjusts val range */8551if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY)8552{8553real_val = 1; /* mode poly omnioff implies a group of only one channel.*/8554}8555else if(val == 0)8556{8557/* mode poly omnion (0), mono omnion (1), mono omni off (3) */8558/* value 0 means all possible channels from basicchan to MIDI channel count -1.*/8559real_val = n_chan - basicchan;8560}8561/* checks if val range is above MIDI channel count */8562else if(basicchan + val > n_chan)8563{8564return FLUID_FAILED;8565}85668567/* checks if this basic channel group overlaps next basic channel group */8568for(i = basicchan + 1; i < basicchan + real_val; i++)8569{8570if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC)8571{8572/* A value of 0 for val means all possible channels from basicchan to8573to the next basic channel -1 (if any).8574When i reaches the next basic channel group, real_val will be8575limited if it is possible */8576if(val == 0)8577{8578/* limitation of real_val */8579real_val = i - basicchan;8580break;8581}85828583/* overlap with the next basic channel group */8584return FLUID_FAILED;8585}8586}85878588return real_val;8589}85908591/**8592* Sets a new basic channel group only. The function doesn't allow to change an8593* existing basic channel.8594*8595* The function fails if any channel overlaps any existing basic channel group.8596* To make room if necessary, basic channel groups can be cleared using8597* fluid_synth_reset_basic_channel().8598*8599* @param synth the synth instance.8600* @param chan the basic Channel number (0 to MIDI channel count-1).8601* @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes).8602* @param val number of channels in the group.8603* @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY,8604* #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value8605* of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any)8606* or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY8607* as this mode implies a group of only one channel.8608* @return8609* - #FLUID_OK on success.8610* - #FLUID_FAILED8611* - \a synth is NULL.8612* - \a chan is outside MIDI channel count.8613* - \a mode is invalid.8614* - \a val has a number of channels overlapping another basic channel group or been8615* above MIDI channel count.8616* - When the function fails, any existing basic channels aren't modified.8617*/8618int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val)8619{8620/* check parameters */8621fluid_return_val_if_fail(mode >= 0, FLUID_FAILED);8622fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED);8623fluid_return_val_if_fail(val >= 0, FLUID_FAILED);8624FLUID_API_ENTRY_CHAN(FLUID_FAILED);86258626/**/8627if(val > 0 && chan + val > synth->midi_channels)8628{8629FLUID_API_RETURN(FLUID_FAILED);8630}86318632/* Checks if there is an overlap with the next basic channel */8633val = fluid_synth_check_next_basic_channel(synth, chan, mode, val);86348635if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)8636{8637/* overlap with the next or previous channel group */8638FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan);8639FLUID_API_RETURN(FLUID_FAILED);8640}86418642/* sets a new basic channel group */8643fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val);8644/**/8645FLUID_API_RETURN(FLUID_OK);8646}86478648/*8649* Local version of fluid_synth_set_basic_channel(), called internally:8650* - by fluid_synth_set_basic_channel() to set a new basic channel group.8651* - during creation new_fluid_synth() or on CC reset to set a default basic channel group.8652* - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group.8653*8654* @param see fluid_synth_set_basic_channel()8655*/8656static void8657fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val)8658{8659int i;86608661/* sets the basic channel group */8662for(i = basicchan; i < basicchan + val; i++)8663{8664int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */8665int new_val;8666/* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */8667fluid_synth_all_notes_off_LOCAL(synth, i);86688669if(i == basicchan)8670{8671new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */8672new_val = val; /* number of channels in the group */8673}8674else8675{8676new_val = 0; /* val is 0 for other channel than basic channel */8677}86788679/* Channel is enabled */8680new_mode |= FLUID_CHANNEL_ENABLED;8681/* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */8682fluid_channel_set_basic_channel_info(synth->channel[i], new_mode);8683synth->channel[i]->mode_val = new_val;8684}8685}86868687/**8688* Searches a previous basic channel starting from chan.8689*8690* @param synth the synth instance.8691* @param chan starting index of the search (including chan).8692* @return index of the basic channel if found , FLUID_FAILED otherwise.8693*/8694static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan)8695{8696for(; chan >= 0; chan--)8697{8698/* searches previous basic channel */8699if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)8700{8701/* chan is the previous basic channel */8702return chan;8703}8704}87058706return FLUID_FAILED;8707}87088709/**8710* Returns poly mono mode information of any MIDI channel.8711*8712* @param synth the synth instance8713* @param chan MIDI channel number (0 to MIDI channel count - 1)8714* @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled.8715* @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled.8716* @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled.8717* @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL8718* the corresponding information isn't returned.8719*8720* @return8721* - #FLUID_OK on success.8722* - #FLUID_FAILED8723* - \a synth is NULL.8724* - \a chan is outside MIDI channel count.8725*/8726int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan,8727int *basic_chan_out,8728int *mode_out,8729int *val_out)8730{8731int basic_chan = FLUID_FAILED;8732int mode = FLUID_FAILED;8733int val = FLUID_FAILED;87348735/* checks parameters first */8736FLUID_API_ENTRY_CHAN(FLUID_FAILED);87378738if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) &&8739/* chan is enabled , we search the basic channel chan belongs to */8740(basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED)8741{8742mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK;8743val = synth->channel[basic_chan]->mode_val;8744}87458746/* returns the information if they are requested */8747if(basic_chan_out)8748{8749* basic_chan_out = basic_chan;8750}87518752if(mode_out)8753{8754* mode_out = mode;8755}87568757if(val_out)8758{8759* val_out = val;8760}87618762FLUID_API_RETURN(FLUID_OK);8763}876487658766