2016-08-23 18:50:54 +02:00
/* FluidSynth - A Software Synthesizer
*
* Copyright ( C ) 2003 Peter Hanappe and others .
*
* This library is free software ; you can redistribute it and / or
2018-10-18 00:41:02 +02:00
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation ; either version 2.1 of
2016-08-23 18:50:54 +02:00
* the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2018-10-18 00:41:02 +02:00
* Lesser General Public License for more details .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* You should have received a copy of the GNU Lesser General Public
2016-08-23 18:50:54 +02:00
* License along with this library ; if not , write to the Free
* Software Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA
*/
# include "fluid_synth.h"
# include "fluid_sys.h"
# include "fluid_chan.h"
# include "fluid_tuning.h"
# include "fluid_settings.h"
# include "fluid_sfont.h"
# include "fluid_defsfont.h"
# ifdef TRAP_ON_FPE
# define _GNU_SOURCE
# include <fenv.h>
/* seems to not be declared in fenv.h */
2018-10-18 00:41:02 +02:00
extern int feenableexcept ( int excepts ) ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
# define FLUID_API_RETURN(return_value) \
do { fluid_synth_api_exit ( synth ) ; \
return return_value ; } while ( 0 )
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
# define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \
do { if ( FLUID_LIKELY ( synth - > channel [ chan ] - > mode & FLUID_CHANNEL_ENABLED ) ) \
{ } \
else \
{ FLUID_API_RETURN ( return_value ) ; } \
} while ( 0 )
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
# define FLUID_API_ENTRY_CHAN(fail_value) \
fluid_return_val_if_fail ( synth ! = NULL , fail_value ) ; \
fluid_return_val_if_fail ( chan > = 0 , fail_value ) ; \
fluid_synth_api_enter ( synth ) ; \
if ( chan > = synth - > midi_channels ) { \
FLUID_API_RETURN ( fail_value ) ; \
} \
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
static void fluid_synth_init ( void ) ;
static void fluid_synth_api_enter ( fluid_synth_t * synth ) ;
static void fluid_synth_api_exit ( fluid_synth_t * synth ) ;
static int fluid_synth_noteon_LOCAL ( fluid_synth_t * synth , int chan , int key ,
int vel ) ;
static int fluid_synth_noteoff_LOCAL ( fluid_synth_t * synth , int chan , int key ) ;
static int fluid_synth_cc_LOCAL ( fluid_synth_t * synth , int channum , int num ) ;
static int fluid_synth_sysex_midi_tuning ( fluid_synth_t * synth , const char * data ,
int len , char * response ,
int * response_len , int avail_response ,
int * handled , int dryrun ) ;
int fluid_synth_all_notes_off_LOCAL ( fluid_synth_t * synth , int chan ) ;
static int fluid_synth_all_sounds_off_LOCAL ( fluid_synth_t * synth , int chan ) ;
static int fluid_synth_system_reset_LOCAL ( fluid_synth_t * synth ) ;
static int fluid_synth_modulate_voices_LOCAL ( fluid_synth_t * synth , int chan ,
int is_cc , int ctrl ) ;
static int fluid_synth_modulate_voices_all_LOCAL ( fluid_synth_t * synth , int chan ) ;
static int fluid_synth_update_channel_pressure_LOCAL ( fluid_synth_t * synth , int channum ) ;
static int fluid_synth_update_key_pressure_LOCAL ( fluid_synth_t * synth , int chan , int key ) ;
static int fluid_synth_update_pitch_bend_LOCAL ( fluid_synth_t * synth , int chan ) ;
static int fluid_synth_update_pitch_wheel_sens_LOCAL ( fluid_synth_t * synth , int chan ) ;
static int fluid_synth_set_preset ( fluid_synth_t * synth , int chan ,
fluid_preset_t * preset ) ;
static fluid_preset_t *
fluid_synth_get_preset ( fluid_synth_t * synth , int sfontnum ,
int banknum , int prognum ) ;
static fluid_preset_t *
fluid_synth_get_preset_by_sfont_name ( fluid_synth_t * synth , const char * sfontname ,
int banknum , int prognum ) ;
static void fluid_synth_update_presets ( fluid_synth_t * synth ) ;
static void fluid_synth_update_gain_LOCAL ( fluid_synth_t * synth ) ;
static int fluid_synth_update_polyphony_LOCAL ( fluid_synth_t * synth , int new_polyphony ) ;
static void init_dither ( void ) ;
2018-11-23 14:36:52 +01:00
static FLUID_INLINE int16_t round_clip_to_i16 ( float x ) ;
2018-10-18 00:41:02 +02:00
static int fluid_synth_render_blocks ( fluid_synth_t * synth , int blockcount ) ;
static fluid_voice_t * fluid_synth_free_voice_by_kill_LOCAL ( fluid_synth_t * synth ) ;
static void fluid_synth_kill_by_exclusive_class_LOCAL ( fluid_synth_t * synth ,
fluid_voice_t * new_voice ) ;
static int fluid_synth_sfunload_callback ( void * data , unsigned int msec ) ;
static fluid_tuning_t * fluid_synth_get_tuning ( fluid_synth_t * synth ,
int bank , int prog ) ;
static int fluid_synth_replace_tuning_LOCK ( fluid_synth_t * synth ,
fluid_tuning_t * tuning ,
int bank , int prog , int apply ) ;
static void fluid_synth_replace_tuning_LOCAL ( fluid_synth_t * synth ,
fluid_tuning_t * old_tuning ,
fluid_tuning_t * new_tuning ,
int apply , int unref_new ) ;
static void fluid_synth_update_voice_tuning_LOCAL ( fluid_synth_t * synth ,
fluid_channel_t * channel ) ;
static int fluid_synth_set_tuning_LOCAL ( fluid_synth_t * synth , int chan ,
fluid_tuning_t * tuning , int apply ) ;
static void fluid_synth_set_gen_LOCAL ( fluid_synth_t * synth , int chan ,
2019-02-23 17:58:05 +01:00
int param , float value ) ;
2018-10-18 00:41:02 +02:00
static void fluid_synth_stop_LOCAL ( fluid_synth_t * synth , unsigned int id ) ;
static int fluid_synth_set_important_channels ( fluid_synth_t * synth , const char * channels ) ;
/* Callback handlers for real-time settings */
static void fluid_synth_handle_sample_rate ( void * data , const char * name , double value ) ;
static void fluid_synth_handle_gain ( void * data , const char * name , double value ) ;
static void fluid_synth_handle_polyphony ( void * data , const char * name , int value ) ;
static void fluid_synth_handle_device_id ( void * data , const char * name , int value ) ;
static void fluid_synth_handle_overflow ( void * data , const char * name , double value ) ;
static void fluid_synth_handle_important_channels ( void * data , const char * name ,
const char * value ) ;
static void fluid_synth_handle_reverb_chorus_num ( void * data , const char * name , double value ) ;
static void fluid_synth_handle_reverb_chorus_int ( void * data , const char * name , int value ) ;
static void fluid_synth_reset_basic_channel_LOCAL ( fluid_synth_t * synth , int chan , int nbr_chan ) ;
static int fluid_synth_check_next_basic_channel ( fluid_synth_t * synth , int basicchan , int mode , int val ) ;
static void fluid_synth_set_basic_channel_LOCAL ( fluid_synth_t * synth , int basicchan , int mode , int val ) ;
static int fluid_synth_set_reverb_full_LOCAL ( fluid_synth_t * synth , int set , double roomsize ,
double damping , double width , double level ) ;
static int fluid_synth_set_chorus_full_LOCAL ( fluid_synth_t * synth , int set , int nr , double level ,
double speed , double depth_ms , int type ) ;
2016-08-23 18:50:54 +02:00
/***************************************************************
*
* GLOBAL
*/
/* has the synth module been initialized? */
2018-10-18 00:41:02 +02:00
/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */
static fluid_atomic_int_t fluid_synth_initialized = { 0 } ;
2016-08-23 18:50:54 +02:00
/* default modulators
* SF2 .01 page 52 ff :
*
* There is a set of predefined default modulators . They have to be
* explicitly overridden by the sound font in order to turn them off .
*/
2018-10-18 00:41:02 +02:00
static fluid_mod_t default_vel2att_mod ; /* SF2.01 section 8.4.1 */
/*not static */ fluid_mod_t default_vel2filter_mod ; /* SF2.01 section 8.4.2 */
static fluid_mod_t default_at2viblfo_mod ; /* SF2.01 section 8.4.3 */
static fluid_mod_t default_mod2viblfo_mod ; /* SF2.01 section 8.4.4 */
static fluid_mod_t default_att_mod ; /* SF2.01 section 8.4.5 */
static fluid_mod_t default_pan_mod ; /* SF2.01 section 8.4.6 */
static fluid_mod_t default_expr_mod ; /* SF2.01 section 8.4.7 */
static fluid_mod_t default_reverb_mod ; /* SF2.01 section 8.4.8 */
static fluid_mod_t default_chorus_mod ; /* SF2.01 section 8.4.9 */
static fluid_mod_t default_pitch_bend_mod ; /* SF2.01 section 8.4.10 */
static fluid_mod_t custom_balance_mod ; /* Non-standard modulator */
/* custom_breath2att_modulator is not a default modulator specified in SF
it is intended to replace default_vel2att_mod on demand using
API fluid_set_breath_mode ( ) or command shell setbreathmode .
*/
static fluid_mod_t custom_breath2att_mod ;
2016-08-23 18:50:54 +02:00
/* reverb presets */
2018-10-18 00:41:02 +02:00
static const fluid_revmodel_presets_t revmodel_preset [ ] =
{
/* name */ /* roomsize */ /* damp */ /* width */ /* level */
{ " Test 1 " , 0.2f , 0.0f , 0.5f , 0.9f } ,
{ " Test 2 " , 0.4f , 0.2f , 0.5f , 0.8f } ,
{ " Test 3 " , 0.6f , 0.4f , 0.5f , 0.7f } ,
{ " Test 4 " , 0.8f , 0.7f , 0.5f , 0.6f } ,
{ " Test 5 " , 0.8f , 1.0f , 0.5f , 0.5f } ,
2016-08-23 18:50:54 +02:00
} ;
/***************************************************************
*
* INITIALIZATION & UTILITIES
*/
2018-10-18 00:41:02 +02:00
void fluid_synth_settings ( fluid_settings_t * settings )
{
fluid_settings_register_int ( settings , " synth.verbose " , 0 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_int ( settings , " synth.reverb.active " , 1 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_num ( settings , " synth.reverb.room-size " , FLUID_REVERB_DEFAULT_ROOMSIZE , 0.0f , 1.0f , 0 ) ;
fluid_settings_register_num ( settings , " synth.reverb.damp " , FLUID_REVERB_DEFAULT_DAMP , 0.0f , 1.0f , 0 ) ;
fluid_settings_register_num ( settings , " synth.reverb.width " , FLUID_REVERB_DEFAULT_WIDTH , 0.0f , 100.0f , 0 ) ;
fluid_settings_register_num ( settings , " synth.reverb.level " , FLUID_REVERB_DEFAULT_LEVEL , 0.0f , 1.0f , 0 ) ;
fluid_settings_register_int ( settings , " synth.chorus.active " , 1 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_int ( settings , " synth.chorus.nr " , FLUID_CHORUS_DEFAULT_N , 0 , 99 , 0 ) ;
fluid_settings_register_num ( settings , " synth.chorus.level " , FLUID_CHORUS_DEFAULT_LEVEL , 0.0f , 10.0f , 0 ) ;
fluid_settings_register_num ( settings , " synth.chorus.speed " , FLUID_CHORUS_DEFAULT_SPEED , 0.29f , 5.0f , 0 ) ;
fluid_settings_register_num ( settings , " synth.chorus.depth " , FLUID_CHORUS_DEFAULT_DEPTH , 0.0f , 256.0f , 0 ) ;
fluid_settings_register_int ( settings , " synth.ladspa.active " , 0 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_int ( settings , " synth.lock-memory " , 1 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_str ( settings , " midi.portname " , " " , 0 ) ;
# ifdef DEFAULT_SOUNDFONT
fluid_settings_register_str ( settings , " synth.default-soundfont " , DEFAULT_SOUNDFONT , 0 ) ;
# endif
fluid_settings_register_int ( settings , " synth.polyphony " , 256 , 1 , 65535 , 0 ) ;
fluid_settings_register_int ( settings , " synth.midi-channels " , 16 , 16 , 256 , 0 ) ;
fluid_settings_register_num ( settings , " synth.gain " , 0.2f , 0.0f , 10.0f , 0 ) ;
fluid_settings_register_int ( settings , " synth.audio-channels " , 1 , 1 , 128 , 0 ) ;
fluid_settings_register_int ( settings , " synth.audio-groups " , 1 , 1 , 128 , 0 ) ;
fluid_settings_register_int ( settings , " synth.effects-channels " , 2 , 2 , 2 , 0 ) ;
fluid_settings_register_int ( settings , " synth.effects-groups " , 1 , 1 , 128 , 0 ) ;
fluid_settings_register_num ( settings , " synth.sample-rate " , 44100.0f , 8000.0f , 96000.0f , 0 ) ;
fluid_settings_register_int ( settings , " synth.device-id " , 0 , 0 , 126 , 0 ) ;
2018-11-23 14:36:52 +01:00
# ifdef ENABLE_MIXER_THREADS
2018-10-18 00:41:02 +02:00
fluid_settings_register_int ( settings , " synth.cpu-cores " , 1 , 1 , 256 , 0 ) ;
2018-11-23 14:36:52 +01:00
# else
fluid_settings_register_int ( settings , " synth.cpu-cores " , 1 , 1 , 1 , 0 ) ;
# endif
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
fluid_settings_register_int ( settings , " synth.min-note-length " , 10 , 0 , 65535 , 0 ) ;
fluid_settings_register_int ( settings , " synth.threadsafe-api " , 1 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
fluid_settings_register_num ( settings , " synth.overflow.percussion " , 4000 , - 10000 , 10000 , 0 ) ;
fluid_settings_register_num ( settings , " synth.overflow.sustained " , - 1000 , - 10000 , 10000 , 0 ) ;
fluid_settings_register_num ( settings , " synth.overflow.released " , - 2000 , - 10000 , 10000 , 0 ) ;
fluid_settings_register_num ( settings , " synth.overflow.age " , 1000 , - 10000 , 10000 , 0 ) ;
fluid_settings_register_num ( settings , " synth.overflow.volume " , 500 , - 10000 , 10000 , 0 ) ;
fluid_settings_register_num ( settings , " synth.overflow.important " , 5000 , - 50000 , 50000 , 0 ) ;
fluid_settings_register_str ( settings , " synth.overflow.important-channels " , " " , 0 ) ;
fluid_settings_register_str ( settings , " synth.midi-bank-select " , " gs " , 0 ) ;
fluid_settings_add_option ( settings , " synth.midi-bank-select " , " gm " ) ;
fluid_settings_add_option ( settings , " synth.midi-bank-select " , " gs " ) ;
fluid_settings_add_option ( settings , " synth.midi-bank-select " , " xg " ) ;
fluid_settings_add_option ( settings , " synth.midi-bank-select " , " mma " ) ;
fluid_settings_register_int ( settings , " synth.dynamic-sample-loading " , 0 , 0 , 1 , FLUID_HINT_TOGGLED ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get FluidSynth runtime version .
* @ param major Location to store major number
* @ param minor Location to store minor number
* @ param micro Location to store micro number
*/
void fluid_version ( int * major , int * minor , int * micro )
{
2018-10-18 00:41:02 +02:00
* major = FLUIDSYNTH_VERSION_MAJOR ;
* minor = FLUIDSYNTH_VERSION_MINOR ;
* micro = FLUIDSYNTH_VERSION_MICRO ;
2016-08-23 18:50:54 +02:00
}
/**
* Get FluidSynth runtime version as a string .
* @ return FluidSynth version string , which is internal and should not be
* modified or freed .
*/
2018-10-18 00:41:02 +02:00
const char *
fluid_version_str ( void )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
return FLUIDSYNTH_VERSION ;
2016-08-23 18:50:54 +02:00
}
/*
* void fluid_synth_init
*
* Does all the initialization for this module .
*/
static void
fluid_synth_init ( void )
{
# ifdef TRAP_ON_FPE
2018-10-18 00:41:02 +02:00
/* Turn on floating point exception traps */
feenableexcept ( FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID ) ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
init_dither ( ) ;
/* custom_breath2att_mod is not a default modulator specified in SF2.01.
it is intended to replace default_vel2att_mod on demand using
API fluid_set_breath_mode ( ) or command shell setbreathmode .
*/
fluid_mod_set_source1 ( & custom_breath2att_mod , /* The modulator we are programming here */
BREATH_MSB , /* Source. breath MSB corresponds to 2. */
FLUID_MOD_CC /* MIDI continuous controller */
| FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */
| FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */
| FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */
) ;
fluid_mod_set_source2 ( & custom_breath2att_mod , 0 , 0 ) ; /* No 2nd source */
fluid_mod_set_dest ( & custom_breath2att_mod , GEN_ATTENUATION ) ; /* Target: Initial attenuation */
fluid_mod_set_amount ( & custom_breath2att_mod , FLUID_PEAK_ATTENUATION ) ; /* Modulation amount: 960 */
/* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */
fluid_mod_set_source1 ( & default_vel2att_mod , /* The modulator we are programming here */
FLUID_MOD_VELOCITY , /* Source. VELOCITY corresponds to 'index=2'. */
FLUID_MOD_GC /* Not a MIDI continuous controller */
| FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */
| FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */
| FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */
) ;
fluid_mod_set_source2 ( & default_vel2att_mod , 0 , 0 ) ; /* No 2nd source */
fluid_mod_set_dest ( & default_vel2att_mod , GEN_ATTENUATION ) ; /* Target: Initial attenuation */
fluid_mod_set_amount ( & default_vel2att_mod , FLUID_PEAK_ATTENUATION ) ; /* Modulation amount: 960 */
/* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff
* Have to make a design decision here . The specs don ' t make any sense this way or another .
* One sound font , ' Kingston Piano ' , which has been praised for its quality , tries to
* override this modulator with an amount of 0 and positive polarity ( instead of what
* the specs say , D = 1 ) for the secondary source .
* So if we change the polarity to ' positive ' , one of the best free sound fonts works . . .
*/
fluid_mod_set_source1 ( & default_vel2filter_mod , FLUID_MOD_VELOCITY , /* Index=2 */
FLUID_MOD_GC /* CC=0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_NEGATIVE /* D=1 */
) ;
fluid_mod_set_source2 ( & default_vel2filter_mod , FLUID_MOD_VELOCITY , /* Index=2 */
FLUID_MOD_GC /* CC=0 */
| FLUID_MOD_SWITCH /* type=3 */
| FLUID_MOD_UNIPOLAR /* P=0 */
// do not remove | FLUID_MOD_NEGATIVE /* D=1 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_dest ( & default_vel2filter_mod , GEN_FILTERFC ) ; /* Target: Initial filter cutoff */
fluid_mod_set_amount ( & default_vel2filter_mod , - 2400 ) ;
/* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */
fluid_mod_set_source1 ( & default_at2viblfo_mod , FLUID_MOD_CHANNELPRESSURE , /* Index=13 */
FLUID_MOD_GC /* CC=0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_at2viblfo_mod , 0 , 0 ) ; /* no second source */
fluid_mod_set_dest ( & default_at2viblfo_mod , GEN_VIBLFOTOPITCH ) ; /* Target: Vib. LFO => pitch */
fluid_mod_set_amount ( & default_at2viblfo_mod , 50 ) ;
/* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */
fluid_mod_set_source1 ( & default_mod2viblfo_mod , MODULATION_MSB , /* Index=1 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_mod2viblfo_mod , 0 , 0 ) ; /* no second source */
fluid_mod_set_dest ( & default_mod2viblfo_mod , GEN_VIBLFOTOPITCH ) ; /* Target: Vib. LFO => pitch */
fluid_mod_set_amount ( & default_mod2viblfo_mod , 50 ) ;
/* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/
fluid_mod_set_source1 ( & default_att_mod , VOLUME_MSB , /* index=7 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_CONCAVE /* type=1 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_NEGATIVE /* D=1 */
) ;
fluid_mod_set_source2 ( & default_att_mod , 0 , 0 ) ; /* No second source */
fluid_mod_set_dest ( & default_att_mod , GEN_ATTENUATION ) ; /* Target: Initial attenuation */
fluid_mod_set_amount ( & default_att_mod , FLUID_PEAK_ATTENUATION ) ; /* Amount: 960 */
/* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */
fluid_mod_set_source1 ( & default_pan_mod , PAN_MSB , /* index=10 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_BIPOLAR /* P=1 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_pan_mod , 0 , 0 ) ; /* No second source */
fluid_mod_set_dest ( & default_pan_mod , GEN_PAN ) ; /* Target: pan */
/* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000
tenths of a percent " . The center value (64) corresponds to 50%,
so it follows that amount = 50 % x 1000 / % = 500. */
fluid_mod_set_amount ( & default_pan_mod , 500.0 ) ;
/* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/
fluid_mod_set_source1 ( & default_expr_mod , EXPRESSION_MSB , /* index=11 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_CONCAVE /* type=1 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_NEGATIVE /* D=1 */
) ;
fluid_mod_set_source2 ( & default_expr_mod , 0 , 0 ) ; /* No second source */
fluid_mod_set_dest ( & default_expr_mod , GEN_ATTENUATION ) ; /* Target: Initial attenuation */
fluid_mod_set_amount ( & default_expr_mod , FLUID_PEAK_ATTENUATION ) ; /* Amount: 960 */
/* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */
fluid_mod_set_source1 ( & default_reverb_mod , EFFECTS_DEPTH1 , /* index=91 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_reverb_mod , 0 , 0 ) ; /* No second source */
fluid_mod_set_dest ( & default_reverb_mod , GEN_REVERBSEND ) ; /* Target: Reverb send */
fluid_mod_set_amount ( & default_reverb_mod , 200 ) ; /* Amount: 200 ('tenths of a percent') */
/* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */
fluid_mod_set_source1 ( & default_chorus_mod , EFFECTS_DEPTH3 , /* index=93 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_chorus_mod , 0 , 0 ) ; /* No second source */
fluid_mod_set_dest ( & default_chorus_mod , GEN_CHORUSSEND ) ; /* Target: Chorus */
fluid_mod_set_amount ( & default_chorus_mod , 200 ) ; /* Amount: 200 ('tenths of a percent') */
/* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */
fluid_mod_set_source1 ( & default_pitch_bend_mod , FLUID_MOD_PITCHWHEEL , /* Index=14 */
FLUID_MOD_GC /* CC =0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_BIPOLAR /* P=1 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & default_pitch_bend_mod , FLUID_MOD_PITCHWHEELSENS , /* Index = 16 */
FLUID_MOD_GC /* CC=0 */
| FLUID_MOD_LINEAR /* type=0 */
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_dest ( & default_pitch_bend_mod , GEN_PITCH ) ; /* Destination: Initial pitch */
fluid_mod_set_amount ( & default_pitch_bend_mod , 12700.0 ) ; /* Amount: 12700 cents */
/* Non-standard MIDI continuous controller 8 to channel stereo balance */
fluid_mod_set_source1 ( & custom_balance_mod , BALANCE_MSB , /* Index=8 */
FLUID_MOD_CC /* CC=1 */
| FLUID_MOD_CONCAVE /* type=1 */
| FLUID_MOD_BIPOLAR /* P=1 */
| FLUID_MOD_POSITIVE /* D=0 */
) ;
fluid_mod_set_source2 ( & custom_balance_mod , 0 , 0 ) ;
fluid_mod_set_dest ( & custom_balance_mod , GEN_CUSTOM_BALANCE ) ; /* Destination: stereo balance */
/* Amount: 96 dB of attenuation (on the opposite channel) */
fluid_mod_set_amount ( & custom_balance_mod , FLUID_PEAK_ATTENUATION ) ; /* Amount: 960 */
}
static FLUID_INLINE unsigned int fluid_synth_get_ticks ( fluid_synth_t * synth )
{
2016-08-23 18:50:54 +02:00
return fluid_atomic_int_get ( & synth - > ticks_since_start ) ;
}
2018-10-18 00:41:02 +02:00
static FLUID_INLINE void fluid_synth_add_ticks ( fluid_synth_t * synth , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_atomic_int_add ( & synth - > ticks_since_start , val ) ;
2016-08-23 18:50:54 +02:00
}
/***************************************************************
2018-10-18 00:41:02 +02:00
* FLUID SAMPLE TIMERS
* Timers that use written audio data as timing reference
2016-08-23 18:50:54 +02:00
*/
struct _fluid_sample_timer_t
{
2018-10-18 00:41:02 +02:00
fluid_sample_timer_t * next ; /* Single linked list of timers */
unsigned long starttick ;
fluid_timer_callback_t callback ;
void * data ;
int isfinished ;
2016-08-23 18:50:54 +02:00
} ;
/*
* fluid_sample_timer_process - called when synth - > ticks is updated
*/
2018-10-18 00:41:02 +02:00
static void fluid_sample_timer_process ( fluid_synth_t * synth )
{
fluid_sample_timer_t * st , * stnext ;
long msec ;
int cont ;
unsigned int ticks = fluid_synth_get_ticks ( synth ) ;
for ( st = synth - > sample_timers ; st ; st = stnext )
{
/* st may be freed in the callback below. cache it's successor now to avoid use after free */
stnext = st - > next ;
if ( st - > isfinished )
{
continue ;
}
msec = ( long ) ( 1000.0 * ( ( double ) ( ticks - st - > starttick ) ) / synth - > sample_rate ) ;
cont = ( * st - > callback ) ( st - > data , msec ) ;
if ( cont = = 0 )
{
st - > isfinished = 1 ;
}
}
}
fluid_sample_timer_t * new_fluid_sample_timer ( fluid_synth_t * synth , fluid_timer_callback_t callback , void * data )
{
fluid_sample_timer_t * result = FLUID_NEW ( fluid_sample_timer_t ) ;
if ( result = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
return NULL ;
}
result - > starttick = fluid_synth_get_ticks ( synth ) ;
result - > isfinished = 0 ;
result - > data = data ;
result - > callback = callback ;
result - > next = synth - > sample_timers ;
synth - > sample_timers = result ;
return result ;
}
void delete_fluid_sample_timer ( fluid_synth_t * synth , fluid_sample_timer_t * timer )
{
fluid_sample_timer_t * * ptr ;
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_return_if_fail ( timer ! = NULL ) ;
ptr = & synth - > sample_timers ;
while ( * ptr )
{
if ( * ptr = = timer )
{
* ptr = timer - > next ;
FLUID_FREE ( timer ) ;
return ;
}
ptr = & ( ( * ptr ) - > next ) ;
}
2016-08-23 18:50:54 +02:00
}
/***************************************************************
*
* FLUID SYNTH
*/
static FLUID_INLINE void
2018-10-18 00:41:02 +02:00
fluid_synth_update_mixer ( fluid_synth_t * synth , fluid_rvoice_function_t method , int intparam ,
fluid_real_t realparam )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL & & synth - > eventhandler ! = NULL ) ;
fluid_return_if_fail ( synth - > eventhandler - > mixer ! = NULL ) ;
fluid_rvoice_eventhandler_push_int_real ( synth - > eventhandler , method ,
synth - > eventhandler - > mixer ,
intparam , realparam ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL ( fluid_synth_t * synth )
{
int i ;
fluid_settings_getint ( synth - > settings , " synth.min-note-length " , & i ) ;
return ( unsigned int ) ( i * synth - > sample_rate / 1000.0f ) ;
}
2016-08-23 18:50:54 +02:00
/**
* Create new FluidSynth instance .
* @ param settings Configuration parameters to use ( used directly ) .
* @ return New FluidSynth instance or NULL on error
*
2018-10-18 00:41:02 +02:00
* @ note The settings parameter is used directly and should not be modified
2016-08-23 18:50:54 +02:00
* or freed independently .
*/
2018-10-18 00:41:02 +02:00
fluid_synth_t *
2016-08-23 18:50:54 +02:00
new_fluid_synth ( fluid_settings_t * settings )
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth ;
fluid_sfloader_t * loader ;
char * important_channels ;
int i , nbuf , prio_level = 0 ;
int with_ladspa = 0 ;
/* initialize all the conversion tables and other stuff */
if ( fluid_atomic_int_compare_and_exchange ( & fluid_synth_initialized , 0 , 1 ) )
{
fluid_synth_init ( ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* allocate a new synthesizer object */
synth = FLUID_NEW ( fluid_synth_t ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( synth = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
return NULL ;
}
FLUID_MEMSET ( synth , 0 , sizeof ( fluid_synth_t ) ) ;
fluid_rec_mutex_init ( synth - > mutex ) ;
fluid_settings_getint ( settings , " synth.threadsafe-api " , & synth - > use_mutex ) ;
synth - > public_api_count = 0 ;
synth - > settings = settings ;
fluid_settings_getint ( settings , " synth.reverb.active " , & synth - > with_reverb ) ;
fluid_settings_getint ( settings , " synth.chorus.active " , & synth - > with_chorus ) ;
fluid_settings_getint ( settings , " synth.verbose " , & synth - > verbose ) ;
fluid_settings_getint ( settings , " synth.polyphony " , & synth - > polyphony ) ;
fluid_settings_getnum ( settings , " synth.sample-rate " , & synth - > sample_rate ) ;
fluid_settings_getint ( settings , " synth.midi-channels " , & synth - > midi_channels ) ;
fluid_settings_getint ( settings , " synth.audio-channels " , & synth - > audio_channels ) ;
fluid_settings_getint ( settings , " synth.audio-groups " , & synth - > audio_groups ) ;
fluid_settings_getint ( settings , " synth.effects-channels " , & synth - > effects_channels ) ;
fluid_settings_getint ( settings , " synth.effects-groups " , & synth - > effects_groups ) ;
fluid_settings_getnum_float ( settings , " synth.gain " , & synth - > gain ) ;
fluid_settings_getint ( settings , " synth.device-id " , & synth - > device_id ) ;
fluid_settings_getint ( settings , " synth.cpu-cores " , & synth - > cores ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.percussion " , & synth - > overflow . percussion ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.released " , & synth - > overflow . released ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.sustained " , & synth - > overflow . sustained ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.volume " , & synth - > overflow . volume ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.age " , & synth - > overflow . age ) ;
fluid_settings_getnum_float ( settings , " synth.overflow.important " , & synth - > overflow . important ) ;
/* register the callbacks */
fluid_settings_callback_num ( settings , " synth.sample-rate " ,
fluid_synth_handle_sample_rate , synth ) ;
fluid_settings_callback_num ( settings , " synth.gain " ,
fluid_synth_handle_gain , synth ) ;
fluid_settings_callback_int ( settings , " synth.polyphony " ,
fluid_synth_handle_polyphony , synth ) ;
fluid_settings_callback_int ( settings , " synth.device-id " ,
fluid_synth_handle_device_id , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.percussion " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.sustained " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.released " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.age " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.volume " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_num ( settings , " synth.overflow.important " ,
fluid_synth_handle_overflow , synth ) ;
fluid_settings_callback_str ( settings , " synth.overflow.important-channels " ,
fluid_synth_handle_important_channels , synth ) ;
fluid_settings_callback_num ( settings , " synth.reverb.room-size " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_num ( settings , " synth.reverb.damp " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_num ( settings , " synth.reverb.width " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_num ( settings , " synth.reverb.level " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_int ( settings , " synth.reverb.active " ,
fluid_synth_handle_reverb_chorus_int , synth ) ;
fluid_settings_callback_int ( settings , " synth.chorus.active " ,
fluid_synth_handle_reverb_chorus_int , synth ) ;
fluid_settings_callback_int ( settings , " synth.chorus.nr " ,
fluid_synth_handle_reverb_chorus_int , synth ) ;
fluid_settings_callback_num ( settings , " synth.chorus.level " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_num ( settings , " synth.chorus.depth " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
fluid_settings_callback_num ( settings , " synth.chorus.speed " ,
fluid_synth_handle_reverb_chorus_num , synth ) ;
/* do some basic sanity checking on the settings */
if ( synth - > midi_channels % 16 ! = 0 )
{
int n = synth - > midi_channels / 16 ;
synth - > midi_channels = ( n + 1 ) * 16 ;
fluid_settings_setint ( settings , " synth.midi-channels " , synth - > midi_channels ) ;
FLUID_LOG ( FLUID_WARN , " Requested number of MIDI channels is not a multiple of 16. "
" I'll increase the number of channels to the next multiple. " ) ;
}
if ( synth - > audio_channels < 1 )
{
FLUID_LOG ( FLUID_WARN , " Requested number of audio channels is smaller than 1. "
" Changing this setting to 1. " ) ;
synth - > audio_channels = 1 ;
}
else if ( synth - > audio_channels > 128 )
{
FLUID_LOG ( FLUID_WARN , " Requested number of audio channels is too big (%d). "
" Limiting this setting to 128. " , synth - > audio_channels ) ;
synth - > audio_channels = 128 ;
}
if ( synth - > audio_groups < 1 )
{
FLUID_LOG ( FLUID_WARN , " Requested number of audio groups is smaller than 1. "
" Changing this setting to 1. " ) ;
synth - > audio_groups = 1 ;
}
else if ( synth - > audio_groups > 128 )
{
FLUID_LOG ( FLUID_WARN , " Requested number of audio groups is too big (%d). "
" Limiting this setting to 128. " , synth - > audio_groups ) ;
synth - > audio_groups = 128 ;
}
if ( synth - > effects_channels < 2 )
{
FLUID_LOG ( FLUID_WARN , " Invalid number of effects channels (%d). "
" Setting effects channels to 2. " , synth - > effects_channels ) ;
synth - > effects_channels = 2 ;
}
/* The number of buffers is determined by the higher number of nr
* groups / nr audio channels . If LADSPA is unused , they should be
* the same . */
nbuf = synth - > audio_channels ;
if ( synth - > audio_groups > nbuf )
{
nbuf = synth - > audio_groups ;
}
if ( fluid_settings_dupstr ( settings , " synth.overflow.important-channels " ,
& important_channels ) = = FLUID_OK )
{
if ( fluid_synth_set_important_channels ( synth , important_channels ) ! = FLUID_OK )
{
FLUID_LOG ( FLUID_WARN , " Failed to set overflow important channels " ) ;
}
FLUID_FREE ( important_channels ) ;
}
/* as soon as the synth is created it starts playing. */
synth - > state = FLUID_SYNTH_PLAYING ;
synth - > fromkey_portamento = INVALID_NOTE ; /* disable portamento */
fluid_atomic_int_set ( & synth - > ticks_since_start , 0 ) ;
synth - > tuning = NULL ;
fluid_private_init ( synth - > tuning_iter ) ;
/* Initialize multi-core variables if multiple cores enabled */
if ( synth - > cores > 1 )
{
fluid_settings_getint ( synth - > settings , " audio.realtime-prio " , & prio_level ) ;
}
/* Allocate event queue for rvoice mixer */
/* In an overflow situation, a new voice takes about 50 spaces in the queue! */
synth - > eventhandler = new_fluid_rvoice_eventhandler ( synth - > polyphony * 64 ,
synth - > polyphony , nbuf , synth - > effects_channels , synth - > effects_groups , synth - > sample_rate , synth - > cores - 1 , prio_level ) ;
if ( synth - > eventhandler = = NULL )
{
goto error_recovery ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Setup the list of default modulators.
* Needs to happen after eventhandler has been set up , as fluid_synth_enter_api is called in the process */
synth - > default_mod = NULL ;
fluid_synth_add_default_mod ( synth , & default_vel2att_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_vel2filter_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_at2viblfo_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_mod2viblfo_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_att_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_pan_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_expr_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_reverb_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_chorus_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & default_pitch_bend_mod , FLUID_SYNTH_ADD ) ;
fluid_synth_add_default_mod ( synth , & custom_balance_mod , FLUID_SYNTH_ADD ) ;
/* Create and initialize the Fx unit.*/
fluid_settings_getint ( settings , " synth.ladspa.active " , & with_ladspa ) ;
if ( with_ladspa )
{
2016-08-23 18:50:54 +02:00
# ifdef LADSPA
2018-10-18 00:41:02 +02:00
synth - > ladspa_fx = new_fluid_ladspa_fx ( synth - > sample_rate ,
FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE ) ;
if ( synth - > ladspa_fx = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
goto error_recovery ;
}
fluid_rvoice_mixer_set_ladspa ( synth - > eventhandler - > mixer , synth - > ladspa_fx ,
synth - > audio_groups ) ;
# else /* LADSPA */
FLUID_LOG ( FLUID_WARN , " FluidSynth has not been compiled with LADSPA support " ) ;
# endif /* LADSPA */
}
/* allocate and add the default sfont loader */
loader = new_fluid_defsfloader ( settings ) ;
if ( loader = = NULL )
{
FLUID_LOG ( FLUID_WARN , " Failed to create the default SoundFont loader " ) ;
}
else
{
fluid_synth_add_sfloader ( synth , loader ) ;
}
/* allocate all channel objects */
synth - > channel = FLUID_ARRAY ( fluid_channel_t * , synth - > midi_channels ) ;
if ( synth - > channel = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
goto error_recovery ;
}
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
synth - > channel [ i ] = new_fluid_channel ( synth , i ) ;
if ( synth - > channel [ i ] = = NULL )
{
goto error_recovery ;
}
}
/* allocate all synthesis processes */
synth - > nvoice = synth - > polyphony ;
synth - > voice = FLUID_ARRAY ( fluid_voice_t * , synth - > nvoice ) ;
if ( synth - > voice = = NULL )
{
goto error_recovery ;
}
for ( i = 0 ; i < synth - > nvoice ; i + + )
{
synth - > voice [ i ] = new_fluid_voice ( synth - > eventhandler , synth - > sample_rate ) ;
if ( synth - > voice [ i ] = = NULL )
{
goto error_recovery ;
}
}
/* sets a default basic channel */
/* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */
/* (i.e all channels are polyphonic) */
/* Must be called after channel objects allocation */
fluid_synth_set_basic_channel_LOCAL ( synth , 0 , FLUID_CHANNEL_MODE_OMNION_POLY ,
synth - > midi_channels ) ;
synth - > min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL ( synth ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_set_polyphony ,
synth - > polyphony , 0.0f ) ;
fluid_synth_set_reverb_on ( synth , synth - > with_reverb ) ;
fluid_synth_set_chorus_on ( synth , synth - > with_chorus ) ;
synth - > cur = FLUID_BUFSIZE ;
synth - > curmax = 0 ;
synth - > dither_index = 0 ;
{
double room , damp , width , level ;
fluid_settings_getnum ( settings , " synth.reverb.room-size " , & room ) ;
fluid_settings_getnum ( settings , " synth.reverb.damp " , & damp ) ;
fluid_settings_getnum ( settings , " synth.reverb.width " , & width ) ;
fluid_settings_getnum ( settings , " synth.reverb.level " , & level ) ;
fluid_synth_set_reverb_full_LOCAL ( synth ,
FLUID_REVMODEL_SET_ALL ,
room ,
damp ,
width ,
level ) ;
}
{
double level , speed , depth ;
fluid_settings_getint ( settings , " synth.chorus.nr " , & i ) ;
fluid_settings_getnum ( settings , " synth.chorus.level " , & level ) ;
fluid_settings_getnum ( settings , " synth.chorus.speed " , & speed ) ;
fluid_settings_getnum ( settings , " synth.chorus.depth " , & depth ) ;
fluid_synth_set_chorus_full_LOCAL ( synth ,
FLUID_CHORUS_SET_ALL ,
i ,
level ,
speed ,
depth ,
FLUID_CHORUS_DEFAULT_TYPE ) ;
}
2016-08-23 18:50:54 +02:00
synth - > bank_select = FLUID_BANK_STYLE_GS ;
2018-10-18 00:41:02 +02:00
if ( fluid_settings_str_equal ( settings , " synth.midi-bank-select " , " gm " ) )
{
synth - > bank_select = FLUID_BANK_STYLE_GM ;
}
else if ( fluid_settings_str_equal ( settings , " synth.midi-bank-select " , " gs " ) )
{
synth - > bank_select = FLUID_BANK_STYLE_GS ;
}
else if ( fluid_settings_str_equal ( settings , " synth.midi-bank-select " , " xg " ) )
{
synth - > bank_select = FLUID_BANK_STYLE_XG ;
}
else if ( fluid_settings_str_equal ( settings , " synth.midi-bank-select " , " mma " ) )
{
synth - > bank_select = FLUID_BANK_STYLE_MMA ;
}
fluid_synth_process_event_queue ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* FIXME */
synth - > start = fluid_curtime ( ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return synth ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
error_recovery :
delete_fluid_synth ( synth ) ;
return NULL ;
2016-08-23 18:50:54 +02:00
}
/**
* Delete a FluidSynth instance .
* @ param synth FluidSynth instance to delete
*
2018-10-18 00:41:02 +02:00
* @ note Other users of a synthesizer instance , such as audio and MIDI drivers ,
2016-08-23 18:50:54 +02:00
* should be deleted prior to freeing the FluidSynth instance .
*/
2018-10-18 00:41:02 +02:00
void
delete_fluid_synth ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i , k ;
fluid_list_t * list ;
fluid_sfont_t * sfont ;
fluid_sfloader_t * loader ;
fluid_mod_t * default_mod ;
fluid_mod_t * mod ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_profiling_print ( ) ;
/* turn off all voices, needed to unload SoundFont data */
if ( synth - > voice ! = NULL )
{
for ( i = 0 ; i < synth - > nvoice ; i + + )
{
fluid_voice_t * voice = synth - > voice [ i ] ;
if ( ! voice )
{
continue ;
}
fluid_voice_unlock_rvoice ( voice ) ;
fluid_voice_overflow_rvoice_finished ( voice ) ;
if ( fluid_voice_is_playing ( voice ) )
{
fluid_voice_off ( voice ) ;
/* If we only use fluid_voice_off(voice) it will trigger a delayed
* fluid_voice_stop ( voice ) via fluid_synth_check_finished_voices ( ) .
* But here , we are deleting the fluid_synth_t instance so
* fluid_voice_stop ( ) will be never triggered resulting in
* SoundFont data never unloaded ( i . e a serious memory leak ) .
* So , fluid_voice_stop ( ) must be explicitly called to insure
* unloading SoundFont data
*/
fluid_voice_stop ( voice ) ;
}
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* also unset all presets for clean SoundFont unload */
if ( synth - > channel ! = NULL )
{
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
fluid_channel_set_preset ( synth - > channel [ i ] , NULL ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
delete_fluid_rvoice_eventhandler ( synth - > eventhandler ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* delete all the SoundFonts */
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
{
sfont = fluid_list_get ( list ) ;
fluid_sfont_delete_internal ( sfont ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
delete_fluid_list ( synth - > sfont ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* delete all the SoundFont loaders */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > loaders ; list ; list = fluid_list_next ( list ) )
{
loader = ( fluid_sfloader_t * ) fluid_list_get ( list ) ;
fluid_sfloader_delete ( loader ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
delete_fluid_list ( synth - > loaders ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( synth - > channel ! = NULL )
{
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
delete_fluid_channel ( synth - > channel [ i ] ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_FREE ( synth - > channel ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( synth - > voice ! = NULL )
{
for ( i = 0 ; i < synth - > nvoice ; i + + )
{
delete_fluid_voice ( synth - > voice [ i ] ) ;
}
FLUID_FREE ( synth - > voice ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* free the tunings, if any */
if ( synth - > tuning ! = NULL )
{
for ( i = 0 ; i < 128 ; i + + )
{
if ( synth - > tuning [ i ] ! = NULL )
{
for ( k = 0 ; k < 128 ; k + + )
{
delete_fluid_tuning ( synth - > tuning [ i ] [ k ] ) ;
}
FLUID_FREE ( synth - > tuning [ i ] ) ;
}
}
FLUID_FREE ( synth - > tuning ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
fluid_private_free ( synth - > tuning_iter ) ;
2016-08-23 18:50:54 +02:00
# ifdef LADSPA
2018-10-18 00:41:02 +02:00
/* Release the LADSPA effects unit */
delete_fluid_ladspa_fx ( synth - > ladspa_fx ) ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
/* delete all default modulators */
default_mod = synth - > default_mod ;
while ( default_mod ! = NULL )
{
mod = default_mod ;
default_mod = mod - > next ;
delete_fluid_mod ( mod ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_FREE ( synth - > overflow . important_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_rec_mutex_destroy ( synth - > mutex ) ;
FLUID_FREE ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get a textual representation of the last error
* @ param synth FluidSynth instance
* @ return Pointer to string of last error message . Valid until the same
* calling thread calls another FluidSynth function which fails . String is
* internal and should not be modified or freed .
2018-11-23 14:36:52 +01:00
* @ deprecated This function is not thread - safe and does not work with multiple synths .
* It has been deprecated . It may return " " in a future release and will eventually be removed .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
const char *
fluid_synth_error ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2019-02-23 17:58:05 +01:00
return " " ;
2016-08-23 18:50:54 +02:00
}
/**
* Send a note - on event to a FluidSynth object .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param key MIDI note number ( 0 - 127 )
* @ param vel MIDI velocity ( 0 - 127 , 0 = noteoff )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_noteon ( fluid_synth_t * synth , int chan , int key , int vel )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( key > = 0 & & key < = 127 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( vel > = 0 & & vel < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = fluid_synth_noteon_LOCAL ( synth , chan , key , vel ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of fluid_synth_noteon */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_noteon_LOCAL ( fluid_synth_t * synth , int chan , int key , int vel )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* notes with velocity zero go to noteoff */
if ( vel = = 0 )
{
return fluid_synth_noteoff_LOCAL ( synth , chan , key ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
/* makes sure this channel has a preset */
if ( channel - > preset = = NULL )
{
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " noteon \t %d \t %d \t %d \t %05d \t %.3f \t %.3f \t %.3f \t %d \t %s " ,
chan , key , vel , 0 ,
fluid_synth_get_ticks ( synth ) / 44100.0f ,
( fluid_curtime ( ) - synth - > start ) / 1000.0f ,
0.0f , 0 , " channel has no preset " ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_FAILED ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_channel_is_playing_mono ( channel ) ) /* channel is mono or legato CC is On) */
{
/* play the noteOn in monophonic */
return fluid_synth_noteon_mono_LOCAL ( synth , chan , key , vel ) ;
}
else
{
/* channel is poly and legato CC is Off) */
/* plays the noteOn in polyphonic */
/* Sets the note at first position in monophonic list */
/* In the case where the musician intends to inter the channel in monophonic
( by depressing the CC legato on ) , the next noteOn mono could be played legato
with the previous note poly ( if the musician choose this ) .
*/
fluid_channel_set_onenote_monolist ( channel , ( unsigned char ) key ,
( unsigned char ) vel ) ;
/* If there is another voice process on the same channel and key,
advance it to the release phase . */
fluid_synth_release_voice_on_same_note_LOCAL ( synth , chan , key ) ;
/* a noteon poly is passed to fluid_synth_noteon_monopoly_legato().
This allows an opportunity to get this note played legato with a previous
note if a CC PTC have been received before this noteon . This behavior is
a MIDI specification ( see FluidPolymono - 0004. pdf chapter 4.3 - a , 3.4 .11
for details ) .
*/
return fluid_synth_noteon_monopoly_legato ( synth , chan , INVALID_NOTE , key , vel ) ;
}
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Sends a note - off event to a FluidSynth object .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param key MIDI note number ( 0 - 127 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise ( may just mean that no
2016-08-23 18:50:54 +02:00
* voices matched the note off event )
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_noteoff ( fluid_synth_t * synth , int chan , int key )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( key > = 0 & & key < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
result = fluid_synth_noteoff_LOCAL ( synth , chan , key ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of fluid_synth_noteoff */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_noteoff_LOCAL ( fluid_synth_t * synth , int chan , int key )
{
int status ;
fluid_channel_t * channel = synth - > channel [ chan ] ;
if ( fluid_channel_is_playing_mono ( channel ) ) /* channel is mono or legato CC is On) */
{
/* play the noteOff in monophonic */
status = fluid_synth_noteoff_mono_LOCAL ( synth , chan , key ) ;
}
else
{
/* channel is poly and legato CC is Off) */
/* removes the note from the monophonic list */
if ( key = = fluid_channel_last_note ( channel ) )
{
fluid_channel_clear_monolist ( channel ) ;
}
status = fluid_synth_noteoff_monopoly ( synth , chan , key , 0 ) ;
}
/* Changes the state (Valid/Invalid) of the most recent note played in a
staccato manner */
fluid_channel_invalid_prev_note_staccato ( channel ) ;
return status ;
}
/* Damps voices on a channel (turn notes off), if they're sustained by
2016-08-23 18:50:54 +02:00
sustain pedal */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_damp_voices_by_sustain_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel = synth - > channel [ chan ] ;
fluid_voice_t * voice ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ( fluid_voice_get_channel ( voice ) = = chan ) & & fluid_voice_is_sustained ( voice ) )
{
if ( voice - > key = = channel - > key_mono_sustained )
{
/* key_mono_sustained is a possible mono note sustainted
( by sustain or sostenuto pedal ) . It must be marked released
( INVALID_NOTE ) here because it is released only by sustain pedal */
channel - > key_mono_sustained = INVALID_NOTE ;
}
fluid_voice_release ( voice ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* Damps voices on a channel (turn notes off), if they're sustained by
2016-08-23 18:50:54 +02:00
sostenuto pedal */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_damp_voices_by_sostenuto_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel = synth - > channel [ chan ] ;
fluid_voice_t * voice ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ( fluid_voice_get_channel ( voice ) = = chan ) & & fluid_voice_is_sostenuto ( voice ) )
{
if ( voice - > key = = channel - > key_mono_sustained )
{
/* key_mono_sustained is a possible mono note sustainted
( by sustain or sostenuto pedal ) . It must be marked released
( INVALID_NOTE ) here because it is released only by sostenuto pedal */
channel - > key_mono_sustained = INVALID_NOTE ;
}
fluid_voice_release ( voice ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Adds the specified modulator \ c mod as default modulator to the synth . \ c mod will
* take effect for any subsequently created voice .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param mod Modulator info ( values copied , passed in object can be freed immediately afterwards )
* @ param mode Determines how to handle an existing identical modulator ( # fluid_synth_add_mod )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2018-11-23 14:36:52 +01:00
*
2018-10-18 00:41:02 +02:00
* @ note Not realtime safe ( due to internal memory allocation ) and therefore should not be called
* from synthesis context at the risk of stalling audio output .
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_add_default_mod ( fluid_synth_t * synth , const fluid_mod_t * mod , int mode )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_mod_t * default_mod ;
fluid_mod_t * last_mod = NULL ;
fluid_mod_t * new_mod ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( mod ! = NULL , FLUID_FAILED ) ;
2019-01-02 15:33:23 +01:00
/* Checks if modulators sources are valid */
if ( ! fluid_mod_check_sources ( mod , " api fluid_synth_add_default_mod mod " ) )
{
return FLUID_FAILED ;
}
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
default_mod = synth - > default_mod ;
while ( default_mod ! = NULL )
{
if ( fluid_mod_test_identity ( default_mod , mod ) )
{
if ( mode = = FLUID_SYNTH_ADD )
{
default_mod - > amount + = mod - > amount ;
}
else if ( mode = = FLUID_SYNTH_OVERWRITE )
{
default_mod - > amount = mod - > amount ;
}
else
{
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
FLUID_API_RETURN ( FLUID_OK ) ;
}
last_mod = default_mod ;
default_mod = default_mod - > next ;
}
/* Add a new modulator (no existing modulator to add / overwrite). */
new_mod = new_fluid_mod ( ) ;
if ( new_mod = = NULL )
{
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
fluid_mod_clone ( new_mod , mod ) ;
new_mod - > next = NULL ;
if ( last_mod = = NULL )
{
synth - > default_mod = new_mod ;
}
else
{
last_mod - > next = new_mod ;
}
FLUID_API_RETURN ( FLUID_OK ) ;
}
/**
* Removes the specified modulator \ c mod from the synth ' s default modulator list .
* fluid_mod_test_identity ( ) will be used to test modulator matching .
* @ param synth synth instance
* @ param mod The modulator to remove
* @ return # FLUID_OK if a matching modulator was found and successfully removed , # FLUID_FAILED otherwise
2018-11-23 14:36:52 +01:00
*
2018-10-18 00:41:02 +02:00
* @ note Not realtime safe ( due to internal memory allocation ) and therefore should not be called
* from synthesis context at the risk of stalling audio output .
*/
int
fluid_synth_remove_default_mod ( fluid_synth_t * synth , const fluid_mod_t * mod )
{
fluid_mod_t * default_mod ;
fluid_mod_t * last_mod ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( mod ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
last_mod = default_mod = synth - > default_mod ;
while ( default_mod ! = NULL )
{
if ( fluid_mod_test_identity ( default_mod , mod ) )
{
if ( synth - > default_mod = = default_mod )
{
synth - > default_mod = synth - > default_mod - > next ;
}
else
{
last_mod - > next = default_mod - > next ;
}
delete_fluid_mod ( default_mod ) ;
FLUID_API_RETURN ( FLUID_OK ) ;
}
last_mod = default_mod ;
default_mod = default_mod - > next ;
}
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
/**
* Send a MIDI controller event on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param num MIDI controller number ( 0 - 127 )
* @ param val MIDI controller value ( 0 - 127 )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ note This function supports MIDI Global Controllers which will be sent to
* all channels of the basic channel if this basic channel is in mode OmniOff / Mono .
* This is accomplished by sending the CC one MIDI channel below the basic
* channel of the receiver .
* Examples : let a synthesizer with 16 MIDI channels :
* - Let a basic channel 7 in mode 3 ( Omni Off , Mono ) . If MIDI channel 6 is disabled it
* could be used as CC global for all channels belonging to basic channel 7.
* - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used
* as CC global for all channels belonging to basic channel 0.
*/
int
fluid_synth_cc ( fluid_synth_t * synth , int chan , int num , int val )
{
int result = FLUID_FAILED ;
fluid_channel_t * channel ;
fluid_return_val_if_fail ( num > = 0 & & num < = 127 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( val > = 0 & & val < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
channel = synth - > channel [ chan ] ;
if ( channel - > mode & FLUID_CHANNEL_ENABLED )
{
/* chan is enabled */
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " cc \t %d \t %d \t %d " , chan , num , val ) ;
}
fluid_channel_set_cc ( channel , num , val ) ;
result = fluid_synth_cc_LOCAL ( synth , chan , num ) ;
}
else /* chan is disabled so it is a candidate for global channel */
{
/* looks for next basic channel */
int n_chan = synth - > midi_channels ; /* MIDI Channels number */
int basicchan ;
if ( chan < n_chan - 1 )
{
basicchan = chan + 1 ; /* next channel */
}
else
{
basicchan = 0 ; /* wrap to 0 */
}
channel = synth - > channel [ basicchan ] ;
/* Channel must be a basicchan in mode OMNIOFF_MONO */
if ( ( channel - > mode & FLUID_CHANNEL_BASIC ) & &
( ( channel - > mode & FLUID_CHANNEL_MODE_MASK ) = = FLUID_CHANNEL_MODE_OMNIOFF_MONO ) )
{
/* sends cc to all channels in this basic channel */
int i , nbr = channel - > mode_val ;
for ( i = basicchan ; i < basicchan + nbr ; i + + )
{
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " cc \t %d \t %d \t %d " , i , num , val ) ;
}
fluid_channel_set_cc ( synth - > channel [ i ] , num , val ) ;
result = fluid_synth_cc_LOCAL ( synth , i , num ) ;
}
}
/* The channel chan is not a valid 'global channel' */
else
{
result = FLUID_FAILED ;
}
}
FLUID_API_RETURN ( result ) ;
}
2016-08-23 18:50:54 +02:00
2018-11-23 14:36:52 +01:00
/* Local synthesis thread variant of MIDI CC set function.
Most of CC are allowed to modulate but not all . A comment describes if CC num
isn ' t allowed to modulate .
Following explanations should help to understand both MIDI specifications and
Soundfont specifications in regard to MIDI specs .
MIDI specs :
CC LSB ( 32 to 63 ) are LSB contributions to CC MSB ( 0 to 31 ) .
It ' s up to the synthesizer to decide to take LSB values into account or not .
Actually Fluidsynth doesn ' t use CC LSB value inside fluid_voice_update_param ( )
( once fluid_voice_modulate ( ) has been triggered ) . This is because actually
fluidsynth needs only 7 bits resolution ( and not 14 bits ) from these CCs .
So fluidsynth is using only 7 bit MSB ( except for portamento time ) .
In regard to MIDI specs Fluidsynth behaves correctly .
Soundfont specs 2.01 - 8.2 .1 :
To deal correctly with MIDI CC ( regardless if any synth will use CC MSB alone ( 7 bit )
or both CCs MSB , LSB ( 14 bits ) during synthesis ) , SF specs recommend not making use of
CC LSB ( i . e only CC MSB ) in modulator sources to trigger modulation ( i . e modulators
with CC LSB connected to sources inputs should be ignored ) .
These specifics are particularly suited for synths that use 14 bits CCs . In this case ,
the MIDI transmitter sends CC LSB first followed by CC MSB . The MIDI synth receives
both CC LSB and CC MSB but only CC MSB will trigger the modulation .
This will produce correct synthesis parameters update from a correct 14 bits CC .
If in SF specs , modulator sources with CC LSB had been accepted , both CC LSB and
CC MSB will triggers 2 modulations . This leads to incorrect synthesis parameters
update followed by correct synthesis parameters update .
However , as long as fluidsynth will use only CC 7 bits resolution , it is safe to ignore
these SF recommendations on CC receive .
*/
2016-08-23 18:50:54 +02:00
static int
2018-10-18 00:41:02 +02:00
fluid_synth_cc_LOCAL ( fluid_synth_t * synth , int channum , int num )
{
fluid_channel_t * chan = synth - > channel [ channum ] ;
int nrpn_select ;
int value ;
value = fluid_channel_get_cc ( chan , num ) ;
switch ( num )
{
2018-11-23 14:36:52 +01:00
case LOCAL_CONTROL : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
break ;
2018-10-18 00:41:02 +02:00
/* CC omnioff, omnion, mono, poly */
2018-11-23 14:36:52 +01:00
/* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
case POLY_OFF :
case POLY_ON :
case OMNI_OFF :
case OMNI_ON :
/* allowed only if channum is a basic channel */
if ( chan - > mode & FLUID_CHANNEL_BASIC )
{
/* Construction of new_mode from current channel mode and this CC mode */
int new_mode = chan - > mode & FLUID_CHANNEL_MODE_MASK ;
switch ( num )
{
case POLY_OFF :
new_mode | = FLUID_CHANNEL_POLY_OFF ;
break ;
case POLY_ON :
new_mode & = ~ FLUID_CHANNEL_POLY_OFF ;
break ;
case OMNI_OFF :
new_mode | = FLUID_CHANNEL_OMNI_OFF ;
break ;
case OMNI_ON :
new_mode & = ~ FLUID_CHANNEL_OMNI_OFF ;
break ;
default : /* should never happen */
return FLUID_FAILED ;
}
/* MIDI specs: if value is 0 it means all channels from channum to next
basic channel minus 1 ( if any ) or to MIDI channel count minus 1.
However , if value is > 0 ( e . g . 4 ) , the group of channels will be be
limited to 4.
value is ignored for # FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode
implies a group of only one channel .
*/
/* Checks value range and changes this existing basic channel group */
value = fluid_synth_check_next_basic_channel ( synth , channum , new_mode , value ) ;
if ( value ! = FLUID_FAILED )
{
/* reset the current basic channel before changing it */
fluid_synth_reset_basic_channel_LOCAL ( synth , channum , chan - > mode_val ) ;
fluid_synth_set_basic_channel_LOCAL ( synth , channum , new_mode , value ) ;
break ; /* FLUID_OK */
}
}
return FLUID_FAILED ;
2018-11-23 14:36:52 +01:00
case LEGATO_SWITCH : /* not allowed to modulate */
2018-10-18 00:41:02 +02:00
/* handles Poly/mono commutation on Legato pedal On/Off.*/
fluid_channel_cc_legato ( chan , value ) ;
break ;
2018-11-23 14:36:52 +01:00
case PORTAMENTO_SWITCH : /* not allowed to modulate */
2018-10-18 00:41:02 +02:00
/* Special handling of the monophonic list */
/* Invalids the most recent note played in a staccato manner */
fluid_channel_invalid_prev_note_staccato ( chan ) ;
break ;
2018-11-23 14:36:52 +01:00
case SUSTAIN_SWITCH : /* not allowed to modulate */
2018-10-18 00:41:02 +02:00
/* Release voices if Sustain switch is released */
if ( value < 64 ) /* Sustain is released */
{
fluid_synth_damp_voices_by_sustain_LOCAL ( synth , channum ) ;
}
break ;
2018-11-23 14:36:52 +01:00
case SOSTENUTO_SWITCH : /* not allowed to modulate */
2018-10-18 00:41:02 +02:00
/* Release voices if Sostetuno switch is released */
if ( value < 64 ) /* Sostenuto is released */
{
fluid_synth_damp_voices_by_sostenuto_LOCAL ( synth , channum ) ;
}
else /* Sostenuto is depressed */
/* Update sostenuto order id when pedaling on Sostenuto */
{
chan - > sostenuto_orderid = synth - > noteid ; /* future voice id value */
}
break ;
2018-11-23 14:36:52 +01:00
case BANK_SELECT_MSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_channel_set_bank_msb ( chan , value & 0x7F ) ;
break ;
2018-11-23 14:36:52 +01:00
case BANK_SELECT_LSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_channel_set_bank_lsb ( chan , value & 0x7F ) ;
break ;
2018-11-23 14:36:52 +01:00
case ALL_NOTES_OFF : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_synth_all_notes_off_LOCAL ( synth , channum ) ;
break ;
2018-11-23 14:36:52 +01:00
case ALL_SOUND_OFF : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_synth_all_sounds_off_LOCAL ( synth , channum ) ;
break ;
2018-11-23 14:36:52 +01:00
case ALL_CTRL_OFF : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_channel_init_ctrl ( chan , 1 ) ;
fluid_synth_modulate_voices_all_LOCAL ( synth , channum ) ;
break ;
2018-11-23 14:36:52 +01:00
case DATA_ENTRY_LSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
break ;
case DATA_ENTRY_MSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
{
int data = ( value < < 7 ) + fluid_channel_get_cc ( chan , DATA_ENTRY_LSB ) ;
if ( chan - > nrpn_active ) /* NRPN is active? */
{
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if ( ( fluid_channel_get_cc ( chan , NRPN_MSB ) = = 120 )
& & ( fluid_channel_get_cc ( chan , NRPN_LSB ) < 100 ) )
{
nrpn_select = chan - > nrpn_select ;
if ( nrpn_select < GEN_LAST )
{
float val = fluid_gen_scale_nrpn ( nrpn_select , data ) ;
2019-02-23 17:58:05 +01:00
fluid_synth_set_gen_LOCAL ( synth , channum , nrpn_select , val ) ;
2018-10-18 00:41:02 +02:00
}
chan - > nrpn_select = 0 ; /* Reset to 0 */
}
}
else if ( fluid_channel_get_cc ( chan , RPN_MSB ) = = 0 ) /* RPN is active: MSB = 0? */
{
switch ( fluid_channel_get_cc ( chan , RPN_LSB ) )
{
case RPN_PITCH_BEND_RANGE : /* Set bend range in semitones */
fluid_channel_set_pitch_wheel_sensitivity ( synth - > channel [ channum ] , value ) ;
fluid_synth_update_pitch_wheel_sens_LOCAL ( synth , channum ) ; /* Update bend range */
/* FIXME - Handle LSB? (Fine bend range in cents) */
break ;
case RPN_CHANNEL_FINE_TUNE : /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */
fluid_synth_set_gen_LOCAL ( synth , channum , GEN_FINETUNE ,
2019-02-23 17:58:05 +01:00
( data - 8192 ) / 8192.0 * 100.0 ) ;
2018-10-18 00:41:02 +02:00
break ;
case RPN_CHANNEL_COARSE_TUNE : /* Coarse tune is 7 bit and in semitones (64 is center) */
fluid_synth_set_gen_LOCAL ( synth , channum , GEN_COARSETUNE ,
2019-02-23 17:58:05 +01:00
value - 64 ) ;
2018-10-18 00:41:02 +02:00
break ;
case RPN_TUNING_PROGRAM_CHANGE :
fluid_channel_set_tuning_prog ( chan , value ) ;
fluid_synth_activate_tuning ( synth , channum ,
fluid_channel_get_tuning_bank ( chan ) ,
value , TRUE ) ;
break ;
case RPN_TUNING_BANK_SELECT :
fluid_channel_set_tuning_bank ( chan , value ) ;
break ;
case RPN_MODULATION_DEPTH_RANGE :
break ;
}
}
break ;
}
2018-11-23 14:36:52 +01:00
case NRPN_MSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
fluid_channel_set_cc ( chan , NRPN_LSB , 0 ) ;
chan - > nrpn_select = 0 ;
chan - > nrpn_active = 1 ;
break ;
2018-11-23 14:36:52 +01:00
case NRPN_LSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if ( fluid_channel_get_cc ( chan , NRPN_MSB ) = = 120 )
{
if ( value = = 100 )
{
chan - > nrpn_select + = 100 ;
}
else if ( value = = 101 )
{
chan - > nrpn_select + = 1000 ;
}
else if ( value = = 102 )
{
chan - > nrpn_select + = 10000 ;
}
else if ( value < 100 )
{
chan - > nrpn_select + = value ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
chan - > nrpn_active = 1 ;
break ;
2018-11-23 14:36:52 +01:00
case RPN_MSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
case RPN_LSB : /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
2018-10-18 00:41:02 +02:00
chan - > nrpn_active = 0 ;
break ;
case BREATH_MSB :
/* handles CC Breath On/Off noteOn/noteOff mode */
fluid_channel_cc_breath_note_on_off ( chan , value ) ;
/* fall-through */
default :
2018-11-23 14:36:52 +01:00
/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */
/* However, as long fluidsynth will use only CC 7 bits resolution, it
is safe to ignore these SF recommendations on CC receive . See
explanations above */
/* if (! (32 <= num && num <= 63)) */
{
return fluid_synth_modulate_voices_LOCAL ( synth , channum , 1 , num ) ;
}
2018-10-18 00:41:02 +02:00
}
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Get current MIDI controller value on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param num MIDI controller number ( 0 - 127 )
* @ param pval Location to store MIDI controller value ( 0 - 127 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_cc ( fluid_synth_t * synth , int chan , int num , int * pval )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( num > = 0 & & num < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( pval ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
* pval = fluid_channel_get_cc ( synth - > channel [ chan ] , num ) ;
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/*
* Handler for synth . device - id setting .
*/
2018-10-18 00:41:02 +02:00
static void
fluid_synth_handle_device_id ( void * data , const char * name , int value )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
synth - > device_id = value ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Process a MIDI SYSEX ( system exclusive ) message .
* @ param synth FluidSynth instance
* @ param data Buffer containing SYSEX data ( not including 0xF0 and 0xF7 )
* @ param len Length of data in buffer
* @ param response Buffer to store response to or NULL to ignore
* @ param response_len IN / OUT parameter , in : size of response buffer , out :
* amount of data written to response buffer ( if FLUID_FAILED is returned and
* this value is non - zero , it indicates the response buffer is too small )
* @ param handled Optional location to store boolean value if message was
* recognized and handled or not ( set to TRUE if it was handled )
* @ param dryrun TRUE to just do a dry run but not actually execute the SYSEX
* command ( useful for checking if a SYSEX message would be handled )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*/
/* SYSEX format (0xF0 and 0xF7 not passed to this function):
* Non - realtime : 0xF0 0x7E < DeviceId > [ BODY ] 0xF7
* Realtime : 0xF0 0x7F < DeviceId > [ BODY ] 0xF7
* Tuning messages : 0xF0 0x7E / 0x7F < DeviceId > 0x08 < sub ID2 > [ BODY ] < ChkSum > 0xF7
*/
int
fluid_synth_sysex ( fluid_synth_t * synth , const char * data , int len ,
char * response , int * response_len , int * handled , int dryrun )
{
2018-10-18 00:41:02 +02:00
int avail_response = 0 ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( handled )
{
* handled = FALSE ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( response_len )
{
avail_response = * response_len ;
* response_len = 0 ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( data ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( len > 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( ! response | | response_len , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( len < 4 )
{
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* MIDI tuning SYSEX message? */
if ( ( data [ 0 ] = = MIDI_SYSEX_UNIV_NON_REALTIME | | data [ 0 ] = = MIDI_SYSEX_UNIV_REALTIME )
& & ( data [ 1 ] = = synth - > device_id | | data [ 1 ] = = MIDI_SYSEX_DEVICE_ID_ALL )
& & data [ 2 ] = = MIDI_SYSEX_MIDI_TUNING_ID )
{
int result ;
fluid_synth_api_enter ( synth ) ;
result = fluid_synth_sysex_midi_tuning ( synth , data , len , response ,
response_len , avail_response ,
handled , dryrun ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
}
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/* Handler for MIDI tuning SYSEX messages */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_sysex_midi_tuning ( fluid_synth_t * synth , const char * data , int len ,
char * response , int * response_len , int avail_response ,
int * handled , int dryrun )
{
int realtime , msgid ;
int bank = 0 , prog , channels ;
double tunedata [ 128 ] ;
int keys [ 128 ] ;
2019-01-02 15:33:23 +01:00
char name [ 17 ] = { 0 } ;
2018-10-18 00:41:02 +02:00
int note , frac , frac2 ;
uint8_t chksum ;
int i , count , index ;
const char * dataptr ;
char * resptr ; ;
realtime = data [ 0 ] = = MIDI_SYSEX_UNIV_REALTIME ;
msgid = data [ 3 ] ;
switch ( msgid )
{
2016-08-23 18:50:54 +02:00
case MIDI_SYSEX_TUNING_BULK_DUMP_REQ :
case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK :
2018-10-18 00:41:02 +02:00
if ( data [ 3 ] = = MIDI_SYSEX_TUNING_BULK_DUMP_REQ )
{
if ( len ! = 5 | | data [ 4 ] & 0x80 | | ! response )
{
return FLUID_OK ;
}
* response_len = 406 ;
prog = data [ 4 ] ;
}
else
{
if ( len ! = 6 | | data [ 4 ] & 0x80 | | data [ 5 ] & 0x80 | | ! response )
{
return FLUID_OK ;
}
* response_len = 407 ;
bank = data [ 4 ] ;
prog = data [ 5 ] ;
}
if ( dryrun )
{
if ( handled )
{
* handled = TRUE ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( avail_response < * response_len )
{
return FLUID_FAILED ;
}
/* Get tuning data, return if tuning not found */
if ( fluid_synth_tuning_dump ( synth , bank , prog , name , 17 , tunedata ) = = FLUID_FAILED )
{
* response_len = 0 ;
return FLUID_OK ;
}
resptr = response ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
* resptr + + = MIDI_SYSEX_UNIV_NON_REALTIME ;
* resptr + + = synth - > device_id ;
* resptr + + = MIDI_SYSEX_MIDI_TUNING_ID ;
* resptr + + = MIDI_SYSEX_TUNING_BULK_DUMP ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( msgid = = MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK )
{
* resptr + + = bank ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
* resptr + + = prog ;
2019-01-02 15:33:23 +01:00
/* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */
FLUID_MEMCPY ( resptr , name , 16 ) ;
2018-10-18 00:41:02 +02:00
resptr + = 16 ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < 128 ; i + + )
{
note = tunedata [ i ] / 100.0 ;
fluid_clip ( note , 0 , 127 ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
frac = ( ( tunedata [ i ] - note * 100.0 ) * 16384.0 + 50.0 ) / 100.0 ;
fluid_clip ( frac , 0 , 16383 ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
* resptr + + = note ;
* resptr + + = frac > > 7 ;
* resptr + + = frac & 0x7F ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( msgid = = MIDI_SYSEX_TUNING_BULK_DUMP_REQ )
{
/* NOTE: Checksum is not as straight forward as the bank based messages */
chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID
^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog ;
for ( i = 21 ; i < 128 * 3 + 21 ; i + + )
{
chksum ^ = response [ i ] ;
}
}
else
{
for ( i = 1 , chksum = 0 ; i < 406 ; i + + )
{
chksum ^ = response [ i ] ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
* resptr + + = chksum & 0x7F ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( handled )
{
* handled = TRUE ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
break ;
2016-08-23 18:50:54 +02:00
case MIDI_SYSEX_TUNING_NOTE_TUNE :
case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK :
2018-10-18 00:41:02 +02:00
dataptr = data + 4 ;
if ( msgid = = MIDI_SYSEX_TUNING_NOTE_TUNE )
{
if ( len < 10 | | data [ 4 ] & 0x80 | | data [ 5 ] & 0x80 | | len ! = data [ 5 ] * 4 + 6 )
{
return FLUID_OK ;
}
}
else
{
if ( len < 11 | | data [ 4 ] & 0x80 | | data [ 5 ] & 0x80 | | data [ 6 ] & 0x80
| | len ! = data [ 6 ] * 4 + 7 )
{
return FLUID_OK ;
}
bank = * dataptr + + ;
}
if ( dryrun )
{
if ( handled )
{
* handled = TRUE ;
}
return FLUID_OK ;
}
prog = * dataptr + + ;
count = * dataptr + + ;
for ( i = 0 , index = 0 ; i < count ; i + + )
{
note = * dataptr + + ;
if ( note & 0x80 )
{
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
keys [ index ] = note ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
note = * dataptr + + ;
frac = * dataptr + + ;
frac2 = * dataptr + + ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( note & 0x80 | | frac & 0x80 | | frac2 & 0x80 )
{
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
frac = frac < < 7 | frac2 ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* No change pitch value? Doesn't really make sense to send that, but.. */
if ( note = = 0x7F & & frac = = 16383 )
{
continue ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
tunedata [ index ] = note * 100.0 + ( frac * 100.0 / 16384.0 ) ;
index + + ;
}
if ( index > 0 )
{
if ( fluid_synth_tune_notes ( synth , bank , prog , index , keys , tunedata ,
realtime ) = = FLUID_FAILED )
{
return FLUID_FAILED ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( handled )
{
* handled = TRUE ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
break ;
2016-08-23 18:50:54 +02:00
case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE :
case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE :
2018-10-18 00:41:02 +02:00
if ( ( msgid = = MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE & & len ! = 19 )
| | ( msgid = = MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE & & len ! = 31 ) )
{
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( data [ 4 ] & 0x80 | | data [ 5 ] & 0x80 | | data [ 6 ] & 0x80 )
{
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( dryrun )
{
if ( handled )
{
* handled = TRUE ;
}
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channels = ( data [ 4 ] & 0x03 ) < < 14 | data [ 5 ] < < 7 | data [ 6 ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( msgid = = MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < 12 ; i + + )
{
frac = data [ i + 7 ] ;
if ( frac & 0x80 )
{
return FLUID_OK ;
}
tunedata [ i ] = ( int ) frac - 64 ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
else
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < 12 ; i + + )
{
frac = data [ i * 2 + 7 ] ;
frac2 = data [ i * 2 + 8 ] ;
if ( frac & 0x80 | | frac2 & 0x80 )
{
return FLUID_OK ;
}
tunedata [ i ] = ( ( ( int ) frac < < 7 | ( int ) frac2 ) - 8192 ) * ( 200.0 / 16384.0 ) ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( fluid_synth_activate_octave_tuning ( synth , 0 , 0 , " SYSEX " ,
2016-08-23 18:50:54 +02:00
tunedata , realtime ) = = FLUID_FAILED )
2018-10-18 00:41:02 +02:00
{
return FLUID_FAILED ;
}
if ( channels )
{
for ( i = 0 ; i < 16 ; i + + )
{
if ( channels & ( 1 < < i ) )
{
fluid_synth_activate_tuning ( synth , i , 0 , 0 , realtime ) ;
}
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( handled )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
* handled = TRUE ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
break ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Turn off all notes on a MIDI channel ( put them into release phase ) .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) , ( chan = - 1 selects all channels )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .4
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_all_notes_off ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( chan > = - 1 , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( chan > = synth - > midi_channels )
{
result = FLUID_FAILED ;
}
else
{
/* Allowed (even for channel disabled) as chan = -1 selects all channels */
result = fluid_synth_all_notes_off_LOCAL ( synth , chan ) ;
}
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */
2018-10-18 00:41:02 +02:00
//static int
int
fluid_synth_all_notes_off_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_playing ( voice ) & & ( ( - 1 = = chan ) | | ( chan = = fluid_voice_get_channel ( voice ) ) ) )
{
fluid_voice_noteoff ( voice ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Immediately stop all notes on a MIDI channel ( skips release phase ) .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) , ( chan = - 1 selects all channels )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .4
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_all_sounds_off ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( chan > = - 1 , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
if ( chan > = synth - > midi_channels )
{
result = FLUID_FAILED ;
}
else
{
/* Allowed (even for channel disabled) as chan = -1 selects all channels */
result = fluid_synth_all_sounds_off_LOCAL ( synth , chan ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_all_sounds_off_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_playing ( voice ) & & ( ( - 1 = = chan ) | | ( chan = = fluid_voice_get_channel ( voice ) ) ) )
{
fluid_voice_off ( voice ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Reset reverb engine
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_reset_reverb ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_reset_reverb , 0 , 0.0f ) ;
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Reset chorus engine
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_reset_chorus ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_reset_chorus , 0 , 0.0f ) ;
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Send MIDI system reset command ( big red ' panic ' button ) , turns off notes , resets
* controllers and restores initial basic channel configuration .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_system_reset ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
result = fluid_synth_system_reset_LOCAL ( synth ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local variant of the system reset command */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_system_reset_LOCAL ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_all_sounds_off_LOCAL ( synth , - 1 ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
fluid_channel_reset ( synth - > channel [ i ] ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Basic channel 0, Mode Omni On Poly */
fluid_synth_set_basic_channel ( synth , 0 , FLUID_CHANNEL_MODE_OMNION_POLY ,
synth - > midi_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_reset_reverb , 0 , 0.0f ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_reset_chorus , 0 , 0.0f ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Update voices on a MIDI channel after a MIDI control change .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param is_cc Boolean value indicating if ctrl is a CC controller or not
* @ param ctrl MIDI controller value
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
static int
2018-10-18 00:41:02 +02:00
fluid_synth_modulate_voices_LOCAL ( fluid_synth_t * synth , int chan , int is_cc , int ctrl )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_get_channel ( voice ) = = chan )
{
fluid_voice_modulate ( voice , is_cc , ctrl ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Update voices on a MIDI channel after all MIDI controllers have been changed .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
static int
2018-10-18 00:41:02 +02:00
fluid_synth_modulate_voices_all_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_get_channel ( voice ) = = chan )
{
fluid_voice_modulate_all ( voice ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Set the MIDI channel pressure controller value .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param val MIDI channel pressure value ( 0 - 127 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_channel_pressure ( fluid_synth_t * synth , int chan , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( val > = 0 & & val < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " channelpressure \t %d \t %d " , chan , val ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_channel_set_channel_pressure ( synth - > channel [ chan ] , val ) ;
result = fluid_synth_update_channel_pressure_LOCAL ( synth , chan ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Updates channel pressure from within synthesis thread */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_update_channel_pressure_LOCAL ( fluid_synth_t * synth , int chan )
{
return fluid_synth_modulate_voices_LOCAL ( synth , chan , 0 , FLUID_MOD_CHANNELPRESSURE ) ;
}
/**
* Set the MIDI polyphonic key pressure controller value .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param key MIDI key number ( 0 - 127 )
* @ param val MIDI key pressure value ( 0 - 127 )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ since 2.0 .0
*/
int
fluid_synth_key_pressure ( fluid_synth_t * synth , int chan , int key , int val )
{
int result ;
fluid_return_val_if_fail ( key > = 0 & & key < = 127 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( val > = 0 & & val < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " keypressure \t %d \t %d \t %d " , chan , key , val ) ;
}
fluid_channel_set_key_pressure ( synth - > channel [ chan ] , key , val ) ;
result = fluid_synth_update_key_pressure_LOCAL ( synth , chan , key ) ;
FLUID_API_RETURN ( result ) ;
}
/* Updates key pressure from within synthesis thread */
static int
fluid_synth_update_key_pressure_LOCAL ( fluid_synth_t * synth , int chan , int key )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
int result = FLUID_OK ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
if ( voice - > chan = = chan & & voice - > key = = key )
{
result = fluid_voice_modulate ( voice , 0 , FLUID_MOD_KEYPRESSURE ) ;
if ( result ! = FLUID_OK )
{
return result ;
}
}
}
return result ;
2016-08-23 18:50:54 +02:00
}
/**
* Set the MIDI pitch bend controller value on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param val MIDI pitch bend value ( 0 - 16383 with 8192 being center )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_pitch_bend ( fluid_synth_t * synth , int chan , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( val > = 0 & & val < = 16383 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " pitchb \t %d \t %d " , chan , val ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_channel_set_pitch_bend ( synth - > channel [ chan ] , val ) ;
result = fluid_synth_update_pitch_bend_LOCAL ( synth , chan ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of pitch bend */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_update_pitch_bend_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
return fluid_synth_modulate_voices_LOCAL ( synth , chan , 0 , FLUID_MOD_PITCHWHEEL ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the MIDI pitch bend controller value on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param ppitch_bend Location to store MIDI pitch bend value ( 0 - 16383 with
* 8192 being center )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_pitch_bend ( fluid_synth_t * synth , int chan , int * ppitch_bend )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( ppitch_bend ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
* ppitch_bend = fluid_channel_get_pitch_bend ( synth - > channel [ chan ] ) ;
result = FLUID_OK ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set MIDI pitch wheel sensitivity on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param val Pitch wheel sensitivity value in semitones
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_pitch_wheel_sens ( fluid_synth_t * synth , int chan , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( val > = 0 & & val < = 72 , FLUID_FAILED ) ; /* 6 octaves!? Better than no limit.. */
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " pitchsens \t %d \t %d " , chan , val ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_channel_set_pitch_wheel_sensitivity ( synth - > channel [ chan ] , val ) ;
result = fluid_synth_update_pitch_wheel_sens_LOCAL ( synth , chan ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread variant of set pitch wheel sensitivity */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_update_pitch_wheel_sens_LOCAL ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
return fluid_synth_modulate_voices_LOCAL ( synth , chan , 0 , FLUID_MOD_PITCHWHEELSENS ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get MIDI pitch wheel sensitivity on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param pval Location to store pitch wheel sensitivity value in semitones
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since Sometime AFTER v1 .0 API freeze .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_pitch_wheel_sens ( fluid_synth_t * synth , int chan , int * pval )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( pval ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
* pval = fluid_channel_get_pitch_wheel_sensitivity ( synth - > channel [ chan ] ) ;
result = FLUID_OK ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Assign a preset to a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param preset Preset to assign to channel or NULL to clear ( ownership is taken over )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
static int
2018-10-18 00:41:02 +02:00
fluid_synth_set_preset ( fluid_synth_t * synth , int chan , fluid_preset_t * preset )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( chan > = 0 & & chan < synth - > midi_channels , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return fluid_channel_set_preset ( channel , preset ) ;
2016-08-23 18:50:54 +02:00
}
/* Get a preset by SoundFont, bank and program numbers.
* Returns preset pointer or NULL .
*/
2018-10-18 00:41:02 +02:00
static fluid_preset_t *
fluid_synth_get_preset ( fluid_synth_t * synth , int sfontnum ,
int banknum , int prognum )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont ;
fluid_list_t * list ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* 128 indicates an "unset" operation" */
if ( prognum = = FLUID_UNSET_PROGRAM )
{
return NULL ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
sfont = fluid_list_get ( list ) ;
if ( fluid_sfont_get_id ( sfont ) = = sfontnum )
{
return fluid_sfont_get_preset ( sfont , banknum - sfont - > bankofs , prognum ) ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
return NULL ;
2016-08-23 18:50:54 +02:00
}
/* Get a preset by SoundFont name, bank and program.
* Returns preset pointer or NULL .
*/
2018-10-18 00:41:02 +02:00
static fluid_preset_t *
fluid_synth_get_preset_by_sfont_name ( fluid_synth_t * synth , const char * sfontname ,
int banknum , int prognum )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont ;
fluid_list_t * list ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
sfont = fluid_list_get ( list ) ;
if ( FLUID_STRCMP ( fluid_sfont_get_name ( sfont ) , sfontname ) = = 0 )
{
return fluid_sfont_get_preset ( sfont , banknum - sfont - > bankofs , prognum ) ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
return NULL ;
2016-08-23 18:50:54 +02:00
}
/* Find a preset by bank and program numbers.
* Returns preset pointer or NULL .
2018-10-18 00:41:02 +02:00
*/
fluid_preset_t *
fluid_synth_find_preset ( fluid_synth_t * synth , int banknum ,
int prognum )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_preset_t * preset ;
fluid_sfont_t * sfont ;
fluid_list_t * list ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
sfont = fluid_list_get ( list ) ;
preset = fluid_sfont_get_preset ( sfont , banknum - sfont - > bankofs , prognum ) ;
if ( preset )
{
return preset ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
return NULL ;
2016-08-23 18:50:54 +02:00
}
/**
* Send a program change event on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param prognum MIDI program number ( 0 - 127 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
/* FIXME - Currently not real-time safe, due to preset allocation and mutex lock,
* and may be called from within synthesis context . */
/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented
* since fluid_synth_unset_program ( ) should be used instead . */
int
2018-10-18 00:41:02 +02:00
fluid_synth_program_change ( fluid_synth_t * synth , int chan , int prognum )
{
fluid_preset_t * preset = NULL ;
fluid_channel_t * channel ;
int subst_bank , subst_prog , banknum = 0 , result = FLUID_FAILED ;
fluid_return_val_if_fail ( prognum > = 0 & & prognum < = 128 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
channel = synth - > channel [ chan ] ;
if ( channel - > channel_type = = CHANNEL_TYPE_DRUM )
{
banknum = DRUM_INST_BANK ;
}
else
{
fluid_channel_get_sfont_bank_prog ( channel , NULL , & banknum , NULL ) ;
}
if ( synth - > verbose )
{
FLUID_LOG ( FLUID_INFO , " prog \t %d \t %d \t %d " , chan , banknum , prognum ) ;
}
/* I think this is a hack for MIDI files that do bank changes in GM mode.
* Proper way to handle this would probably be to ignore bank changes when in
* GM mode . - JG
* This is now possible by setting synth . midi - bank - select = gm , but let the hack
* stay for the time being . - DH
*/
if ( prognum ! = FLUID_UNSET_PROGRAM )
{
subst_bank = banknum ;
subst_prog = prognum ;
2016-08-23 18:50:54 +02:00
preset = fluid_synth_find_preset ( synth , subst_bank , subst_prog ) ;
2018-10-18 00:41:02 +02:00
/* Fallback to another preset if not found */
if ( ! preset )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
/* Percussion: Fallback to preset 0 in percussion bank */
if ( channel - > channel_type = = CHANNEL_TYPE_DRUM )
{
subst_prog = 0 ;
subst_bank = DRUM_INST_BANK ;
preset = fluid_synth_find_preset ( synth , subst_bank , subst_prog ) ;
}
/* Melodic instrument */
else
{
/* Fallback first to bank 0:prognum */
subst_bank = 0 ;
preset = fluid_synth_find_preset ( synth , subst_bank , subst_prog ) ;
/* Fallback to first preset in bank 0 (usually piano...) */
if ( ! preset )
{
subst_prog = 0 ;
preset = fluid_synth_find_preset ( synth , subst_bank , subst_prog ) ;
}
}
if ( preset )
{
FLUID_LOG ( FLUID_WARN , " Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d] " ,
chan , banknum , prognum , subst_bank , subst_prog ) ;
}
else
{
FLUID_LOG ( FLUID_WARN , " No preset found on channel %d [bank=%d prog=%d] " , chan , banknum , prognum ) ;
}
2016-08-23 18:50:54 +02:00
}
}
2018-10-18 00:41:02 +02:00
/* Assign the SoundFont ID and program number to the channel */
fluid_channel_set_sfont_bank_prog ( channel , preset ? fluid_sfont_get_id ( preset - > sfont ) : 0 ,
- 1 , prognum ) ;
result = fluid_synth_set_preset ( synth , chan , preset ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set instrument bank number on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param bank MIDI bank number
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ note This function does not change the instrument currently assigned to \ c chan ,
* as it is usually called prior to fluid_synth_program_change ( ) . If you still want
* instrument changes to take effect immediately , call fluid_synth_program_reset ( )
* after having set up the bank configuration .
*
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_bank_select ( fluid_synth_t * synth , int chan , int bank )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( bank < = 16383 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( bank > = 0 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
fluid_channel_set_sfont_bank_prog ( synth - > channel [ chan ] , - 1 , bank , - 1 ) ;
result = FLUID_OK ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set SoundFont ID on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param sfont_id ID of a loaded SoundFont
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ note This function does not change the instrument currently assigned to \ c chan ,
* as it is usually called prior to fluid_synth_bank_select ( ) or fluid_synth_program_change ( ) .
* If you still want instrument changes to take effect immediately , call fluid_synth_program_reset ( )
* after having selected the soundfont .
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_sfont_select ( fluid_synth_t * synth , int chan , int sfont_id )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_channel_set_sfont_bank_prog ( synth - > channel [ chan ] , sfont_id , - 1 , - 1 ) ;
result = FLUID_OK ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set the preset of a MIDI channel to an unassigned state .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ since 1.1 .1
*
2018-10-18 00:41:02 +02:00
* @ note Channel retains its SoundFont ID and bank numbers , while the program
2016-08-23 18:50:54 +02:00
* number is set to an " unset " state . MIDI program changes may re - assign a
* preset if one matches .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_unset_program ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
FLUID_API_RETURN ( fluid_synth_program_change ( synth , chan , FLUID_UNSET_PROGRAM ) ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get current SoundFont ID , bank number and program number for a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param sfont_id Location to store SoundFont ID
* @ param bank_num Location to store MIDI bank number
* @ param preset_num Location to store MIDI program number
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_program ( fluid_synth_t * synth , int chan , int * sfont_id ,
int * bank_num , int * preset_num )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_channel_t * channel ;
fluid_return_val_if_fail ( sfont_id ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( bank_num ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( preset_num ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
fluid_channel_get_sfont_bank_prog ( channel , sfont_id , bank_num , preset_num ) ;
/* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */
if ( * preset_num = = FLUID_UNSET_PROGRAM )
{
* preset_num = 0 ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = FLUID_OK ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Select an instrument on a MIDI channel by SoundFont ID , bank and program numbers .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param sfont_id ID of a loaded SoundFont
* @ param bank_num MIDI bank number
* @ param preset_num MIDI program number
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_program_select ( fluid_synth_t * synth , int chan , int sfont_id ,
int bank_num , int preset_num )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_preset_t * preset = NULL ;
fluid_channel_t * channel ;
int result ;
fluid_return_val_if_fail ( bank_num > = 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( preset_num > = 0 , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
preset = fluid_synth_get_preset ( synth , sfont_id , bank_num , preset_num ) ;
if ( preset = = NULL )
{
FLUID_LOG ( FLUID_ERR ,
" There is no preset with bank number %d and preset number %d in SoundFont %d " ,
bank_num , preset_num , sfont_id ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Assign the new SoundFont ID, bank and program number to the channel */
fluid_channel_set_sfont_bank_prog ( channel , sfont_id , bank_num , preset_num ) ;
result = fluid_synth_set_preset ( synth , chan , preset ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Select an instrument on a MIDI channel by SoundFont name , bank and program numbers .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param sfont_name Name of a loaded SoundFont
* @ param bank_num MIDI bank number
* @ param preset_num MIDI program number
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_program_select_by_sfont_name ( fluid_synth_t * synth , int chan ,
const char * sfont_name , int bank_num ,
int preset_num )
{
fluid_preset_t * preset = NULL ;
fluid_channel_t * channel ;
int result ;
fluid_return_val_if_fail ( sfont_name ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Allowed only on MIDI channel enabled */
FLUID_API_RETURN_IF_CHAN_DISABLED ( FLUID_FAILED ) ;
channel = synth - > channel [ chan ] ;
preset = fluid_synth_get_preset_by_sfont_name ( synth , sfont_name , bank_num ,
preset_num ) ;
if ( preset = = NULL )
{
FLUID_LOG ( FLUID_ERR ,
" There is no preset with bank number %d and preset number %d in SoundFont %s " ,
bank_num , preset_num , sfont_name ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
/* Assign the new SoundFont ID, bank and program number to the channel */
fluid_channel_set_sfont_bank_prog ( channel , fluid_sfont_get_id ( preset - > sfont ) ,
bank_num , preset_num ) ;
result = fluid_synth_set_preset ( synth , chan , preset ) ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/*
* This function assures that every MIDI channel has a valid preset
* ( NULL is okay ) . This function is called after a SoundFont is
* unloaded or reloaded .
*/
static void
2018-10-18 00:41:02 +02:00
fluid_synth_update_presets ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel ;
fluid_preset_t * preset ;
int sfont , bank , prog ;
int chan ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( chan = 0 ; chan < synth - > midi_channels ; chan + + )
{
channel = synth - > channel [ chan ] ;
fluid_channel_get_sfont_bank_prog ( channel , & sfont , & bank , & prog ) ;
preset = fluid_synth_get_preset ( synth , sfont , bank , prog ) ;
fluid_synth_set_preset ( synth , chan , preset ) ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* Handler for synth.sample-rate setting. */
static void
fluid_synth_handle_sample_rate ( void * data , const char * name , double value )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_synth_set_sample_rate ( synth , ( float ) value ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Set sample rate of the synth .
* @ note This function should only be used when no voices or notes are active .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
* @ param sample_rate New sample rate ( Hz )
* @ since 1.1 .2
*/
2018-10-18 00:41:02 +02:00
void
fluid_synth_set_sample_rate ( fluid_synth_t * synth , float sample_rate )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
fluid_clip ( sample_rate , 8000.0f , 96000.0f ) ;
synth - > sample_rate = sample_rate ;
synth - > min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL ( synth ) ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
fluid_voice_set_output_rate ( synth - > voice [ i ] , sample_rate ) ;
}
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_set_samplerate ,
0 , sample_rate ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/* Handler for synth.gain setting. */
2018-10-18 00:41:02 +02:00
static void
fluid_synth_handle_gain ( void * data , const char * name , double value )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_synth_set_gain ( synth , ( float ) value ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set synth output gain value .
* @ param synth FluidSynth instance
* @ param gain Gain value ( function clamps value to the range 0.0 to 10.0 )
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_set_gain ( fluid_synth_t * synth , float gain )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
fluid_clip ( gain , 0.0f , 10.0f ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > gain = gain ;
fluid_synth_update_gain_LOCAL ( synth ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/* Called by synthesis thread to update the gain in all voices */
static void
2018-10-18 00:41:02 +02:00
fluid_synth_update_gain_LOCAL ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
float gain ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
gain = synth - > gain ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
if ( fluid_voice_is_playing ( voice ) )
{
fluid_voice_set_gain ( voice , gain ) ;
}
}
2016-08-23 18:50:54 +02:00
}
/**
* Get synth output gain value .
* @ param synth FluidSynth instance
* @ return Synth gain value ( 0.0 to 10.0 )
*/
float
2018-10-18 00:41:02 +02:00
fluid_synth_get_gain ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
float result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > gain ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/*
* Handler for synth . polyphony setting .
*/
2018-10-18 00:41:02 +02:00
static void
fluid_synth_handle_polyphony ( void * data , const char * name , int value )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_synth_set_polyphony ( synth , value ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set synthesizer polyphony ( max number of voices ) .
* @ param synth FluidSynth instance
* @ param polyphony Polyphony to assign
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.0 .6
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_set_polyphony ( fluid_synth_t * synth , int polyphony )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( polyphony > = 1 & & polyphony < = 65535 , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = fluid_synth_update_polyphony_LOCAL ( synth , polyphony ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/* Called by synthesis thread to update the polyphony value */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_update_polyphony_LOCAL ( fluid_synth_t * synth , int new_polyphony )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( new_polyphony > synth - > nvoice )
{
/* Create more voices */
fluid_voice_t * * new_voices = FLUID_REALLOC ( synth - > voice ,
sizeof ( fluid_voice_t * ) * new_polyphony ) ;
if ( new_voices = = NULL )
{
return FLUID_FAILED ;
}
synth - > voice = new_voices ;
for ( i = synth - > nvoice ; i < new_polyphony ; i + + )
{
synth - > voice [ i ] = new_fluid_voice ( synth - > eventhandler , synth - > sample_rate ) ;
if ( synth - > voice [ i ] = = NULL )
{
return FLUID_FAILED ;
}
fluid_voice_set_custom_filter ( synth - > voice [ i ] , synth - > custom_filter_type , synth - > custom_filter_flags ) ;
}
synth - > nvoice = new_polyphony ;
}
synth - > polyphony = new_polyphony ;
/* turn off any voices above the new limit */
for ( i = synth - > polyphony ; i < synth - > nvoice ; i + + )
{
voice = synth - > voice [ i ] ;
if ( fluid_voice_is_playing ( voice ) )
{
fluid_voice_off ( voice ) ;
}
}
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_set_polyphony ,
synth - > polyphony , 0.0f ) ;
return FLUID_OK ;
}
/**
* Get current synthesizer polyphony ( max number of voices ) .
* @ param synth FluidSynth instance
* @ return Synth polyphony value .
* @ since 1.0 .6
*/
int
fluid_synth_get_polyphony ( fluid_synth_t * synth )
{
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
result = synth - > polyphony ;
FLUID_API_RETURN ( result ) ;
}
/**
* @ brief Get current number of active voices .
*
* I . e . the no . of voices that have been
* started and have not yet finished . Unless called from synthesis context ,
* this number does not necessarily have to be equal to the number of voices
* currently processed by the DSP loop , see below .
* @ param synth FluidSynth instance
* @ return Number of currently active voices .
* @ since 1.1 .0
*
* @ note To generate accurate continuous statistics of the voice count , caller
* should ensure this function is called synchronously with the audio synthesis
* process . This can be done in the new_fluid_audio_driver2 ( ) audio callback
* function for example . Otherwise every call to this function may return different
* voice counts as it may change after any ( concurrent ) call to fluid_synth_write_ * ( ) made by
* e . g . an audio driver or the applications audio rendering thread .
*/
int
fluid_synth_get_active_voice_count ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > active_voice_count ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the internal synthesis buffer size value .
* @ param synth FluidSynth instance
* @ return Internal buffer size in audio frames .
*
* Audio is synthesized this number of frames at a time . Defaults to 64 frames .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_internal_bufsize ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
return FLUID_BUFSIZE ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Resend a bank select and a program change for every channel and assign corresponding instruments .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
* This function is called mainly after a SoundFont has been loaded ,
* unloaded or reloaded .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_program_reset ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i , prog ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
/* try to set the correct presets */
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
fluid_channel_get_sfont_bank_prog ( synth - > channel [ i ] , NULL , NULL , & prog ) ;
fluid_synth_program_change ( synth , i , prog ) ;
}
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Synthesize a block of floating point audio to separate audio buffers ( multichannel rendering ) . First effect channel used by reverb , second for chorus .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
* @ param len Count of audio frames to synthesize
2018-10-18 00:41:02 +02:00
* @ param left Array of float buffers to store left channel of planar audio ( as many as \ c synth . audio - channels buffers , each of \ c len in size )
* @ param right Array of float buffers to store right channel of planar audio ( size : dito )
* @ param fx_left Since 1.1 .7 : If not \ c NULL , array of float buffers to store left effect channels ( as many as \ c synth . effects - channels buffers , each of \ c len in size )
* @ param fx_right Since 1.1 .7 : If not \ c NULL , array of float buffers to store right effect channels ( size : dito )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*
* @ note Should only be called from synthesis thread .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ deprecated fluid_synth_nwrite_float ( ) is deprecated and will be removed in a future release . It may continue to work or it may return # FLUID_FAILED in the future . Consider using the more powerful and flexible fluid_synth_process ( ) .
*
* Usage example :
* @ code { . cpp }
const int FramesToRender = 64 ;
int channels ;
// retrieve number of stereo audio channels
fluid_settings_getint ( settings , " synth.audio-channels " , & channels ) ;
// we need twice as many (mono-)buffers
channels * = 2 ;
// fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM
float * * mix_buf = new float * [ channels ] ;
for ( int i = 0 ; i < channels ; i + + )
{
mix_buf [ i ] = new float [ FramesToRender ] ;
}
// retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan))
fluid_settings_getint ( settings , " synth.effects-channels " , & channels ) ;
channels * = 2 ;
float * * fx_buf = new float * [ channels ] ;
for ( int i = 0 ; i < channels ; i + + )
{
fx_buf [ i ] = new float [ FramesToRender ] ;
}
float * * mix_buf_l = mix_buf ;
float * * mix_buf_r = & mix_buf [ channels / 2 ] ;
float * * fx_buf_l = fx_buf ;
float * * fx_buf_r = & fx_buf [ channels / 2 ] ;
fluid_synth_nwrite_float ( synth , FramesToRender , mix_buf_l , mix_buf_r , fx_buf_l , fx_buf_r )
* @ endcode
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_nwrite_float ( fluid_synth_t * synth , int len ,
float * * left , float * * right ,
float * * fx_left , float * * fx_right )
{
fluid_real_t * left_in , * fx_left_in ;
fluid_real_t * right_in , * fx_right_in ;
double time = fluid_utime ( ) ;
int i , num , available , count ;
2016-08-23 18:50:54 +02:00
# ifdef WITH_FLOAT
2018-10-18 00:41:02 +02:00
int bytes ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
float cpu_load ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( left ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( right ! = NULL , FLUID_FAILED ) ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
/* First, take what's still available in the buffer */
count = 0 ;
num = synth - > cur ;
if ( synth - > cur < FLUID_BUFSIZE )
{
available = FLUID_BUFSIZE - synth - > cur ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
fluid_rvoice_mixer_get_fx_bufs ( synth - > eventhandler - > mixer , & fx_left_in , & fx_right_in ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
num = ( available > len ) ? len : available ;
2016-08-23 18:50:54 +02:00
# ifdef WITH_FLOAT
2018-10-18 00:41:02 +02:00
bytes = num * sizeof ( float ) ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > audio_channels ; i + + )
{
# ifdef WITH_FLOAT
FLUID_MEMCPY ( left [ i ] , & left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth - > cur ] , bytes ) ;
FLUID_MEMCPY ( right [ i ] , & right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth - > cur ] , bytes ) ;
# else //WITH_FLOAT
int j ;
for ( j = 0 ; j < num ; j + + )
{
left [ i ] [ j ] = ( float ) left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth - > cur ] ;
right [ i ] [ j ] = ( float ) right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth - > cur ] ;
}
# endif //WITH_FLOAT
}
for ( i = 0 ; i < synth - > effects_channels ; i + + )
{
2016-08-23 18:50:54 +02:00
# ifdef WITH_FLOAT
2018-10-18 00:41:02 +02:00
if ( fx_left ! = NULL )
{
FLUID_MEMCPY ( fx_left [ i ] , & fx_left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth - > cur ] , bytes ) ;
}
if ( fx_right ! = NULL )
{
FLUID_MEMCPY ( fx_right [ i ] , & fx_right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth - > cur ] , bytes ) ;
}
2016-08-23 18:50:54 +02:00
# else //WITH_FLOAT
2018-10-18 00:41:02 +02:00
int j ;
if ( fx_left ! = NULL )
{
for ( j = 0 ; j < num ; j + + )
{
fx_left [ i ] [ j ] = ( float ) fx_left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth - > cur ] ;
}
}
if ( fx_right ! = NULL )
{
for ( j = 0 ; j < num ; j + + )
{
fx_right [ i ] [ j ] = ( float ) fx_right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth - > cur ] ;
}
}
2016-08-23 18:50:54 +02:00
# endif //WITH_FLOAT
2018-10-18 00:41:02 +02:00
}
count + = num ;
num + = synth - > cur ; /* if we're now done, num becomes the new synth->cur below */
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* Then, run one_block() and copy till we have 'len' samples */
while ( count < len )
{
fluid_rvoice_mixer_set_mix_fx ( synth - > eventhandler - > mixer , 0 ) ;
fluid_synth_render_blocks ( synth , 1 ) ; // TODO:
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
fluid_rvoice_mixer_get_fx_bufs ( synth - > eventhandler - > mixer , & fx_left_in , & fx_right_in ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
num = ( FLUID_BUFSIZE > len - count ) ? len - count : FLUID_BUFSIZE ;
2016-08-23 18:50:54 +02:00
# ifdef WITH_FLOAT
2018-10-18 00:41:02 +02:00
bytes = num * sizeof ( float ) ;
2016-08-23 18:50:54 +02:00
# endif
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > audio_channels ; i + + )
{
# ifdef WITH_FLOAT
FLUID_MEMCPY ( left [ i ] + count , & left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT ] , bytes ) ;
FLUID_MEMCPY ( right [ i ] + count , & right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT ] , bytes ) ;
# else //WITH_FLOAT
int j ;
for ( j = 0 ; j < num ; j + + )
{
left [ i ] [ j + count ] = ( float ) left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j ] ;
right [ i ] [ j + count ] = ( float ) right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j ] ;
}
# endif //WITH_FLOAT
}
for ( i = 0 ; i < synth - > effects_channels ; i + + )
{
2016-08-23 18:50:54 +02:00
# ifdef WITH_FLOAT
2018-10-18 00:41:02 +02:00
if ( fx_left ! = NULL )
{
FLUID_MEMCPY ( fx_left [ i ] + count , & fx_left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT ] , bytes ) ;
}
if ( fx_right ! = NULL )
{
FLUID_MEMCPY ( fx_right [ i ] + count , & fx_right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT ] , bytes ) ;
}
2016-08-23 18:50:54 +02:00
# else //WITH_FLOAT
2018-10-18 00:41:02 +02:00
int j ;
if ( fx_left ! = NULL )
{
for ( j = 0 ; j < num ; j + + )
{
fx_left [ i ] [ j + count ] = ( float ) fx_left_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j ] ;
}
}
if ( fx_right ! = NULL )
{
for ( j = 0 ; j < num ; j + + )
{
fx_right [ i ] [ j + count ] = ( float ) fx_right_in [ i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j ] ;
}
}
2016-08-23 18:50:54 +02:00
# endif //WITH_FLOAT
2018-10-18 00:41:02 +02:00
}
count + = num ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
synth - > cur = num ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
time = fluid_utime ( ) - time ;
cpu_load = 0.5 * ( fluid_atomic_float_get ( & synth - > cpu_load ) + time * synth - > sample_rate / len / 10000.0 ) ;
fluid_atomic_float_set ( & synth - > cpu_load , cpu_load ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/**
* mixes the samples of \ p in to \ p out
2019-01-02 15:33:23 +01:00
*
2018-10-18 00:41:02 +02:00
* @ param out the output sample buffer to mix to
* @ param ooff sample offset in \ p out
* @ param in the rvoice_mixer input sample buffer to mix from
* @ param ioff sample offset in \ p in
* @ param buf_idx the sample buffer index of \ p in to mix from
* @ param num number of samples to mix
*/
static FLUID_INLINE void fluid_synth_mix_single_buffer ( float * FLUID_RESTRICT out ,
int ooff ,
const fluid_real_t * FLUID_RESTRICT in ,
int ioff ,
int buf_idx ,
int num )
{
if ( out ! = NULL )
{
int j ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
for ( j = 0 ; j < num ; j + + )
{
out [ j + ooff ] + = ( float ) in [ buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff ] ;
}
}
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* @ brief Synthesize floating point audio to stereo audio channels ( implements the default interface # fluid_audio_func_t ) .
*
* Synthesize and < strong > mix < / strong > audio to a given number of planar audio buffers .
* Therefore pass < code > nout = N * 2 < / code > float buffers to \ p out in order to render
* the synthesized audio to \ p N stereo channels . Each float buffer must be
* able to hold \ p len elements .
*
* \ p out contains an array of planar buffers for normal , dry , stereo
* audio ( alternating left and right ) . Like :
@ code { . cpp }
out [ 0 ] = left_buffer_audio_channel_0
out [ 1 ] = right_buffer_audio_channel_0
out [ 2 ] = left_buffer_audio_channel_1
out [ 3 ] = right_buffer_audio_channel_1
. . .
out [ ( i * 2 + 0 ) % nout ] = left_buffer_audio_channel_i
out [ ( i * 2 + 1 ) % nout ] = right_buffer_audio_channel_i
@ endcode
*
* for zero - based channel index \ p i .
* The buffer layout of \ p fx used for storing effects
* like reverb and chorus looks similar :
@ code { . cpp }
fx [ 0 ] = left_buffer_channel_of_reverb_unit_0
fx [ 1 ] = right_buffer_channel_of_reverb_unit_0
fx [ 2 ] = left_buffer_channel_of_chorus_unit_0
fx [ 3 ] = right_buffer_channel_of_chorus_unit_0
fx [ 4 ] = left_buffer_channel_of_reverb_unit_1
fx [ 5 ] = right_buffer_channel_of_reverb_unit_1
fx [ 6 ] = left_buffer_channel_of_chorus_unit_1
fx [ 7 ] = right_buffer_channel_of_chorus_unit_1
fx [ 8 ] = left_buffer_channel_of_reverb_unit_2
. . .
fx [ ( ( k * fluid_synth_count_effects_channels ( ) + j ) * 2 + 0 ) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k
fx [ ( ( k * fluid_synth_count_effects_channels ( ) + j ) * 2 + 1 ) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k
@ endcode
* where < code > 0 < = k < fluid_synth_count_effects_groups ( ) < / code > is a zero - based index denoting the effects unit and
* < code > 0 < = j < fluid_synth_count_effects_channels ( ) < / code > is a zero - based index denoting the effect channel within
* unit \ p k .
*
* Any voice playing is assigned to audio channels based on the MIDI channel its playing on . Let \ p chan be the
* zero - based MIDI channel index an arbitrary voice is playing on . To determine the audio channel and effects unit it is
* going to be rendered to use :
*
* < code > i = chan % fluid_synth_count_audio_groups ( ) < / code >
*
* < code > k = chan % fluid_synth_count_effects_groups ( ) < / code >
*
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param len Count of audio frames to synthesize and store in every single buffer provided by \ p out and \ p fx .
* @ param nfx Count of arrays in \ c fx . Must be a multiple of 2 ( because of stereo )
* and in the range < code > 0 < = nfx / 2 < = ( fluid_synth_count_effects_channels ( ) * fluid_synth_count_effects_groups ( ) ) < / code > .
* @ param fx Array of buffers to store effects audio to . Buffers may
alias with buffers of \ c out . NULL buffers are permitted and will cause to skip mixing any audio into that buffer .
* @ param nout Count of arrays in \ c out . Must be a multiple of 2
( because of stereo ) and in the range < code > 0 < = nout / 2 < = fluid_synth_count_audio_channels ( ) < / code > .
* @ param out Array of buffers to store ( dry ) audio to . Buffers may
alias with buffers of \ c fx . NULL buffers are permitted and will cause to skip mixing any audio into that buffer .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ parblock
* @ note The owner of the sample buffers must zero them out before calling this
* function , because any synthesized audio is mixed ( i . e . added ) to the buffers .
* E . g . if fluid_synth_process ( ) is called from a custom audio driver process function
* ( see new_fluid_audio_driver2 ( ) ) , the audio driver takes care of zeroing the buffers .
* @ endparblock
*
* @ parblock
* @ note No matter how many buffers you pass in , fluid_synth_process ( )
* will always render all audio channels to the
* buffers in \ c out and all effects channels to the
* buffers in \ c fx , provided that < code > nout > 0 < / code > and < code > nfx > 0 < / code > respectively . If
* < code > nout / 2 < fluid_synth_count_audio_channels ( ) < / code > it will wrap around . Same
* is true for effects audio if < code > nfx / 2 < ( fluid_synth_count_effects_channels ( ) * fluid_synth_count_effects_groups ( ) ) < / code > .
* See usage examples below .
* @ endparblock
*
* @ parblock
* @ note Should only be called from synthesis thread .
* @ endparblock
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_process ( fluid_synth_t * synth , int len , int nfx , float * fx [ ] ,
int nout , float * out [ ] )
{
fluid_real_t * left_in , * fx_left_in ;
fluid_real_t * right_in , * fx_right_in ;
int nfxchan , nfxunits , naudchan ;
double time = fluid_utime ( ) ;
int i , f , num , count ;
float cpu_load ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( nfx % 2 = = 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( nout % 2 = = 0 , FLUID_FAILED ) ;
nfxchan = synth - > effects_channels ;
nfxunits = synth - > effects_groups ;
naudchan = synth - > audio_channels ;
fluid_return_val_if_fail ( 0 < = nfx / 2 & & nfx / 2 < = nfxchan * nfxunits , FLUID_FAILED ) ;
fluid_return_val_if_fail ( 0 < = nout / 2 & & nout / 2 < = naudchan , FLUID_FAILED ) ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
fluid_rvoice_mixer_get_fx_bufs ( synth - > eventhandler - > mixer , & fx_left_in , & fx_right_in ) ;
fluid_rvoice_mixer_set_mix_fx ( synth - > eventhandler - > mixer , FALSE ) ;
/* First, take what's still available in the buffer */
count = 0 ;
num = synth - > cur ;
if ( synth - > cur < FLUID_BUFSIZE )
{
int available = FLUID_BUFSIZE - synth - > cur ;
num = ( available > len ) ? len : available ;
if ( nout ! = 0 )
{
for ( i = 0 ; i < naudchan ; i + + )
{
float * out_buf = out [ ( i * 2 ) % nout ] ;
fluid_synth_mix_single_buffer ( out_buf , 0 , left_in , synth - > cur , i , num ) ;
out_buf = out [ ( i * 2 + 1 ) % nout ] ;
fluid_synth_mix_single_buffer ( out_buf , 0 , right_in , synth - > cur , i , num ) ;
}
}
if ( nfx ! = 0 )
{
// loop over all effects units
for ( f = 0 ; f < nfxunits ; f + + )
{
// write out all effects (i.e. reverb and chorus)
for ( i = 0 ; i < nfxchan ; i + + )
{
int buf_idx = f * nfxchan + i ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
float * out_buf = fx [ ( buf_idx * 2 ) % nfx ] ;
fluid_synth_mix_single_buffer ( out_buf , 0 , fx_left_in , synth - > cur , buf_idx , num ) ;
out_buf = fx [ ( buf_idx * 2 + 1 ) % nfx ] ;
fluid_synth_mix_single_buffer ( out_buf , 0 , fx_right_in , synth - > cur , buf_idx , num ) ;
}
}
}
count + = num ;
num + = synth - > cur ; /* if we're now done, num becomes the new synth->cur below */
}
/* Then, render blocks and copy till we have 'len' samples */
while ( count < len )
{
int blocksleft = ( len - count + FLUID_BUFSIZE - 1 ) / FLUID_BUFSIZE ;
int blockcount = fluid_synth_render_blocks ( synth , blocksleft ) ;
num = ( blockcount * FLUID_BUFSIZE > len - count ) ? len - count : blockcount * FLUID_BUFSIZE ;
if ( nout ! = 0 )
{
for ( i = 0 ; i < naudchan ; i + + )
{
float * out_buf = out [ ( i * 2 ) % nout ] ;
fluid_synth_mix_single_buffer ( out_buf , count , left_in , 0 , i , num ) ;
out_buf = out [ ( i * 2 + 1 ) % nout ] ;
fluid_synth_mix_single_buffer ( out_buf , count , right_in , 0 , i , num ) ;
}
}
if ( nfx ! = 0 )
{
// loop over all effects units
for ( f = 0 ; f < nfxunits ; f + + )
{
// write out all effects (i.e. reverb and chorus)
for ( i = 0 ; i < nfxchan ; i + + )
{
int buf_idx = f * nfxchan + i ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
float * out_buf = fx [ ( buf_idx * 2 ) % nfx ] ;
fluid_synth_mix_single_buffer ( out_buf , count , fx_left_in , 0 , buf_idx , num ) ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
out_buf = fx [ ( buf_idx * 2 + 1 ) % nfx ] ;
fluid_synth_mix_single_buffer ( out_buf , count , fx_right_in , 0 , buf_idx , num ) ;
}
}
}
count + = num ;
}
synth - > cur = num ;
time = fluid_utime ( ) - time ;
cpu_load = 0.5 * ( fluid_atomic_float_get ( & synth - > cpu_load ) + time * synth - > sample_rate / len / 10000.0 ) ;
fluid_atomic_float_set ( & synth - > cpu_load , cpu_load ) ;
2016-08-23 18:50:54 +02:00
return FLUID_OK ;
}
/**
* Synthesize a block of floating point audio samples to audio buffers .
* @ param synth FluidSynth instance
* @ param len Count of audio frames to synthesize
* @ param lout Array of floats to store left channel of audio
* @ param loff Offset index in ' lout ' for first sample
* @ param lincr Increment between samples stored to ' lout '
* @ param rout Array of floats to store right channel of audio
* @ param roff Offset index in ' rout ' for first sample
* @ param rincr Increment between samples stored to ' rout '
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
* Useful for storing interleaved stereo ( lout = rout , loff = 0 , roff = 1 ,
* lincr = 2 , rincr = 2 ) .
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from synthesis thread .
* @ note Reverb and Chorus are mixed to \ c lout resp . \ c rout .
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_write_float ( fluid_synth_t * synth , int len ,
void * lout , int loff , int lincr ,
void * rout , int roff , int rincr )
{
int i , j , k , l ;
float * left_out = ( float * ) lout ;
float * right_out = ( float * ) rout ;
fluid_real_t * left_in ;
fluid_real_t * right_in ;
double time = fluid_utime ( ) ;
float cpu_load ;
fluid_profile_ref_var ( prof_ref ) ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( lout ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( rout ! = NULL , FLUID_FAILED ) ;
fluid_rvoice_mixer_set_mix_fx ( synth - > eventhandler - > mixer , 1 ) ;
l = synth - > cur ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 , j = loff , k = roff ; i < len ; i + + , l + + , j + = lincr , k + = rincr )
{
/* fill up the buffers as needed */
if ( l > = synth - > curmax )
{
int blocksleft = ( len - i + FLUID_BUFSIZE - 1 ) / FLUID_BUFSIZE ;
synth - > curmax = FLUID_BUFSIZE * fluid_synth_render_blocks ( synth , blocksleft ) ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
l = 0 ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
left_out [ j ] = ( float ) left_in [ 0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l ] ;
right_out [ k ] = ( float ) right_in [ 0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l ] ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > cur = l ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
time = fluid_utime ( ) - time ;
cpu_load = 0.5 * ( fluid_atomic_float_get ( & synth - > cpu_load ) + time * synth - > sample_rate / len / 10000.0 ) ;
fluid_atomic_float_set ( & synth - > cpu_load , cpu_load ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_profile_write ( FLUID_PROF_WRITE , prof_ref ,
fluid_rvoice_mixer_get_active_voices ( synth - > eventhandler - > mixer ) ,
len ) ;
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
# define DITHER_SIZE 48000
# define DITHER_CHANNELS 2
static float rand_table [ DITHER_CHANNELS ] [ DITHER_SIZE ] ;
/* Init dither table */
2018-10-18 00:41:02 +02:00
static void
2016-08-23 18:50:54 +02:00
init_dither ( void )
{
2018-10-18 00:41:02 +02:00
float d , dp ;
int c , i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( c = 0 ; c < DITHER_CHANNELS ; c + + )
{
dp = 0 ;
for ( i = 0 ; i < DITHER_SIZE - 1 ; i + + )
{
d = rand ( ) / ( float ) RAND_MAX - 0.5f ;
rand_table [ c ] [ i ] = d - dp ;
dp = d ;
}
rand_table [ c ] [ DITHER_SIZE - 1 ] = 0 - dp ;
2016-08-23 18:50:54 +02:00
}
}
/* A portable replacement for roundf(), seems it may actually be faster too! */
2018-11-23 14:36:52 +01:00
static FLUID_INLINE int16_t
round_clip_to_i16 ( float x )
2016-08-23 18:50:54 +02:00
{
2018-11-23 14:36:52 +01:00
long i ;
2019-01-02 15:33:23 +01:00
2018-10-18 00:41:02 +02:00
if ( x > = 0.0f )
{
2018-11-23 14:36:52 +01:00
i = ( long ) ( x + 0.5f ) ;
2019-01-02 15:33:23 +01:00
if ( FLUID_UNLIKELY ( i > 32767 ) )
2018-11-23 14:36:52 +01:00
{
i = 32767 ;
}
2018-10-18 00:41:02 +02:00
}
else
{
2018-11-23 14:36:52 +01:00
i = ( long ) ( x - 0.5f ) ;
2019-01-02 15:33:23 +01:00
if ( FLUID_UNLIKELY ( i < - 32768 ) )
2018-11-23 14:36:52 +01:00
{
i = - 32768 ;
}
2018-10-18 00:41:02 +02:00
}
2019-01-02 15:33:23 +01:00
2018-11-23 14:36:52 +01:00
return ( int16_t ) i ;
2016-08-23 18:50:54 +02:00
}
/**
* Synthesize a block of 16 bit audio samples to audio buffers .
* @ param synth FluidSynth instance
* @ param len Count of audio frames to synthesize
* @ param lout Array of 16 bit words to store left channel of audio
* @ param loff Offset index in ' lout ' for first sample
* @ param lincr Increment between samples stored to ' lout '
* @ param rout Array of 16 bit words to store right channel of audio
* @ param roff Offset index in ' rout ' for first sample
* @ param rincr Increment between samples stored to ' rout '
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
* Useful for storing interleaved stereo ( lout = rout , loff = 0 , roff = 1 ,
* lincr = 2 , rincr = 2 ) .
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from synthesis thread .
* @ note Reverb and Chorus are mixed to \ c lout resp . \ c rout .
* @ note Dithering is performed when converting from internal floating point to
2016-08-23 18:50:54 +02:00
* 16 bit audio .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_write_s16 ( fluid_synth_t * synth , int len ,
void * lout , int loff , int lincr ,
void * rout , int roff , int rincr )
{
int i , j , k , cur ;
2018-11-23 14:36:52 +01:00
int16_t * left_out = lout ;
int16_t * right_out = rout ;
2018-10-18 00:41:02 +02:00
fluid_real_t * left_in ;
fluid_real_t * right_in ;
double time = fluid_utime ( ) ;
int di ;
float cpu_load ;
fluid_profile_ref_var ( prof_ref ) ;
fluid_rvoice_mixer_set_mix_fx ( synth - > eventhandler - > mixer , 1 ) ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
cur = synth - > cur ;
di = synth - > dither_index ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 , j = loff , k = roff ; i < len ; i + + , cur + + , j + = lincr , k + = rincr )
{
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* fill up the buffers as needed */
if ( cur > = synth - > curmax )
{
int blocksleft = ( len - i + FLUID_BUFSIZE - 1 ) / FLUID_BUFSIZE ;
synth - > curmax = FLUID_BUFSIZE * fluid_synth_render_blocks ( synth , blocksleft ) ;
fluid_rvoice_mixer_get_bufs ( synth - > eventhandler - > mixer , & left_in , & right_in ) ;
cur = 0 ;
}
2016-08-23 18:50:54 +02:00
2018-11-23 14:36:52 +01:00
left_out [ j ] = round_clip_to_i16 ( left_in [ 0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur ] * 32766.0f + rand_table [ 0 ] [ di ] ) ;
right_out [ k ] = round_clip_to_i16 ( right_in [ 0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur ] * 32766.0f + rand_table [ 1 ] [ di ] ) ;
2016-08-23 18:50:54 +02:00
2018-11-23 14:36:52 +01:00
if ( + + di > = DITHER_SIZE )
2018-10-18 00:41:02 +02:00
{
di = 0 ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > cur = cur ;
synth - > dither_index = di ; /* keep dither buffer continous */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
time = fluid_utime ( ) - time ;
cpu_load = 0.5 * ( fluid_atomic_float_get ( & synth - > cpu_load ) + time * synth - > sample_rate / len / 10000.0 ) ;
fluid_atomic_float_set ( & synth - > cpu_load , cpu_load ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_profile_write ( FLUID_PROF_WRITE , prof_ref ,
fluid_rvoice_mixer_get_active_voices ( synth - > eventhandler - > mixer ) ,
len ) ;
return 0 ;
2016-08-23 18:50:54 +02:00
}
/**
* Converts stereo floating point sample data to signed 16 bit data with dithering .
* @ param dither_index Pointer to an integer which should be initialized to 0
* before the first call and passed unmodified to additional calls which are
* part of the same synthesis output .
* @ param len Length in frames to convert
* @ param lin Buffer of left audio samples to convert from
* @ param rin Buffer of right audio samples to convert from
* @ param lout Array of 16 bit words to store left channel of audio
* @ param loff Offset index in ' lout ' for first sample
* @ param lincr Increment between samples stored to ' lout '
* @ param rout Array of 16 bit words to store right channel of audio
* @ param roff Offset index in ' rout ' for first sample
* @ param rincr Increment between samples stored to ' rout '
*
2018-10-18 00:41:02 +02:00
* @ note Currently private to libfluidsynth .
2016-08-23 18:50:54 +02:00
*/
void
2018-11-23 14:36:52 +01:00
fluid_synth_dither_s16 ( int * dither_index , int len , const float * lin , const float * rin ,
2018-10-18 00:41:02 +02:00
void * lout , int loff , int lincr ,
void * rout , int roff , int rincr )
{
int i , j , k ;
2018-11-23 14:36:52 +01:00
int16_t * left_out = lout ;
int16_t * right_out = rout ;
2018-10-18 00:41:02 +02:00
int di = * dither_index ;
fluid_profile_ref_var ( prof_ref ) ;
for ( i = 0 , j = loff , k = roff ; i < len ; i + + , j + = lincr , k + = rincr )
{
2018-11-23 14:36:52 +01:00
left_out [ j ] = round_clip_to_i16 ( lin [ i ] * 32766.0f + rand_table [ 0 ] [ di ] ) ;
right_out [ k ] = round_clip_to_i16 ( rin [ i ] * 32766.0f + rand_table [ 1 ] [ di ] ) ;
2016-08-23 18:50:54 +02:00
2018-11-23 14:36:52 +01:00
if ( + + di > = DITHER_SIZE )
2018-10-18 00:41:02 +02:00
{
di = 0 ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
* dither_index = di ; /* keep dither buffer continous */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_profile ( FLUID_PROF_WRITE , prof_ref , 0 , len ) ;
2016-08-23 18:50:54 +02:00
}
static void
2018-10-18 00:41:02 +02:00
fluid_synth_check_finished_voices ( fluid_synth_t * synth )
{
int j ;
fluid_rvoice_t * fv ;
while ( NULL ! = ( fv = fluid_rvoice_eventhandler_get_finished_voice ( synth - > eventhandler ) ) )
{
for ( j = 0 ; j < synth - > polyphony ; j + + )
{
if ( synth - > voice [ j ] - > rvoice = = fv )
{
fluid_voice_unlock_rvoice ( synth - > voice [ j ] ) ;
fluid_voice_stop ( synth - > voice [ j ] ) ;
break ;
}
else if ( synth - > voice [ j ] - > overflow_rvoice = = fv )
{
fluid_voice_overflow_rvoice_finished ( synth - > voice [ j ] ) ;
break ;
}
}
2016-08-23 18:50:54 +02:00
}
}
/**
* Process all waiting events in the rvoice queue .
* Make sure no ( other ) rendering is running in parallel when
* you call this function !
*/
2018-10-18 00:41:02 +02:00
void fluid_synth_process_event_queue ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_rvoice_eventhandler_dispatch_all ( synth - > eventhandler ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Process blocks ( FLUID_BUFSIZE ) of audio .
* Must be called from renderer thread only !
* @ return number of blocks rendered . Might ( often ) return less than requested
*/
static int
2018-10-18 00:41:02 +02:00
fluid_synth_render_blocks ( fluid_synth_t * synth , int blockcount )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i , maxblocks ;
fluid_profile_ref_var ( prof_ref ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Assign ID of synthesis thread */
2016-08-23 18:50:54 +02:00
// synth->synth_thread_id = fluid_thread_get_id ();
2018-10-18 00:41:02 +02:00
fluid_check_fpe ( " ??? Just starting up ??? " ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_rvoice_eventhandler_dispatch_all ( synth - > eventhandler ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* do not render more blocks than we can store internally */
maxblocks = fluid_rvoice_mixer_get_bufcount ( synth - > eventhandler - > mixer ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( blockcount > maxblocks )
{
blockcount = maxblocks ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < blockcount ; i + + )
{
fluid_sample_timer_process ( synth ) ;
fluid_synth_add_ticks ( synth , FLUID_BUFSIZE ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all()
* ( should only happen with parallel render ) stop processing and go for rendering
*/
if ( fluid_rvoice_eventhandler_dispatch_count ( synth - > eventhandler ) )
{
// Something has happened, we can't process more
blockcount = i + 1 ;
break ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
fluid_check_fpe ( " fluid_sample_timer_process " ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
blockcount = fluid_rvoice_mixer_render ( synth - > eventhandler - > mixer , blockcount ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Testcase, that provokes a denormal floating point error */
#if 0
{
float num = 1 ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
while ( num ! = 0 )
{
num * = 0.5 ;
} ;
} ;
# endif
fluid_check_fpe ( " ??? Remainder of synth_one_block ??? " ) ;
fluid_profile ( FLUID_PROF_ONE_BLOCK , prof_ref ,
fluid_rvoice_mixer_get_active_voices ( synth - > eventhandler - > mixer ) ,
blockcount * FLUID_BUFSIZE ) ;
return blockcount ;
}
/*
* Handler for synth . reverb . * and synth . chorus . * double settings .
*/
static void fluid_synth_handle_reverb_chorus_num ( void * data , const char * name , double value )
{
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_return_if_fail ( synth ! = NULL ) ;
if ( FLUID_STRCMP ( name , " synth.reverb.room-size " ) = = 0 )
{
fluid_synth_set_reverb_roomsize ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.reverb.damp " ) = = 0 )
{
fluid_synth_set_reverb_damp ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.reverb.width " ) = = 0 )
{
fluid_synth_set_reverb_width ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.reverb.level " ) = = 0 )
{
fluid_synth_set_reverb_level ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.chorus.depth " ) = = 0 )
{
fluid_synth_set_chorus_depth ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.chorus.speed " ) = = 0 )
{
fluid_synth_set_chorus_speed ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.chorus.level " ) = = 0 )
{
fluid_synth_set_chorus_level ( synth , value ) ;
}
}
/*
* Handler for synth . reverb . * and synth . chorus . * integer settings .
*/
static void fluid_synth_handle_reverb_chorus_int ( void * data , const char * name , int value )
{
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_return_if_fail ( synth ! = NULL ) ;
if ( FLUID_STRCMP ( name , " synth.reverb.active " ) = = 0 )
{
fluid_synth_set_reverb_on ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.chorus.active " ) = = 0 )
{
fluid_synth_set_chorus_on ( synth , value ) ;
}
else if ( FLUID_STRCMP ( name , " synth.chorus.nr " ) = = 0 )
{
fluid_synth_set_chorus_nr ( synth , value ) ;
}
}
/*
* Handler for synth . overflow . * settings .
*/
static void fluid_synth_handle_overflow ( void * data , const char * name , double value )
{
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
if ( FLUID_STRCMP ( name , " synth.overflow.percussion " ) = = 0 )
{
synth - > overflow . percussion = value ;
}
else if ( FLUID_STRCMP ( name , " synth.overflow.released " ) = = 0 )
{
synth - > overflow . released = value ;
}
else if ( FLUID_STRCMP ( name , " synth.overflow.sustained " ) = = 0 )
{
synth - > overflow . sustained = value ;
}
else if ( FLUID_STRCMP ( name , " synth.overflow.volume " ) = = 0 )
{
synth - > overflow . volume = value ;
}
else if ( FLUID_STRCMP ( name , " synth.overflow.age " ) = = 0 )
{
synth - > overflow . age = value ;
}
else if ( FLUID_STRCMP ( name , " synth.overflow.important " ) = = 0 )
{
synth - > overflow . important = value ;
}
fluid_synth_api_exit ( synth ) ;
}
/* Selects a voice for killing. */
static fluid_voice_t *
fluid_synth_free_voice_by_kill_LOCAL ( fluid_synth_t * synth )
{
int i ;
float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1 ;
float this_voice_prio ;
fluid_voice_t * voice ;
int best_voice_index = - 1 ;
unsigned int ticks = fluid_synth_get_ticks ( synth ) ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
/* safeguard against an available voice. */
if ( _AVAILABLE ( voice ) )
{
return voice ;
}
this_voice_prio = fluid_voice_get_overflow_prio ( voice , & synth - > overflow ,
ticks ) ;
/* check if this voice has less priority than the previous candidate. */
if ( this_voice_prio < best_prio )
{
best_voice_index = i ;
best_prio = this_voice_prio ;
}
}
if ( best_voice_index < 0 )
{
return NULL ;
}
voice = synth - > voice [ best_voice_index ] ;
FLUID_LOG ( FLUID_DBG , " Killing voice %d, index %d, chan %d, key %d " ,
fluid_voice_get_id ( voice ) , best_voice_index , fluid_voice_get_channel ( voice ) , fluid_voice_get_key ( voice ) ) ;
fluid_voice_off ( voice ) ;
return voice ;
2016-08-23 18:50:54 +02:00
}
/**
* Allocate a synthesis voice .
* @ param synth FluidSynth instance
* @ param sample Sample to assign to the voice
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param key MIDI note number for the voice ( 0 - 127 )
* @ param vel MIDI velocity for the voice ( 0 - 127 )
* @ return Allocated synthesis voice or NULL on error
*
* This function is called by a SoundFont ' s preset in response to a noteon event .
* The returned voice comes with default modulators and generators .
* A single noteon event may create any number of voices , when the preset is layered .
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from within synthesis thread , which includes
2016-08-23 18:50:54 +02:00
* SoundFont loader preset noteon method .
*/
2018-10-18 00:41:02 +02:00
fluid_voice_t *
fluid_synth_alloc_voice ( fluid_synth_t * synth , fluid_sample_t * sample ,
int chan , int key , int vel )
{
fluid_return_val_if_fail ( sample ! = NULL , NULL ) ;
FLUID_API_ENTRY_CHAN ( NULL ) ;
FLUID_API_RETURN ( fluid_synth_alloc_voice_LOCAL ( synth , sample , chan , key , vel , NULL ) ) ;
}
fluid_voice_t *
fluid_synth_alloc_voice_LOCAL ( fluid_synth_t * synth , fluid_sample_t * sample , int chan , int key , int vel , fluid_zone_range_t * zone_range )
{
int i , k ;
fluid_voice_t * voice = NULL ;
fluid_channel_t * channel = NULL ;
unsigned int ticks ;
/* check if there's an available synthesis process */
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
if ( _AVAILABLE ( synth - > voice [ i ] ) )
{
voice = synth - > voice [ i ] ;
break ;
}
}
/* No success yet? Then stop a running voice. */
if ( voice = = NULL )
{
FLUID_LOG ( FLUID_DBG , " Polyphony exceeded, trying to kill a voice " ) ;
voice = fluid_synth_free_voice_by_kill_LOCAL ( synth ) ;
}
if ( voice = = NULL )
{
FLUID_LOG ( FLUID_WARN , " Failed to allocate a synthesis process. (chan=%d,key=%d) " , chan , key ) ;
return NULL ;
}
ticks = fluid_synth_get_ticks ( synth ) ;
if ( synth - > verbose )
{
k = 0 ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
if ( ! _AVAILABLE ( synth - > voice [ i ] ) )
{
k + + ;
}
}
FLUID_LOG ( FLUID_INFO , " noteon \t %d \t %d \t %d \t %05d \t %.3f \t %.3f \t %.3f \t %d " ,
chan , key , vel , synth - > storeid ,
( float ) ticks / 44100.0f ,
( fluid_curtime ( ) - synth - > start ) / 1000.0f ,
0.0f ,
k ) ;
}
channel = synth - > channel [ chan ] ;
if ( fluid_voice_init ( voice , sample , zone_range , channel , key , vel ,
synth - > storeid , ticks , synth - > gain ) ! = FLUID_OK )
{
FLUID_LOG ( FLUID_WARN , " Failed to initialize voice " ) ;
return NULL ;
}
/* add the default modulators to the synthesis process. */
/* custom_breath2att_modulator is not a default modulator specified in SF
it is intended to replace default_vel2att_mod for this channel on demand using
API fluid_synth_set_breath_mode ( ) or shell command setbreathmode for this channel .
*/
{
int mono = fluid_channel_is_playing_mono ( channel ) ;
fluid_mod_t * default_mod = synth - > default_mod ;
while ( default_mod ! = NULL )
{
if (
/* See if default_mod is the velocity_to_attenuation modulator */
fluid_mod_test_identity ( default_mod , & default_vel2att_mod ) & &
// See if a replacement by custom_breath2att_modulator has been demanded
// for this channel
( ( ! mono & & ( channel - > mode & FLUID_CHANNEL_BREATH_POLY ) ) | |
( mono & & ( channel - > mode & FLUID_CHANNEL_BREATH_MONO ) ) )
)
{
// Replacement of default_vel2att modulator by custom_breath2att_modulator
2019-01-02 15:33:23 +01:00
fluid_voice_add_mod_local ( voice , & custom_breath2att_mod , FLUID_VOICE_DEFAULT , 0 ) ;
2018-10-18 00:41:02 +02:00
}
else
{
2019-01-02 15:33:23 +01:00
fluid_voice_add_mod_local ( voice , default_mod , FLUID_VOICE_DEFAULT , 0 ) ;
2018-10-18 00:41:02 +02:00
}
// Next default modulator to add to the voice
default_mod = default_mod - > next ;
}
}
return voice ;
2016-08-23 18:50:54 +02:00
}
/* Kill all voices on a given channel, which have the same exclusive class
* generator as new_voice .
*/
static void
2018-10-18 00:41:02 +02:00
fluid_synth_kill_by_exclusive_class_LOCAL ( fluid_synth_t * synth ,
fluid_voice_t * new_voice )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int excl_class = fluid_voice_gen_value ( new_voice , GEN_EXCLUSIVECLASS ) ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Excl. class 0: No exclusive class */
if ( excl_class = = 0 )
{
return ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Kill all notes on the same channel with the same exclusive class */
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
fluid_voice_t * existing_voice = synth - > voice [ i ] ;
int existing_excl_class = fluid_voice_gen_value ( existing_voice , GEN_EXCLUSIVECLASS ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* If voice is playing, on the same channel, has same exclusive
* class and is not part of the same noteon event ( voice group ) , then kill it */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_playing ( existing_voice )
& & fluid_voice_get_channel ( existing_voice ) = = fluid_voice_get_channel ( new_voice )
& & existing_excl_class = = excl_class
& & fluid_voice_get_id ( existing_voice ) ! = fluid_voice_get_id ( new_voice ) )
{
fluid_voice_kill_excl ( existing_voice ) ;
}
}
2016-08-23 18:50:54 +02:00
}
/**
* Activate a voice previously allocated with fluid_synth_alloc_voice ( ) .
* @ param synth FluidSynth instance
* @ param voice Voice to activate
*
* This function is called by a SoundFont ' s preset in response to a noteon
* event . Exclusive classes are processed here .
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from within synthesis thread , which includes
2016-08-23 18:50:54 +02:00
* SoundFont loader preset noteon method .
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_start_voice ( fluid_synth_t * synth , fluid_voice_t * voice )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_return_if_fail ( voice ! = NULL ) ;
2016-08-23 18:50:54 +02:00
// fluid_return_if_fail (fluid_synth_is_synth_thread (synth));
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Find the exclusive class of this voice. If set, kill all voices
* that match the exclusive class and are younger than the first
* voice process created by this noteon event . */
fluid_synth_kill_by_exclusive_class_LOCAL ( synth , voice ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_voice_start ( voice ) ; /* Start the new voice */
2016-08-23 18:50:54 +02:00
fluid_voice_lock_rvoice ( voice ) ;
2018-10-18 00:41:02 +02:00
fluid_rvoice_eventhandler_add_rvoice ( synth - > eventhandler , voice - > rvoice ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Add a SoundFont loader to the synth . This function takes ownership of \ c loader
* and frees it automatically upon \ c synth destruction .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param loader Loader API structure
2016-08-23 18:50:54 +02:00
*
* SoundFont loaders are used to add custom instrument loading to FluidSynth .
* The caller supplied functions for loading files , allocating presets ,
* retrieving information on them and synthesizing note - on events . Using this
* method even non SoundFont instruments can be synthesized , although limited
* to the SoundFont synthesis model .
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called before any SoundFont files are loaded .
2016-08-23 18:50:54 +02:00
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_add_sfloader ( fluid_synth_t * synth , fluid_sfloader_t * loader )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_return_if_fail ( loader ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
/* Test if sfont is already loaded */
if ( synth - > sfont = = NULL )
{
synth - > loaders = fluid_list_prepend ( synth - > loaders , loader ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Load a SoundFont file ( filename is interpreted by SoundFont loaders ) .
* The newly loaded SoundFont will be put on top of the SoundFont
* stack . Presets are searched starting from the SoundFont on the
* top of the stack , working the way down the stack until a preset is found .
*
2018-10-18 00:41:02 +02:00
* @ param synth FluidSynth instance
2016-08-23 18:50:54 +02:00
* @ param filename File to load
2018-10-18 00:41:02 +02:00
* @ param reset_presets TRUE to re - assign presets for all MIDI channels ( equivalent to calling fluid_synth_program_reset ( ) )
* @ return SoundFont ID on success , # FLUID_FAILED on error
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_sfload ( fluid_synth_t * synth , const char * filename , int reset_presets )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont ;
fluid_list_t * list ;
fluid_sfloader_t * loader ;
int sfont_id ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( filename ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
sfont_id = synth - > sfont_id ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( + + sfont_id ! = FLUID_FAILED )
{
/* MT NOTE: Loaders list should not change. */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > loaders ; list ; list = fluid_list_next ( list ) )
{
loader = ( fluid_sfloader_t * ) fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
sfont = fluid_sfloader_load ( loader , filename ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( sfont ! = NULL )
{
sfont - > refcount + + ;
synth - > sfont_id = sfont - > id = sfont_id ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > sfont = fluid_list_prepend ( synth - > sfont , sfont ) ; /* prepend to list */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* reset the presets for all channels if requested */
if ( reset_presets )
{
fluid_synth_program_reset ( synth ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( sfont_id ) ;
}
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_LOG ( FLUID_ERR , " Failed to load SoundFont \" %s \" " , filename ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Unload a SoundFont .
2018-10-18 00:41:02 +02:00
* @ param synth FluidSynth instance
2016-08-23 18:50:54 +02:00
* @ param id ID of SoundFont to unload
* @ param reset_presets TRUE to re - assign presets for all MIDI channels
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED on error
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_sfunload ( fluid_synth_t * synth , int id , int reset_presets )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont = NULL ;
fluid_list_t * list ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* remove the SoundFont from the list */
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
sfont = fluid_list_get ( list ) ;
if ( fluid_sfont_get_id ( sfont ) = = id )
{
synth - > sfont = fluid_list_remove ( synth - > sfont , sfont ) ;
break ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( ! list )
{
FLUID_LOG ( FLUID_ERR , " No SoundFont with id = %d " , id ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* reset the presets for all channels (SoundFont will be freed when there are no more references) */
if ( reset_presets )
{
fluid_synth_program_reset ( synth ) ;
}
else
{
fluid_synth_update_presets ( synth ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* -- Remove synth->sfont list's reference to SoundFont */
fluid_synth_sfont_unref ( synth , sfont ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/* Unref a SoundFont and destroy if no more references */
void
2018-10-18 00:41:02 +02:00
fluid_synth_sfont_unref ( fluid_synth_t * synth , fluid_sfont_t * sfont )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( sfont ! = NULL ) ; /* Shouldn't happen, programming error if so */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
sfont - > refcount - - ; /* -- Remove the sfont list's reference */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( sfont - > refcount = = 0 ) /* No more references? - Attempt delete */
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
if ( fluid_sfont_delete_internal ( sfont ) = = 0 ) /* SoundFont loader can block SoundFont unload */
{
FLUID_LOG ( FLUID_DBG , " Unloaded SoundFont " ) ;
} /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */
else
{
new_fluid_timer ( 100 , fluid_synth_sfunload_callback , sfont , TRUE , TRUE , FALSE ) ;
}
}
2016-08-23 18:50:54 +02:00
}
/* Callback to continually attempt to unload a SoundFont,
* only if a SoundFont loader blocked the unload operation */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_sfunload_callback ( void * data , unsigned int msec )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont = data ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_sfont_delete_internal ( sfont ) = = 0 )
{
FLUID_LOG ( FLUID_DBG , " Unloaded SoundFont " ) ;
return FALSE ;
}
else
{
return TRUE ;
}
2016-08-23 18:50:54 +02:00
}
/**
* Reload a SoundFont . The SoundFont retains its ID and index on the SoundFont stack .
2018-10-18 00:41:02 +02:00
* @ param synth FluidSynth instance
2016-08-23 18:50:54 +02:00
* @ param id ID of SoundFont to reload
2018-10-18 00:41:02 +02:00
* @ return SoundFont ID on success , # FLUID_FAILED on error
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_sfreload ( fluid_synth_t * synth , int id )
{
char * filename = NULL ;
fluid_sfont_t * sfont ;
fluid_sfloader_t * loader ;
fluid_list_t * list ;
int index , ret = FLUID_FAILED ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Search for SoundFont and get its index */
for ( list = synth - > sfont , index = 0 ; list ; list = fluid_list_next ( list ) , index + + )
{
sfont = fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_sfont_get_id ( sfont ) = = id )
{
break ;
}
}
if ( ! list )
{
FLUID_LOG ( FLUID_ERR , " No SoundFont with id = %d " , id ) ;
goto exit ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* keep a copy of the SoundFont's filename */
filename = FLUID_STRDUP ( fluid_sfont_get_name ( sfont ) ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( filename = = NULL | | fluid_synth_sfunload ( synth , id , FALSE ) ! = FLUID_OK )
{
goto exit ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* MT Note: SoundFont loader list will not change */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > loaders ; list ; list = fluid_list_next ( list ) )
{
loader = ( fluid_sfloader_t * ) fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
sfont = fluid_sfloader_load ( loader , filename ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( sfont ! = NULL )
{
sfont - > id = id ;
sfont - > refcount + + ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > sfont = fluid_list_insert_at ( synth - > sfont , index , sfont ) ; /* insert the sfont at the same index */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* reset the presets for all channels */
fluid_synth_update_presets ( synth ) ;
ret = id ;
goto exit ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
FLUID_LOG ( FLUID_ERR , " Failed to load SoundFont \" %s \" " , filename ) ;
exit :
FLUID_FREE ( filename ) ;
FLUID_API_RETURN ( ret ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Add a SoundFont . The SoundFont will be added to the top of the SoundFont stack .
* @ param synth FluidSynth instance
* @ param sfont SoundFont to add
2018-10-18 00:41:02 +02:00
* @ return New assigned SoundFont ID or # FLUID_FAILED on error
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_add_sfont ( fluid_synth_t * synth , fluid_sfont_t * sfont )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int sfont_id ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( sfont ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
sfont_id = synth - > sfont_id ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( + + sfont_id ! = FLUID_FAILED )
{
synth - > sfont_id = sfont - > id = sfont_id ;
synth - > sfont = fluid_list_prepend ( synth - > sfont , sfont ) ; /* prepend to list */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* reset the presets for all channels */
fluid_synth_program_reset ( synth ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( sfont_id ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Remove a SoundFont from the SoundFont stack without deleting it .
* @ param synth FluidSynth instance
* @ param sfont SoundFont to remove
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK if \ c sfont successfully removed , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
* SoundFont is not freed and is left as the responsibility of the caller .
*
2018-10-18 00:41:02 +02:00
* @ note The SoundFont should only be freed after there are no presets
2016-08-23 18:50:54 +02:00
* referencing it . This can only be ensured by the SoundFont loader and
* therefore this function should not normally be used .
*/
2018-10-18 00:41:02 +02:00
int
fluid_synth_remove_sfont ( fluid_synth_t * synth , fluid_sfont_t * sfont )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont_tmp ;
fluid_list_t * list ;
int ret = FLUID_FAILED ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( sfont ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* remove the SoundFont from the list */
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
sfont_tmp = fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( sfont_tmp = = sfont )
{
synth - > sfont = fluid_list_remove ( synth - > sfont , sfont_tmp ) ;
ret = FLUID_OK ;
break ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* reset the presets for all channels */
fluid_synth_program_reset ( synth ) ;
FLUID_API_RETURN ( ret ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Count number of loaded SoundFont files .
* @ param synth FluidSynth instance
* @ return Count of loaded SoundFont files .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_sfcount ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int count ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
count = fluid_list_size ( synth - > sfont ) ;
FLUID_API_RETURN ( count ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get SoundFont by index .
* @ param synth FluidSynth instance
* @ param num SoundFont index on the stack ( starting from 0 for top of stack ) .
* @ return SoundFont instance or NULL if invalid index
*
2018-10-18 00:41:02 +02:00
* @ note Caller should be certain that SoundFont is not deleted ( unloaded ) for
2016-08-23 18:50:54 +02:00
* the duration of use of the returned pointer .
*/
fluid_sfont_t *
2018-10-18 00:41:02 +02:00
fluid_synth_get_sfont ( fluid_synth_t * synth , unsigned int num )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont = NULL ;
fluid_list_t * list ;
fluid_return_val_if_fail ( synth ! = NULL , NULL ) ;
fluid_synth_api_enter ( synth ) ;
list = fluid_list_nth ( synth - > sfont , num ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( list )
{
sfont = fluid_list_get ( list ) ;
}
FLUID_API_RETURN ( sfont ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get SoundFont by ID .
* @ param synth FluidSynth instance
* @ param id SoundFont ID
* @ return SoundFont instance or NULL if invalid ID
*
2018-10-18 00:41:02 +02:00
* @ note Caller should be certain that SoundFont is not deleted ( unloaded ) for
2016-08-23 18:50:54 +02:00
* the duration of use of the returned pointer .
*/
fluid_sfont_t *
2018-10-18 00:41:02 +02:00
fluid_synth_get_sfont_by_id ( fluid_synth_t * synth , int id )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont = NULL ;
fluid_list_t * list ;
fluid_return_val_if_fail ( synth ! = NULL , NULL ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
{
sfont = fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_sfont_get_id ( sfont ) = = id )
{
break ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( list ? sfont : NULL ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get SoundFont by name .
* @ param synth FluidSynth instance
* @ param name Name of SoundFont
* @ return SoundFont instance or NULL if invalid name
* @ since 1.1 .0
*
2018-10-18 00:41:02 +02:00
* @ note Caller should be certain that SoundFont is not deleted ( unloaded ) for
2016-08-23 18:50:54 +02:00
* the duration of use of the returned pointer .
*/
fluid_sfont_t *
2018-10-18 00:41:02 +02:00
fluid_synth_get_sfont_by_name ( fluid_synth_t * synth , const char * name )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont = NULL ;
fluid_list_t * list ;
fluid_return_val_if_fail ( synth ! = NULL , NULL ) ;
fluid_return_val_if_fail ( name ! = NULL , NULL ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
{
sfont = fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( FLUID_STRCMP ( fluid_sfont_get_name ( sfont ) , name ) = = 0 )
{
break ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( list ? sfont : NULL ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get active preset on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
2018-10-18 00:41:02 +02:00
* @ return Preset or NULL if no preset active on \ c chan
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from within synthesis thread , which includes
* SoundFont loader preset noteon methods . Not thread safe otherwise .
2016-08-23 18:50:54 +02:00
*/
fluid_preset_t *
2018-10-18 00:41:02 +02:00
fluid_synth_get_channel_preset ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_preset_t * result ;
fluid_channel_t * channel ;
FLUID_API_ENTRY_CHAN ( NULL ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
result = channel - > preset ;
fluid_synth_api_exit ( synth ) ;
return result ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Get list of currently playing voices .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
* @ param buf Array to store voices to ( NULL terminated if not filled completely )
* @ param bufsize Count of indexes in buf
* @ param id Voice ID to search for or < 0 to return list of all playing voices
*
2018-10-18 00:41:02 +02:00
* @ note Should only be called from within synthesis thread , which includes
2016-08-23 18:50:54 +02:00
* SoundFont loader preset noteon methods . Voices are only guaranteed to remain
* unchanged until next synthesis process iteration .
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_get_voicelist ( fluid_synth_t * synth , fluid_voice_t * buf [ ] , int bufsize ,
2016-08-23 18:50:54 +02:00
int id )
{
2018-10-18 00:41:02 +02:00
int count = 0 ;
int i ;
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_return_if_fail ( buf ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony & & count < bufsize ; i + + )
{
fluid_voice_t * voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_playing ( voice ) & & ( id < 0 | | ( int ) voice - > id = = id ) )
{
buf [ count + + ] = voice ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( count < bufsize )
{
buf [ count ] = NULL ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Enable or disable reverb effect .
* @ param synth FluidSynth instance
* @ param on TRUE to enable reverb , FALSE to disable
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_set_reverb_on ( fluid_synth_t * synth , int on )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > with_reverb = ( on ! = 0 ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_set_reverb_enabled ,
on ! = 0 , 0.0f ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Activate a reverb preset .
* @ param synth FluidSynth instance
* @ param num Reverb preset number
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ note Currently private to libfluidsynth .
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_set_reverb_preset ( fluid_synth_t * synth , unsigned int num )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail (
num < FLUID_N_ELEMENTS ( revmodel_preset ) ,
FLUID_FAILED
) ;
fluid_synth_set_reverb ( synth , revmodel_preset [ num ] . roomsize ,
revmodel_preset [ num ] . damp , revmodel_preset [ num ] . width ,
revmodel_preset [ num ] . level ) ;
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Set reverb parameters .
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param roomsize Reverb room size value ( 0.0 - 1.0 )
2016-08-23 18:50:54 +02:00
* @ param damping Reverb damping value ( 0.0 - 1.0 )
* @ param width Reverb width value ( 0.0 - 100.0 )
* @ param level Reverb level value ( 0.0 - 1.0 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ note Not realtime safe and therefore should not be called from synthesis
2016-08-23 18:50:54 +02:00
* context at the risk of stalling audio output .
*/
2018-10-18 00:41:02 +02:00
int
fluid_synth_set_reverb ( fluid_synth_t * synth , double roomsize , double damping ,
2016-08-23 18:50:54 +02:00
double width , double level )
{
2018-10-18 00:41:02 +02:00
return fluid_synth_set_reverb_full ( synth , FLUID_REVMODEL_SET_ALL ,
roomsize , damping , width , level ) ;
}
/**
* Set reverb roomsize . See fluid_synth_set_reverb ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_reverb_roomsize ( fluid_synth_t * synth , double roomsize )
{
return fluid_synth_set_reverb_full ( synth , FLUID_REVMODEL_SET_ROOMSIZE , roomsize , 0 , 0 , 0 ) ;
}
/**
* Set reverb damping . See fluid_synth_set_reverb ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_reverb_damp ( fluid_synth_t * synth , double damping )
{
return fluid_synth_set_reverb_full ( synth , FLUID_REVMODEL_SET_DAMPING , 0 , damping , 0 , 0 ) ;
}
/**
* Set reverb width . See fluid_synth_set_reverb ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_reverb_width ( fluid_synth_t * synth , double width )
{
return fluid_synth_set_reverb_full ( synth , FLUID_REVMODEL_SET_WIDTH , 0 , 0 , width , 0 ) ;
}
/**
* Set reverb level . See fluid_synth_set_reverb ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_reverb_level ( fluid_synth_t * synth , double level )
{
return fluid_synth_set_reverb_full ( synth , FLUID_REVMODEL_SET_LEVEL , 0 , 0 , 0 , level ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set one or more reverb parameters .
* @ param synth FluidSynth instance
* @ param set Flags indicating which parameters should be set ( # fluid_revmodel_set_t )
* @ param roomsize Reverb room size value ( 0.0 - 1.2 )
* @ param damping Reverb damping value ( 0.0 - 1.0 )
* @ param width Reverb width value ( 0.0 - 100.0 )
* @ param level Reverb level value ( 0.0 - 1.0 )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ note Not realtime safe and therefore should not be called from synthesis
2016-08-23 18:50:54 +02:00
* context at the risk of stalling audio output .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_set_reverb_full ( fluid_synth_t * synth , int set , double roomsize ,
2016-08-23 18:50:54 +02:00
double damping , double width , double level )
{
2018-10-18 00:41:02 +02:00
int ret ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
/* if non of the flags is set, fail */
fluid_return_val_if_fail ( set & FLUID_REVMODEL_SET_ALL , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Synth shadow values are set here so that they will be returned if querried */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
ret = fluid_synth_set_reverb_full_LOCAL ( synth , set , roomsize , damping , width , level ) ;
FLUID_API_RETURN ( ret ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
static int
fluid_synth_set_reverb_full_LOCAL ( fluid_synth_t * synth , int set , double roomsize ,
double damping , double width , double level )
{
int ret ;
fluid_rvoice_param_t param [ MAX_EVENT_PARAMS ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_REVMODEL_SET_ROOMSIZE )
{
synth - > reverb_roomsize = roomsize ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_REVMODEL_SET_DAMPING )
{
synth - > reverb_damping = damping ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_REVMODEL_SET_WIDTH )
{
synth - > reverb_width = width ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_REVMODEL_SET_LEVEL )
{
synth - > reverb_level = level ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
param [ 0 ] . i = set ;
param [ 1 ] . real = roomsize ;
param [ 2 ] . real = damping ;
param [ 3 ] . real = width ;
param [ 4 ] . real = level ;
/* finally enqueue an rvoice event to the mixer to actual update reverb */
ret = fluid_rvoice_eventhandler_push ( synth - > eventhandler ,
fluid_rvoice_mixer_set_reverb_params ,
synth - > eventhandler - > mixer ,
param ) ;
return ret ;
2016-08-23 18:50:54 +02:00
}
/**
* Get reverb room size .
* @ param synth FluidSynth instance
* @ return Reverb room size ( 0.0 - 1.2 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_reverb_roomsize ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
result = synth - > reverb_roomsize ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get reverb damping .
* @ param synth FluidSynth instance
* @ return Reverb damping value ( 0.0 - 1.0 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_reverb_damp ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > reverb_damping ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get reverb level .
* @ param synth FluidSynth instance
* @ return Reverb level value ( 0.0 - 1.0 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_reverb_level ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > reverb_level ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get reverb width .
* @ param synth FluidSynth instance
* @ return Reverb width value ( 0.0 - 100.0 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_reverb_width ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > reverb_width ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Enable or disable chorus effect .
* @ param synth FluidSynth instance
* @ param on TRUE to enable chorus , FALSE to disable
*/
2018-10-18 00:41:02 +02:00
void
fluid_synth_set_chorus_on ( fluid_synth_t * synth , int on )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > with_chorus = ( on ! = 0 ) ;
fluid_synth_update_mixer ( synth , fluid_rvoice_mixer_set_chorus_enabled ,
on ! = 0 , 0.0f ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Set chorus parameters . It should be turned on with fluid_synth_set_chorus_on ( ) .
* Keep in mind , that the needed CPU time is proportional to ' nr ' .
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
* @ param nr Chorus voice count ( 0 - 99 , CPU time consumption proportional to
* this value )
* @ param level Chorus level ( 0.0 - 10.0 )
* @ param speed Chorus speed in Hz ( 0.29 - 5.0 )
* @ param depth_ms Chorus depth ( max value depends on synth sample rate ,
* 0.0 - 21.0 is safe for sample rate values up to 96 KHz )
* @ param type Chorus waveform type ( # fluid_chorus_mod )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_set_chorus ( fluid_synth_t * synth , int nr , double level ,
double speed , double depth_ms , int type )
{
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_ALL , nr , level , speed ,
depth_ms , type ) ;
}
/**
* Set the chorus voice count . See fluid_synth_set_chorus ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_chorus_nr ( fluid_synth_t * synth , int nr )
{
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_NR , nr , 0 , 0 , 0 , 0 ) ;
}
/**
* Set the chorus level . See fluid_synth_set_chorus ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_chorus_level ( fluid_synth_t * synth , double level )
{
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_LEVEL , 0 , level , 0 , 0 , 0 ) ;
}
/**
* Set the chorus speed . See fluid_synth_set_chorus ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_chorus_speed ( fluid_synth_t * synth , double speed )
{
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_SPEED , 0 , 0 , speed , 0 , 0 ) ;
}
/**
* Set the chorus depth . See fluid_synth_set_chorus ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_chorus_depth ( fluid_synth_t * synth , double depth_ms )
{
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_DEPTH , 0 , 0 , 0 , depth_ms , 0 ) ;
}
/**
* Set the chorus type . See fluid_synth_set_chorus ( ) for further info .
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int fluid_synth_set_chorus_type ( fluid_synth_t * synth , int type )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
return fluid_synth_set_chorus_full ( synth , FLUID_CHORUS_SET_TYPE , 0 , 0 , 0 , 0 , type ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set one or more chorus parameters .
* @ param synth FluidSynth instance
* @ param set Flags indicating which chorus parameters to set ( # fluid_chorus_set_t )
* @ param nr Chorus voice count ( 0 - 99 , CPU time consumption proportional to
* this value )
* @ param level Chorus level ( 0.0 - 10.0 )
* @ param speed Chorus speed in Hz ( 0.29 - 5.0 )
* @ param depth_ms Chorus depth ( max value depends on synth sample rate ,
* 0.0 - 21.0 is safe for sample rate values up to 96 KHz )
* @ param type Chorus waveform type ( # fluid_chorus_mod )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_set_chorus_full ( fluid_synth_t * synth , int set , int nr , double level ,
2016-08-23 18:50:54 +02:00
double speed , double depth_ms , int type )
{
2018-10-18 00:41:02 +02:00
int ret ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
/* if non of the flags is set, fail */
fluid_return_val_if_fail ( set & FLUID_CHORUS_SET_ALL , FLUID_FAILED ) ;
/* Synth shadow values are set here so that they will be returned if queried */
fluid_synth_api_enter ( synth ) ;
ret = fluid_synth_set_chorus_full_LOCAL ( synth , set , nr , level , speed , depth_ms , type ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( ret ) ;
}
static int
fluid_synth_set_chorus_full_LOCAL ( fluid_synth_t * synth , int set , int nr , double level ,
double speed , double depth_ms , int type )
{
int ret ;
fluid_rvoice_param_t param [ MAX_EVENT_PARAMS ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_CHORUS_SET_NR )
{
synth - > chorus_nr = nr ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_CHORUS_SET_LEVEL )
{
synth - > chorus_level = level ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_CHORUS_SET_SPEED )
{
synth - > chorus_speed = speed ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_CHORUS_SET_DEPTH )
{
synth - > chorus_depth = depth_ms ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( set & FLUID_CHORUS_SET_TYPE )
{
synth - > chorus_type = type ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
param [ 0 ] . i = set ;
param [ 1 ] . i = nr ;
param [ 2 ] . real = level ;
param [ 3 ] . real = speed ;
param [ 4 ] . real = depth_ms ;
param [ 5 ] . i = type ;
ret = fluid_rvoice_eventhandler_push ( synth - > eventhandler ,
fluid_rvoice_mixer_set_chorus_params ,
synth - > eventhandler - > mixer ,
param ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return ( ret ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get chorus voice number ( delay line count ) value .
* @ param synth FluidSynth instance
* @ return Chorus voice count ( 0 - 99 )
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_chorus_nr ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > chorus_nr ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get chorus level .
* @ param synth FluidSynth instance
* @ return Chorus level value ( 0.0 - 10.0 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_chorus_level ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > chorus_level ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get chorus speed in Hz .
* @ param synth FluidSynth instance
* @ return Chorus speed in Hz ( 0.29 - 5.0 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_chorus_speed ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > chorus_speed ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get chorus depth .
* @ param synth FluidSynth instance
* @ return Chorus depth
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_chorus_depth ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > chorus_depth ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get chorus waveform type .
* @ param synth FluidSynth instance
* @ return Chorus waveform type ( # fluid_chorus_mod )
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_get_chorus_type ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
double result ;
fluid_return_val_if_fail ( synth ! = NULL , 0.0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > chorus_type ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/*
* If the same note is hit twice on the same channel , then the older
* voice process is advanced to the release stage . Using a mechanical
* MIDI controller , the only way this can happen is when the sustain
* pedal is held . In this case the behaviour implemented here is
* natural for many instruments . Note : One noteon event can trigger
* several voice processes , for example a stereo sample . Don ' t
* release those . . .
*/
2018-10-18 00:41:02 +02:00
void
fluid_synth_release_voice_on_same_note_LOCAL ( fluid_synth_t * synth , int chan ,
int key )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
fluid_voice_t * voice ;
/* storeid is a parameter for fluid_voice_init() */
synth - > storeid = synth - > noteid + + ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* for "monophonic playing" key is the previous sustained note
if it exists ( 0 to 127 ) or INVALID_NOTE otherwise */
if ( key = = INVALID_NOTE )
{
return ;
}
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_playing ( voice )
& & ( fluid_voice_get_channel ( voice ) = = chan )
& & ( fluid_voice_get_key ( voice ) = = key )
& & ( fluid_voice_get_id ( voice ) ! = synth - > noteid ) )
{
/* Id of voices that was sustained by sostenuto */
if ( fluid_voice_is_sostenuto ( voice ) )
{
synth - > storeid = fluid_voice_get_id ( voice ) ;
}
/* Force the voice into release stage (pedaling is ignored) */
fluid_voice_release ( voice ) ;
}
2016-08-23 18:50:54 +02:00
}
}
/**
* Set synthesis interpolation method on one or all MIDI channels .
* @ param synth FluidSynth instance
* @ param chan MIDI channel to set interpolation method on or - 1 for all channels
* @ param interp_method Interpolation method ( # fluid_interp )
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_set_interp_method ( fluid_synth_t * synth , int chan , int interp_method )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
if ( chan < - 1 | | chan > = synth - > midi_channels )
{
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
if ( synth - > channel [ 0 ] = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Channels don't exist (yet)! " ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > midi_channels ; i + + )
{
if ( chan < 0 | | fluid_channel_get_num ( synth - > channel [ i ] ) = = chan )
{
fluid_channel_set_interp_method ( synth - > channel [ i ] , interp_method ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
} ;
/**
* Get the total count of MIDI channels .
* @ param synth FluidSynth instance
* @ return Count of MIDI channels
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_count_midi_channels ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > midi_channels ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the total count of audio channels .
* @ param synth FluidSynth instance
* @ return Count of audio channel stereo pairs ( 1 = 2 channels , 2 = 4 , etc )
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_count_audio_channels ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > audio_channels ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the total number of allocated audio channels . Usually identical to the
* number of audio channels . Can be employed by LADSPA effects subsystem .
*
* @ param synth FluidSynth instance
* @ return Count of audio group stereo pairs ( 1 = 2 channels , 2 = 4 , etc )
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_count_audio_groups ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > audio_groups ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the total number of allocated effects channels .
* @ param synth FluidSynth instance
* @ return Count of allocated effects channels
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_count_effects_channels ( fluid_synth_t * synth )
{
int result ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
result = synth - > effects_channels ;
FLUID_API_RETURN ( result ) ;
}
/**
* Get the total number of allocated effects units .
* @ param synth FluidSynth instance
* @ return Count of allocated effects units
*/
int
fluid_synth_count_effects_groups ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int result ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
result = synth - > effects_groups ;
FLUID_API_RETURN ( result ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Get the synth CPU load value .
* @ param synth FluidSynth instance
* @ return Estimated CPU load value in percent ( 0 - 100 )
*/
double
2018-10-18 00:41:02 +02:00
fluid_synth_get_cpu_load ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
return fluid_atomic_float_get ( & synth - > cpu_load ) ;
2016-08-23 18:50:54 +02:00
}
/* Get tuning for a given bank:program */
static fluid_tuning_t *
2018-10-18 00:41:02 +02:00
fluid_synth_get_tuning ( fluid_synth_t * synth , int bank , int prog )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
if ( ( synth - > tuning = = NULL ) | |
( synth - > tuning [ bank ] = = NULL ) | |
( synth - > tuning [ bank ] [ prog ] = = NULL ) )
{
return NULL ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return synth - > tuning [ bank ] [ prog ] ;
2016-08-23 18:50:54 +02:00
}
/* Replace tuning on a given bank:program (need not already exist).
* Synth mutex should already be locked by caller . */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_replace_tuning_LOCK ( fluid_synth_t * synth , fluid_tuning_t * tuning ,
int bank , int prog , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * old_tuning ;
if ( synth - > tuning = = NULL )
{
synth - > tuning = FLUID_ARRAY ( fluid_tuning_t * * , 128 ) ;
if ( synth - > tuning = = NULL )
{
FLUID_LOG ( FLUID_PANIC , " Out of memory " ) ;
return FLUID_FAILED ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_MEMSET ( synth - > tuning , 0 , 128 * sizeof ( fluid_tuning_t * * ) ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( synth - > tuning [ bank ] = = NULL )
{
synth - > tuning [ bank ] = FLUID_ARRAY ( fluid_tuning_t * , 128 ) ;
if ( synth - > tuning [ bank ] = = NULL )
{
FLUID_LOG ( FLUID_PANIC , " Out of memory " ) ;
return FLUID_FAILED ;
}
FLUID_MEMSET ( synth - > tuning [ bank ] , 0 , 128 * sizeof ( fluid_tuning_t * ) ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
old_tuning = synth - > tuning [ bank ] [ prog ] ;
synth - > tuning [ bank ] [ prog ] = tuning ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( old_tuning )
{
if ( ! fluid_tuning_unref ( old_tuning , 1 ) ) /* -- unref old tuning */
{
/* Replace old tuning if present */
fluid_synth_replace_tuning_LOCAL ( synth , old_tuning , tuning , apply , FALSE ) ;
}
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/* Replace a tuning with a new one in all MIDI channels. new_tuning can be
* NULL , in which case channels are reset to default equal tempered scale . */
static void
2018-10-18 00:41:02 +02:00
fluid_synth_replace_tuning_LOCAL ( fluid_synth_t * synth , fluid_tuning_t * old_tuning ,
fluid_tuning_t * new_tuning , int apply , int unref_new )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_channel_t * channel ;
int old_tuning_unref = 0 ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > midi_channels ; i + + )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ i ] ;
if ( fluid_channel_get_tuning ( channel ) = = old_tuning )
{
old_tuning_unref + + ;
if ( new_tuning )
{
fluid_tuning_ref ( new_tuning ) ; /* ++ ref new tuning for channel */
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_channel_set_tuning ( channel , new_tuning ) ;
if ( apply )
{
fluid_synth_update_voice_tuning_LOCAL ( synth , channel ) ;
}
}
}
/* Send unref old tuning event if any unrefs */
if ( old_tuning & & old_tuning_unref )
{
fluid_tuning_unref ( old_tuning , old_tuning_unref ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( ! unref_new | | ! new_tuning )
{
return ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_tuning_unref ( new_tuning , 1 ) ;
2016-08-23 18:50:54 +02:00
}
/* Update voice tunings in realtime */
static void
2018-10-18 00:41:02 +02:00
fluid_synth_update_voice_tuning_LOCAL ( fluid_synth_t * synth , fluid_channel_t * channel )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_voice_t * voice ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_voice_is_on ( voice ) & & ( voice - > channel = = channel ) )
{
fluid_voice_calculate_gen_pitch ( voice ) ;
fluid_voice_update_param ( voice , GEN_PITCH ) ;
}
}
2016-08-23 18:50:54 +02:00
}
/**
* Set the tuning of the entire MIDI note scale .
* @ param synth FluidSynth instance
* @ param bank Tuning bank number ( 0 - 127 ) , not related to MIDI instrument bank
* @ param prog Tuning preset number ( 0 - 127 ) , not related to MIDI instrument program
* @ param name Label name for this tuning
* @ param pitch Array of pitch values ( length of 128 , each value is number of
* cents , for example normally note 0 is 0.0 , 1 is 100.0 , 60 is 6000.0 , etc ) .
2018-10-18 00:41:02 +02:00
* Pass NULL to create a equal tempered ( normal ) scale .
2016-08-23 18:50:54 +02:00
* @ param apply TRUE to apply new tuning in realtime to existing notes which
* are using the replaced tuning ( if any ) , FALSE otherwise
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_activate_key_tuning ( fluid_synth_t * synth , int bank , int prog ,
const char * name , const double * pitch , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * tuning ;
int retval = FLUID_OK ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( bank > = 0 & & bank < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( prog > = 0 & & prog < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( name ! = NULL , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
tuning = new_fluid_tuning ( name , bank , prog ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( tuning )
{
if ( pitch )
{
fluid_tuning_set_all ( tuning , pitch ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
retval = fluid_synth_replace_tuning_LOCK ( synth , tuning , bank , prog , apply ) ;
if ( retval = = FLUID_FAILED )
{
fluid_tuning_unref ( tuning , 1 ) ;
}
}
else
{
retval = FLUID_FAILED ;
}
FLUID_API_RETURN ( retval ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Activate an octave tuning on every octave in the MIDI note scale .
* @ param synth FluidSynth instance
* @ param bank Tuning bank number ( 0 - 127 ) , not related to MIDI instrument bank
* @ param prog Tuning preset number ( 0 - 127 ) , not related to MIDI instrument program
* @ param name Label name for this tuning
* @ param pitch Array of pitch values ( length of 12 for each note of an octave
* starting at note C , values are number of offset cents to add to the normal
* tuning amount )
* @ param apply TRUE to apply new tuning in realtime to existing notes which
* are using the replaced tuning ( if any ) , FALSE otherwise
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_activate_octave_tuning ( fluid_synth_t * synth , int bank , int prog ,
const char * name , const double * pitch , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * tuning ;
int retval = FLUID_OK ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( bank > = 0 & & bank < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( prog > = 0 & & prog < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( name ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( pitch ! = NULL , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
tuning = new_fluid_tuning ( name , bank , prog ) ;
if ( tuning )
{
fluid_tuning_set_octave ( tuning , pitch ) ;
retval = fluid_synth_replace_tuning_LOCK ( synth , tuning , bank , prog , apply ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( retval = = FLUID_FAILED )
{
fluid_tuning_unref ( tuning , 1 ) ;
}
}
else
{
retval = FLUID_FAILED ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( retval ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Set tuning values for one or more MIDI notes for an existing tuning .
* @ param synth FluidSynth instance
* @ param bank Tuning bank number ( 0 - 127 ) , not related to MIDI instrument bank
* @ param prog Tuning preset number ( 0 - 127 ) , not related to MIDI instrument program
* @ param len Number of MIDI notes to assign
* @ param key Array of MIDI key numbers ( length of ' len ' , values 0 - 127 )
* @ param pitch Array of pitch values ( length of ' len ' , values are number of
* cents from MIDI note 0 )
* @ param apply TRUE to apply tuning change in realtime to existing notes using
* the specified tuning , FALSE otherwise
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ note Prior to version 1.1 .0 it was an error to specify a tuning that didn ' t
* already exist . Starting with 1.1 .0 , the default equal tempered scale will be
2016-08-23 18:50:54 +02:00
* used as a basis , if no tuning exists for the given bank and prog .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_tune_notes ( fluid_synth_t * synth , int bank , int prog ,
int len , const int * key , const double * pitch , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * old_tuning , * new_tuning ;
int retval = FLUID_OK ;
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( bank > = 0 & & bank < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( prog > = 0 & & prog < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( len > 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( key ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( pitch ! = NULL , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
old_tuning = fluid_synth_get_tuning ( synth , bank , prog ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( old_tuning )
{
new_tuning = fluid_tuning_duplicate ( old_tuning ) ;
}
else
{
new_tuning = new_fluid_tuning ( " Unnamed " , bank , prog ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( new_tuning )
{
for ( i = 0 ; i < len ; i + + )
{
fluid_tuning_set_pitch ( new_tuning , key [ i ] , pitch [ i ] ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
retval = fluid_synth_replace_tuning_LOCK ( synth , new_tuning , bank , prog , apply ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( retval = = FLUID_FAILED )
{
fluid_tuning_unref ( new_tuning , 1 ) ;
}
}
else
{
retval = FLUID_FAILED ;
}
FLUID_API_RETURN ( retval ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Activate a tuning scale on a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param bank Tuning bank number ( 0 - 127 ) , not related to MIDI instrument bank
* @ param prog Tuning preset number ( 0 - 127 ) , not related to MIDI instrument program
* @ param apply TRUE to apply tuning change to active notes , FALSE otherwise
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*
2018-10-18 00:41:02 +02:00
* @ note A default equal tempered scale will be created , if no tuning exists
2016-08-23 18:50:54 +02:00
* on the given bank and prog .
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_activate_tuning ( fluid_synth_t * synth , int chan , int bank , int prog ,
2016-08-23 18:50:54 +02:00
int apply )
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * tuning ;
int retval = FLUID_OK ;
//fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
//fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
fluid_return_val_if_fail ( bank > = 0 & & bank < 128 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( prog > = 0 & & prog < 128 , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
tuning = fluid_synth_get_tuning ( synth , bank , prog ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* If no tuning exists, create a new default tuning. We do this, so that
* it can be replaced later , if any changes are made . */
if ( ! tuning )
{
tuning = new_fluid_tuning ( " Unnamed " , bank , prog ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( tuning )
{
fluid_synth_replace_tuning_LOCK ( synth , tuning , bank , prog , FALSE ) ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( tuning )
{
fluid_tuning_ref ( tuning ) ; /* ++ ref for outside of lock */
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ! tuning )
{
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_tuning_ref ( tuning ) ; /* ++ ref new tuning for following function */
retval = fluid_synth_set_tuning_LOCAL ( synth , chan , tuning , apply ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_tuning_unref ( tuning , 1 ) ; /* -- unref for outside of lock */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( retval ) ;
2016-08-23 18:50:54 +02:00
}
/* Local synthesis thread set tuning function (takes over tuning reference) */
static int
2018-10-18 00:41:02 +02:00
fluid_synth_set_tuning_LOCAL ( fluid_synth_t * synth , int chan ,
fluid_tuning_t * tuning , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_tuning_t * old_tuning ;
fluid_channel_t * channel ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
channel = synth - > channel [ chan ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
old_tuning = fluid_channel_get_tuning ( channel ) ;
fluid_channel_set_tuning ( channel , tuning ) ; /* !! Takes over callers reference */
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( apply )
{
fluid_synth_update_voice_tuning_LOCAL ( synth , channel ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Send unref old tuning event */
if ( old_tuning )
{
fluid_tuning_unref ( old_tuning , 1 ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_OK ;
2016-08-23 18:50:54 +02:00
}
/**
* Clear tuning scale on a MIDI channel ( use default equal tempered scale ) .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param apply TRUE to apply tuning change to active notes , FALSE otherwise
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
2016-08-23 18:50:54 +02:00
* @ since 1.1 .0
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_deactivate_tuning ( fluid_synth_t * synth , int chan , int apply )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int retval = FLUID_OK ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
retval = fluid_synth_set_tuning_LOCAL ( synth , chan , NULL , apply ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( retval ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Start tuning iteration .
* @ param synth FluidSynth instance
*/
void
2018-10-18 00:41:02 +02:00
fluid_synth_tuning_iteration_start ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_if_fail ( synth ! = NULL ) ;
fluid_synth_api_enter ( synth ) ;
fluid_private_set ( synth - > tuning_iter , FLUID_INT_TO_POINTER ( 0 ) ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
/**
* Advance to next tuning .
* @ param synth FluidSynth instance
* @ param bank Location to store MIDI bank number of next tuning scale
* @ param prog Location to store MIDI program number of next tuning scale
* @ return 1 if tuning iteration advanced , 0 if no more tunings
*/
int
2018-10-18 00:41:02 +02:00
fluid_synth_tuning_iteration_next ( fluid_synth_t * synth , int * bank , int * prog )
{
void * pval ;
int b = 0 , p = 0 ;
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_return_val_if_fail ( bank ! = NULL , 0 ) ;
fluid_return_val_if_fail ( prog ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
/* Current tuning iteration stored as: bank << 8 | program */
pval = fluid_private_get ( synth - > tuning_iter ) ;
p = FLUID_POINTER_TO_INT ( pval ) ;
b = ( p > > 8 ) & 0xFF ;
p & = 0xFF ;
if ( ! synth - > tuning )
{
FLUID_API_RETURN ( 0 ) ;
}
for ( ; b < 128 ; b + + , p = 0 )
{
if ( synth - > tuning [ b ] = = NULL )
{
continue ;
}
for ( ; p < 128 ; p + + )
{
if ( synth - > tuning [ b ] [ p ] = = NULL )
{
continue ;
}
* bank = b ;
* prog = p ;
if ( p < 127 )
{
fluid_private_set ( synth - > tuning_iter ,
FLUID_INT_TO_POINTER ( b < < 8 | ( p + 1 ) ) ) ;
}
else
{
fluid_private_set ( synth - > tuning_iter , FLUID_INT_TO_POINTER ( ( b + 1 ) < < 8 ) ) ;
}
FLUID_API_RETURN ( 1 ) ;
}
}
FLUID_API_RETURN ( 0 ) ;
}
/**
* Get the entire note tuning for a given MIDI bank and program .
* @ param synth FluidSynth instance
* @ param bank MIDI bank number of tuning
* @ param prog MIDI program number of tuning
* @ param name Location to store tuning name or NULL to ignore
* @ param len Maximum number of chars to store to ' name ' ( including NULL byte )
* @ param pitch Array to store tuning scale to or NULL to ignore ( len of 128 )
* @ return # FLUID_OK if matching tuning was found , # FLUID_FAILED otherwise
*/
int
fluid_synth_tuning_dump ( fluid_synth_t * synth , int bank , int prog ,
char * name , int len , double * pitch )
{
fluid_tuning_t * tuning ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
tuning = fluid_synth_get_tuning ( synth , bank , prog ) ;
if ( tuning )
{
if ( name )
{
FLUID_SNPRINTF ( name , len - 1 , " %s " , fluid_tuning_get_name ( tuning ) ) ;
name [ len - 1 ] = 0 ; /* make sure the string is null terminated */
}
if ( pitch )
{
FLUID_MEMCPY ( pitch , fluid_tuning_get_all ( tuning ) , 128 * sizeof ( double ) ) ;
}
}
FLUID_API_RETURN ( tuning ? FLUID_OK : FLUID_FAILED ) ;
}
/**
* Get settings assigned to a synth .
* @ param synth FluidSynth instance
* @ return FluidSynth settings which are assigned to the synth
*/
fluid_settings_t *
fluid_synth_get_settings ( fluid_synth_t * synth )
{
fluid_return_val_if_fail ( synth ! = NULL , NULL ) ;
return synth - > settings ;
}
/**
* Set a SoundFont generator ( effect ) value on a MIDI channel in real - time .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param param SoundFont generator ID ( # fluid_gen_type )
* @ param value Offset or absolute generator value to assign to the MIDI channel
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*
* This function allows for setting all effect parameters in real time on a
* MIDI channel . Setting absolute to non - zero will cause the value to override
* any generator values set in the instruments played on the MIDI channel .
* See SoundFont 2.01 spec , paragraph 8.1 .3 , page 48 for details on SoundFont
* generator parameters and valid ranges .
*/
int fluid_synth_set_gen ( fluid_synth_t * synth , int chan , int param , float value )
{
fluid_return_val_if_fail ( param > = 0 & & param < GEN_LAST , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2019-02-23 17:58:05 +01:00
fluid_synth_set_gen_LOCAL ( synth , chan , param , value ) ;
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( FLUID_OK ) ;
}
/* Synthesis thread local set gen function */
static void
2019-02-23 17:58:05 +01:00
fluid_synth_set_gen_LOCAL ( fluid_synth_t * synth , int chan , int param , float value )
2018-10-18 00:41:02 +02:00
{
fluid_voice_t * voice ;
int i ;
2019-02-23 17:58:05 +01:00
fluid_channel_set_gen ( synth - > channel [ chan ] , param , value ) ;
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
if ( fluid_voice_get_channel ( voice ) = = chan )
{
2019-02-23 17:58:05 +01:00
fluid_voice_set_param ( voice , param , value ) ;
2018-10-18 00:41:02 +02:00
}
}
}
/**
* Get generator value assigned to a MIDI channel .
* @ param synth FluidSynth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param param SoundFont generator ID ( # fluid_gen_type )
* @ return Current generator value assigned to MIDI channel
*/
float
fluid_synth_get_gen ( fluid_synth_t * synth , int chan , int param )
{
float result ;
fluid_return_val_if_fail ( param > = 0 & & param < GEN_LAST , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
result = fluid_channel_get_gen ( synth - > channel [ chan ] , param ) ;
FLUID_API_RETURN ( result ) ;
}
/**
* Handle MIDI event from MIDI router , used as a callback function .
* @ param data FluidSynth instance
* @ param event MIDI event to handle
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*/
int
fluid_synth_handle_midi_event ( void * data , fluid_midi_event_t * event )
{
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
int type = fluid_midi_event_get_type ( event ) ;
int chan = fluid_midi_event_get_channel ( event ) ;
switch ( type )
{
case NOTE_ON :
return fluid_synth_noteon ( synth , chan ,
fluid_midi_event_get_key ( event ) ,
fluid_midi_event_get_velocity ( event ) ) ;
case NOTE_OFF :
return fluid_synth_noteoff ( synth , chan , fluid_midi_event_get_key ( event ) ) ;
case CONTROL_CHANGE :
return fluid_synth_cc ( synth , chan ,
fluid_midi_event_get_control ( event ) ,
fluid_midi_event_get_value ( event ) ) ;
case PROGRAM_CHANGE :
return fluid_synth_program_change ( synth , chan , fluid_midi_event_get_program ( event ) ) ;
case CHANNEL_PRESSURE :
return fluid_synth_channel_pressure ( synth , chan , fluid_midi_event_get_program ( event ) ) ;
case KEY_PRESSURE :
return fluid_synth_key_pressure ( synth , chan ,
fluid_midi_event_get_key ( event ) ,
fluid_midi_event_get_value ( event ) ) ;
case PITCH_BEND :
return fluid_synth_pitch_bend ( synth , chan , fluid_midi_event_get_pitch ( event ) ) ;
case MIDI_SYSTEM_RESET :
return fluid_synth_system_reset ( synth ) ;
case MIDI_SYSEX :
return fluid_synth_sysex ( synth , event - > paramptr , event - > param1 , NULL , NULL , NULL , FALSE ) ;
case MIDI_TEXT :
case MIDI_LYRIC :
case MIDI_SET_TEMPO :
return FLUID_OK ;
}
return FLUID_FAILED ;
}
/**
* Create and start voices using a preset and a MIDI note on event .
* @ param synth FluidSynth instance
* @ param id Voice group ID to use ( can be used with fluid_synth_stop ( ) ) .
* @ param preset Preset to synthesize
* @ param audio_chan Unused currently , set to 0
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param key MIDI note number ( 0 - 127 )
* @ param vel MIDI velocity number ( 1 - 127 )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*
* @ note Should only be called from within synthesis thread , which includes
* SoundFont loader preset noteon method .
*/
int
fluid_synth_start ( fluid_synth_t * synth , unsigned int id , fluid_preset_t * preset ,
int audio_chan , int chan , int key , int vel )
{
int result ;
fluid_return_val_if_fail ( preset ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( key > = 0 & & key < = 127 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( vel > = 1 & & vel < = 127 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
synth - > storeid = id ;
result = fluid_preset_noteon ( preset , synth , chan , key , vel ) ;
FLUID_API_RETURN ( result ) ;
}
/**
* Stop notes for a given note event voice ID .
* @ param synth FluidSynth instance
* @ param id Voice note event ID
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
*
* @ note In FluidSynth versions prior to 1.1 .0 # FLUID_FAILED would be returned
* if no matching voice note event ID was found . Versions after 1.1 .0 only
* return # FLUID_FAILED if an error occurs .
*/
int
fluid_synth_stop ( fluid_synth_t * synth , unsigned int id )
{
int result ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
fluid_synth_stop_LOCAL ( synth , id ) ;
result = FLUID_OK ;
FLUID_API_RETURN ( result ) ;
}
/* Local synthesis thread variant of fluid_synth_stop */
static void
fluid_synth_stop_LOCAL ( fluid_synth_t * synth , unsigned int id )
{
fluid_voice_t * voice ;
int i ;
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
if ( fluid_voice_is_on ( voice ) & & ( fluid_voice_get_id ( voice ) = = id ) )
{
fluid_voice_noteoff ( voice ) ;
}
}
}
/**
* Offset the bank numbers of a loaded SoundFont , i . e . \ subtract
* \ c offset from any bank number when assigning instruments .
*
* @ param synth FluidSynth instance
* @ param sfont_id ID of a loaded SoundFont
* @ param offset Bank offset value to apply to all instruments
* @ return # FLUID_OK if the offset was set successfully , # FLUID_FAILED otherwise
*/
int
fluid_synth_set_bank_offset ( fluid_synth_t * synth , int sfont_id , int offset )
{
fluid_sfont_t * sfont ;
fluid_list_t * list ;
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
{
sfont = fluid_list_get ( list ) ;
if ( fluid_sfont_get_id ( sfont ) = = sfont_id )
{
sfont - > bankofs = offset ;
break ;
}
}
if ( ! list )
{
FLUID_LOG ( FLUID_ERR , " No SoundFont with id = %d " , sfont_id ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
FLUID_API_RETURN ( FLUID_OK ) ;
}
/**
* Get bank offset of a loaded SoundFont .
* @ param synth FluidSynth instance
* @ param sfont_id ID of a loaded SoundFont
* @ return SoundFont bank offset value
*/
int
fluid_synth_get_bank_offset ( fluid_synth_t * synth , int sfont_id )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_sfont_t * sfont ;
fluid_list_t * list ;
int offset = 0 ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , 0 ) ;
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( list = synth - > sfont ; list ; list = fluid_list_next ( list ) )
{
sfont = fluid_list_get ( list ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( fluid_sfont_get_id ( sfont ) = = sfont_id )
{
offset = sfont - > bankofs ;
break ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ! list )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
FLUID_LOG ( FLUID_ERR , " No SoundFont with id = %d " , sfont_id ) ;
FLUID_API_RETURN ( 0 ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( offset ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
void
fluid_synth_api_enter ( fluid_synth_t * synth )
{
if ( synth - > use_mutex )
{
fluid_rec_mutex_lock ( synth - > mutex ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ! synth - > public_api_count )
{
fluid_synth_check_finished_voices ( synth ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
synth - > public_api_count + + ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
void fluid_synth_api_exit ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
synth - > public_api_count - - ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( ! synth - > public_api_count )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_rvoice_eventhandler_flush ( synth - > eventhandler ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
if ( synth - > use_mutex )
{
fluid_rec_mutex_unlock ( synth - > mutex ) ;
}
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Set midi channel type
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param type MIDI channel type ( # fluid_midi_channel_type )
* @ return # FLUID_OK on success , # FLUID_FAILED otherwise
* @ since 1.1 .4
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_set_channel_type ( fluid_synth_t * synth , int chan , int type )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( ( type > = CHANNEL_TYPE_MELODIC ) & & ( type < = CHANNEL_TYPE_DRUM ) , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > channel [ chan ] - > channel_type = type ;
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
# ifdef LADSPA
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Return the LADSPA effects instance used by FluidSynth
*
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
fluid_ladspa_fx_t * fluid_synth_get_ladspa_fx ( fluid_synth_t * synth )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , NULL ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return synth - > ladspa_fx ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
# endif
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Configure a general - purpose IIR biquad filter .
*
* This is an optional , additional filter that operates independently from the default low - pass filter required by the Soundfont2 standard .
* By default this filter is off ( # FLUID_IIR_DISABLED ) .
*
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param type Type of the IIR filter to use ( see # fluid_iir_filter_type )
* @ param flags Additional flags to customize this filter or zero to stay with the default ( see # fluid_iir_filter_flags )
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ return # FLUID_OK if the settings have been successfully applied , otherwise # FLUID_FAILED
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_set_custom_filter ( fluid_synth_t * synth , int type , int flags )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
fluid_voice_t * voice ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_return_val_if_fail ( type > = FLUID_IIR_DISABLED & & type < FLUID_IIR_LAST , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
synth - > custom_filter_type = type ;
synth - > custom_filter_flags = flags ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < synth - > polyphony ; i + + )
{
voice = synth - > voice [ i ] ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_voice_set_custom_filter ( voice , type , flags ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Set the important channels for voice overflow priority calculation .
*
2016-08-23 18:50:54 +02:00
* @ param synth FluidSynth instance
2018-10-18 00:41:02 +02:00
* @ param channels comma - separated list of channel numbers
* @ return # FLUID_OK on success , otherwise # FLUID_FAILED
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
static int fluid_synth_set_important_channels ( fluid_synth_t * synth , const char * channels )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
int retval = FLUID_FAILED ;
int * values = NULL ;
int num_values ;
fluid_overflow_prio_t * scores ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
scores = & synth - > overflow ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( scores - > num_important_channels < synth - > midi_channels )
{
scores - > important_channels = FLUID_REALLOC ( scores - > important_channels ,
sizeof ( * scores - > important_channels ) * synth - > midi_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( scores - > important_channels = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
goto exit ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
scores - > num_important_channels = synth - > midi_channels ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
FLUID_MEMSET ( scores - > important_channels , FALSE ,
sizeof ( * scores - > important_channels ) * scores - > num_important_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( channels ! = NULL )
{
values = FLUID_ARRAY ( int , synth - > midi_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( values = = NULL )
{
FLUID_LOG ( FLUID_ERR , " Out of memory " ) ;
goto exit ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Every channel given in the comma-separated list of channel numbers
* is set to TRUE , i . e . flagging it as " important " . Channel numbers are
* 1 - based . */
num_values = fluid_settings_split_csv ( channels , values , synth - > midi_channels ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = 0 ; i < num_values ; i + + )
{
if ( values [ i ] > 0 & & values [ i ] < = synth - > midi_channels )
{
scores - > important_channels [ values [ i ] - 1 ] = TRUE ;
}
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
retval = FLUID_OK ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
exit :
FLUID_FREE ( values ) ;
return retval ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/*
* Handler for synth . overflow . important - channels setting .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
static void fluid_synth_handle_important_channels ( void * data , const char * name ,
const char * value )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_synth_t * synth = ( fluid_synth_t * ) data ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
fluid_synth_api_enter ( synth ) ;
fluid_synth_set_important_channels ( synth , value ) ;
fluid_synth_api_exit ( synth ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/** API legato mode *********************************************************/
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Sets the legato mode of a channel .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param legatomode The legato mode as indicated by # fluid_channel_legato_mode .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a legatomode is invalid .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_set_legato_mode ( fluid_synth_t * synth , int chan , int legatomode )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
/* checks parameters first */
fluid_return_val_if_fail ( legatomode > = 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
synth - > channel [ chan ] - > legatomode = legatomode ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
}
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Gets the legato mode of a channel .
*
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param legatomode The legato mode as indicated by # fluid_channel_legato_mode .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a legatomode is NULL .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_get_legato_mode ( fluid_synth_t * synth , int chan , int * legatomode )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
/* checks parameters first */
fluid_return_val_if_fail ( legatomode ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
* legatomode = synth - > channel [ chan ] - > legatomode ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/** API portamento mode *********************************************************/
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/**
* Sets the portamento mode of a channel .
*
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param portamentomode The portamento mode as indicated by # fluid_channel_portamento_mode .
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a portamentomode is invalid .
*/
int fluid_synth_set_portamento_mode ( fluid_synth_t * synth , int chan ,
int portamentomode )
{
/* checks parameters first */
fluid_return_val_if_fail ( portamentomode > = 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
synth - > channel [ chan ] - > portamentomode = portamentomode ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Gets the portamento mode of a channel .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param portamentomode Pointer to the portamento mode as indicated by # fluid_channel_portamento_mode .
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a portamentomode is NULL .
*/
int fluid_synth_get_portamento_mode ( fluid_synth_t * synth , int chan ,
int * portamentomode )
{
/* checks parameters first */
fluid_return_val_if_fail ( portamentomode ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
* portamentomode = synth - > channel [ chan ] - > portamentomode ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
}
/** API breath mode *********************************************************/
/**
* Sets the breath mode of a channel .
*
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param breathmode The breath mode as indicated by # fluid_channel_breath_flags .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_set_breath_mode ( fluid_synth_t * synth , int chan , int breathmode )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
/* checks parameters first */
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
fluid_channel_set_breath_info ( synth - > channel [ chan ] , breathmode ) ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Gets the breath mode of a channel .
2016-08-23 18:50:54 +02:00
*
2018-10-18 00:41:02 +02:00
* @ param synth the synth instance .
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 ) .
* @ param breathmode Pointer to the returned breath mode as indicated by # fluid_channel_breath_flags .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a breathmode is NULL .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_get_breath_mode ( fluid_synth_t * synth , int chan , int * breathmode )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
/* checks parameters first */
fluid_return_val_if_fail ( breathmode ! = NULL , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
* breathmode = fluid_channel_get_breath_info ( synth - > channel [ chan ] ) ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/** API Poly/mono mode ******************************************************/
/*
* Resets a basic channel group of MIDI channels .
* @ param synth the synth instance .
* @ param chan the beginning channel of the group .
* @ param nbr_chan the number of channel in the group .
*/
2016-08-23 18:50:54 +02:00
static void
2018-10-18 00:41:02 +02:00
fluid_synth_reset_basic_channel_LOCAL ( fluid_synth_t * synth , int chan , int nbr_chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
for ( i = chan ; i < chan + nbr_chan ; i + + )
{
fluid_channel_reset_basic_channel_info ( synth - > channel [ i ] ) ;
synth - > channel [ i ] - > mode_val = 0 ;
}
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Disables and unassigns all channels from a basic channel group .
*
* @ param synth The synth instance .
* @ param chan The basic channel of the group to reset or - 1 to reset all channels .
* @ note By default ( i . e . on creation after new_fluid_synth ( ) and after fluid_synth_system_reset ( ) )
* a synth instance has one basic channel at channel 0 in mode # FLUID_CHANNEL_MODE_OMNION_POLY .
* All other channels belong to this basic channel group . Make sure to call this function before
* setting any custom basic channel setup .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a chan isn ' t a basic channel .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
int fluid_synth_reset_basic_channel ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int nbr_chan ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* checks parameters first */
if ( chan < 0 )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
fluid_return_val_if_fail ( synth ! = NULL , FLUID_FAILED ) ;
fluid_synth_api_enter ( synth ) ;
/* The range is all MIDI channels from 0 to MIDI channel count -1 */
chan = 0 ; /* beginning chan */
nbr_chan = synth - > midi_channels ; /* MIDI Channels number */
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
else
{
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* checks if chan is a basic channel */
if ( ! ( synth - > channel [ chan ] - > mode & FLUID_CHANNEL_BASIC ) )
{
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* The range is all MIDI channels in the group from chan */
nbr_chan = synth - > channel [ chan ] - > mode_val ; /* nbr of channels in the group */
}
/* resets the range of MIDI channels */
fluid_synth_reset_basic_channel_LOCAL ( synth , chan , nbr_chan ) ;
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
/**
2018-10-18 00:41:02 +02:00
* Checks if a new basic channel group overlaps the next basic channel group .
*
* On success the function returns the possible number of channel for this
* new basic channel group .
* The function fails if the new group overlaps the next basic channel group .
*
* @ param see fluid_synth_set_basic_channel .
* @ return
* - On success , the effective number of channels for this new basic channel group ,
* # FLUID_FAILED otherwise .
* - # FLUID_FAILED
* - \ a val has a number of channels overlapping next basic channel group or been
* above MIDI channel count .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
static int
fluid_synth_check_next_basic_channel ( fluid_synth_t * synth , int basicchan , int mode , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i , n_chan = synth - > midi_channels ; /* MIDI Channels count */
int real_val = val ; /* real number of channels in the group */
/* adjusts val range */
if ( mode = = FLUID_CHANNEL_MODE_OMNIOFF_POLY )
{
real_val = 1 ; /* mode poly omnioff implies a group of only one channel.*/
}
else if ( val = = 0 )
{
/* mode poly omnion (0), mono omnion (1), mono omni off (3) */
/* value 0 means all possible channels from basicchan to MIDI channel count -1.*/
real_val = n_chan - basicchan ;
}
/* checks if val range is above MIDI channel count */
else if ( basicchan + val > n_chan )
{
return FLUID_FAILED ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* checks if this basic channel group overlaps next basic channel group */
for ( i = basicchan + 1 ; i < basicchan + real_val ; i + + )
{
if ( synth - > channel [ i ] - > mode & FLUID_CHANNEL_BASIC )
{
/* A value of 0 for val means all possible channels from basicchan to
to the next basic channel - 1 ( if any ) .
When i reachs the next basic channel group , real_val will be
limited if it is possible */
if ( val = = 0 )
{
/* limitation of real_val */
real_val = i - basicchan ;
break ;
}
/* overlap with the next basic channel group */
return FLUID_FAILED ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return real_val ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/**
* Sets a new basic channel group only . The function doesn ' t allow to change an
* existing basic channel .
*
* The function fails if any channel overlaps any existing basic channel group .
* To make room if necessary , basic channel groups can be cleared using
* fluid_synth_reset_basic_channel ( ) .
*
* @ param synth the synth instance .
* @ param chan the basic Channel number ( 0 to MIDI channel count - 1 ) .
* @ param mode the MIDI mode to use for chan ( see # fluid_basic_channel_modes ) .
* @ param val number of channels in the group .
* @ note \ a val is only relevant for mode # FLUID_CHANNEL_MODE_OMNION_POLY ,
* # FLUID_CHANNEL_MODE_OMNION_MONO and # FLUID_CHANNEL_MODE_OMNIOFF_MONO . A value
* of 0 means all possible channels from \ a chan to to next basic channel minus 1 ( if any )
* or to MIDI channel count minus 1. Val is ignored for # FLUID_CHANNEL_MODE_OMNIOFF_POLY
* as this mode implies a group of only one channel .
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
* - \ a mode is invalid .
* - \ a val has a number of channels overlapping another basic channel group or been
* above MIDI channel count .
* - When the function fails , any existing basic channels aren ' t modified .
*/
int fluid_synth_set_basic_channel ( fluid_synth_t * synth , int chan , int mode , int val )
{
/* check parameters */
fluid_return_val_if_fail ( mode > = 0 , FLUID_FAILED ) ;
fluid_return_val_if_fail ( mode < FLUID_CHANNEL_MODE_LAST , FLUID_FAILED ) ;
fluid_return_val_if_fail ( val > = 0 , FLUID_FAILED ) ;
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
/**/
if ( val > 0 & & chan + val > synth - > midi_channels )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
FLUID_API_RETURN ( FLUID_FAILED ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/* Checks if there is an overlap with the next basic channel */
val = fluid_synth_check_next_basic_channel ( synth , chan , mode , val ) ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
if ( val = = FLUID_FAILED | | synth - > channel [ chan ] - > mode & FLUID_CHANNEL_ENABLED )
{
/* overlap with the next or previous channel group */
FLUID_LOG ( FLUID_INFO , " basic channel %d overlaps another group " , chan ) ;
FLUID_API_RETURN ( FLUID_FAILED ) ;
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* sets a new basic channel group */
fluid_synth_set_basic_channel_LOCAL ( synth , chan , mode , val ) ;
/**/
FLUID_API_RETURN ( FLUID_OK ) ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/*
* Local version of fluid_synth_set_basic_channel ( ) , called internally :
* - by fluid_synth_set_basic_channel ( ) to set a new basic channel group .
* - during creation new_fluid_synth ( ) or on CC reset to set a default basic channel group .
* - on CC ominoff , CC omnion , CC poly , CC mono to change an existing basic channel group .
*
* @ param see fluid_synth_set_basic_channel ( )
*/
static void
fluid_synth_set_basic_channel_LOCAL ( fluid_synth_t * synth , int basicchan , int mode , int val )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
int i ;
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* sets the basic channel group */
for ( i = basicchan ; i < basicchan + val ; i + + )
{
int new_mode = mode ; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */
int new_val ;
/* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */
fluid_synth_all_notes_off_LOCAL ( synth , i ) ;
if ( i = = basicchan )
{
new_mode | = FLUID_CHANNEL_BASIC ; /* First channel in the group */
new_val = val ; /* number of channels in the group */
}
else
{
new_val = 0 ; /* val is 0 for other channel than basic channel */
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
/* Channel is enabled */
new_mode | = FLUID_CHANNEL_ENABLED ;
/* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */
fluid_channel_set_basic_channel_info ( synth - > channel [ i ] , new_mode ) ;
synth - > channel [ i ] - > mode_val = new_val ;
}
}
2016-08-23 18:50:54 +02:00
/**
2018-10-18 00:41:02 +02:00
* Searchs a previous basic channel starting from chan .
*
* @ param synth the synth instance .
* @ param chan starting index of the search ( including chan ) .
* @ return index of the basic channel if found , FLUID_FAILED otherwise .
2016-08-23 18:50:54 +02:00
*/
2018-10-18 00:41:02 +02:00
static int fluid_synth_get_previous_basic_channel ( fluid_synth_t * synth , int chan )
2016-08-23 18:50:54 +02:00
{
2018-10-18 00:41:02 +02:00
for ( ; chan > = 0 ; chan - - )
{
/* searchs previous basic channel */
if ( synth - > channel [ chan ] - > mode & FLUID_CHANNEL_BASIC )
{
/* chan is the previous basic channel */
return chan ;
}
}
2016-08-23 18:50:54 +02:00
2018-10-18 00:41:02 +02:00
return FLUID_FAILED ;
2016-08-23 18:50:54 +02:00
}
2018-10-18 00:41:02 +02:00
/**
* Returns poly mono mode information of any MIDI channel .
*
* @ param synth the synth instance
* @ param chan MIDI channel number ( 0 to MIDI channel count - 1 )
* @ param basic_chan_out Buffer to store the basic channel \ a chan belongs to or # FLUID_FAILED if \ a chan is disabled .
* @ param mode_out Buffer to store the mode of \ a chan ( see # fluid_basic_channel_modes ) or # FLUID_FAILED if \ a chan is disabled .
* @ param val_out Buffer to store the total number of channels in this basic channel group or # FLUID_FAILED if \ a chan is disabled .
* @ note If any of \ a basic_chan_out , \ a mode_out , \ a val_out pointer is NULL
* the corresponding information isn ' t returned .
*
* @ return
* - # FLUID_OK on success .
* - # FLUID_FAILED
* - \ a synth is NULL .
* - \ a chan is outside MIDI channel count .
*/
int fluid_synth_get_basic_channel ( fluid_synth_t * synth , int chan ,
int * basic_chan_out ,
int * mode_out ,
int * val_out )
{
int basic_chan = FLUID_FAILED ;
int mode = FLUID_FAILED ;
int val = FLUID_FAILED ;
/* checks parameters first */
FLUID_API_ENTRY_CHAN ( FLUID_FAILED ) ;
if ( ( synth - > channel [ chan ] - > mode & FLUID_CHANNEL_ENABLED ) & &
/* chan is enabled , we search the basic channel chan belongs to */
( basic_chan = fluid_synth_get_previous_basic_channel ( synth , chan ) ) ! = FLUID_FAILED )
{
mode = synth - > channel [ chan ] - > mode & FLUID_CHANNEL_MODE_MASK ;
val = synth - > channel [ basic_chan ] - > mode_val ;
}
/* returns the informations if they are requested */
if ( basic_chan_out )
{
* basic_chan_out = basic_chan ;
}
if ( mode_out )
{
* mode_out = mode ;
}
if ( val_out )
{
* val_out = val ;
}
FLUID_API_RETURN ( FLUID_OK ) ;
}