2003-03-11 16:56:45 +00:00
|
|
|
/* FluidSynth - A Software Synthesizer
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Peter Hanappe and others.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
2007-03-04 16:45:05 +00:00
|
|
|
*
|
2003-03-11 16:56:45 +00:00
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the Free
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA
|
|
|
|
*/
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
#include <math.h>
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
#include "fluid_synth.h"
|
|
|
|
#include "fluid_sys.h"
|
|
|
|
#include "fluid_chan.h"
|
|
|
|
#include "fluid_tuning.h"
|
|
|
|
#include "fluid_settings.h"
|
|
|
|
#include "fluid_sfont.h"
|
2009-09-22 07:04:07 +00:00
|
|
|
#include "fluid_hash.h"
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
#ifdef TRAP_ON_FPE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <fenv.h>
|
|
|
|
|
|
|
|
/* seems to not be declared in fenv.h */
|
|
|
|
extern int feenableexcept (int excepts);
|
|
|
|
#endif
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* A descriptive alias for fluid_return_if_fail/fluid_return_val_if_fail */
|
|
|
|
#define fluid_synth_is_synth_thread(_s) (fluid_thread_get_id() == (_s)->synth_thread_id)
|
|
|
|
|
|
|
|
/* Macro used to check if an event should be queued or not (not in synthesis thread context?) */
|
|
|
|
#define fluid_synth_should_queue(_s) (!fluid_synth_is_synth_thread(_s))
|
|
|
|
|
|
|
|
|
2009-06-07 05:16:47 +00:00
|
|
|
static void fluid_synth_init(void);
|
2009-09-22 07:04:07 +00:00
|
|
|
static int fluid_synth_return_event_process_callback (void* data, unsigned int msec);
|
|
|
|
static fluid_event_queue_t *fluid_synth_get_event_queue (fluid_synth_t* synth);
|
|
|
|
static int fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan,
|
|
|
|
int param1, int param2);
|
|
|
|
static int fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan,
|
|
|
|
int param, float value, int absolute);
|
|
|
|
static int fluid_synth_queue_float_event (fluid_synth_t* synth, int type, float val);
|
|
|
|
static int fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val);
|
|
|
|
static void fluid_synth_thread_queue_destroy_notify (void *data);
|
|
|
|
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_damp_voices_LOCAL(fluid_synth_t* synth, int chan);
|
|
|
|
static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num,
|
|
|
|
int value);
|
|
|
|
static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan);
|
|
|
|
static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan);
|
|
|
|
static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth);
|
|
|
|
static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan,
|
|
|
|
int is_cc, int ctrl);
|
|
|
|
static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan);
|
|
|
|
static int fluid_synth_channel_pressure_LOCAL(fluid_synth_t* synth, int channum,
|
|
|
|
int val);
|
|
|
|
static int fluid_synth_pitch_bend_LOCAL(fluid_synth_t* synth, int chan, int val);
|
|
|
|
static int fluid_synth_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan,
|
|
|
|
int val);
|
|
|
|
static int fluid_synth_set_preset (fluid_synth_t *synth, int chan,
|
|
|
|
fluid_preset_t *preset);
|
|
|
|
static int fluid_synth_set_preset_LOCAL (fluid_synth_t *synth, int chan,
|
|
|
|
fluid_preset_t *preset);
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_preset_t*
|
|
|
|
fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
|
2009-09-22 07:04:07 +00:00
|
|
|
unsigned int banknum, unsigned int prognum);
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_preset_t*
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname,
|
|
|
|
unsigned int banknum, unsigned int prognum);
|
|
|
|
|
2009-06-07 05:16:47 +00:00
|
|
|
static void fluid_synth_update_presets(fluid_synth_t* synth);
|
|
|
|
static int fluid_synth_update_gain(fluid_synth_t* synth,
|
|
|
|
char* name, double value);
|
2009-09-22 07:04:07 +00:00
|
|
|
static void fluid_synth_set_gain_LOCAL(fluid_synth_t* synth, float gain);
|
2009-06-07 05:16:47 +00:00
|
|
|
static int fluid_synth_update_polyphony(fluid_synth_t* synth,
|
|
|
|
char* name, int value);
|
2009-09-22 07:04:07 +00:00
|
|
|
static int fluid_synth_set_polyphony_LOCAL(fluid_synth_t* synth, int polyphony);
|
2009-06-07 05:16:47 +00:00
|
|
|
static void init_dither(void);
|
|
|
|
static inline int roundi (float x);
|
|
|
|
static int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out);
|
2009-09-22 07:04:07 +00:00
|
|
|
static FLUID_INLINE void fluid_synth_process_event_queue_LOCAL
|
|
|
|
(fluid_synth_t *synth, fluid_event_queue_t *queue);
|
|
|
|
static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth);
|
|
|
|
static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth,
|
|
|
|
fluid_voice_t* new_voice);
|
|
|
|
static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth,
|
|
|
|
fluid_sfont_t *sfont);
|
|
|
|
static void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont);
|
2009-06-07 05:16:47 +00:00
|
|
|
static int fluid_synth_sfunload_callback(void* data, unsigned int msec);
|
2009-09-22 07:04:07 +00:00
|
|
|
static int fluid_synth_set_reverb_LOCAL(fluid_synth_t* synth, int set, double roomsize,
|
|
|
|
double damping, double width, double level);
|
|
|
|
static int fluid_synth_set_chorus_LOCAL(fluid_synth_t* synth, int set, int nr, float level,
|
|
|
|
float speed, float depth_ms, int type);
|
|
|
|
static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth,
|
|
|
|
int chan, int key);
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth,
|
|
|
|
int bank, int prog);
|
|
|
|
static fluid_tuning_t* fluid_synth_create_tuning(fluid_synth_t* synth, int bank,
|
|
|
|
int prog, char* name);
|
2009-09-22 07:04:07 +00:00
|
|
|
static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan,
|
|
|
|
int param, float value, int absolute);
|
|
|
|
static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id);
|
2009-06-07 05:16:47 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_sfloader_t* new_fluid_defsfloader(void);
|
|
|
|
|
2004-02-28 13:20:07 +00:00
|
|
|
/************************************************************************
|
|
|
|
*
|
|
|
|
* These functions were added after the v1.0 API freeze. They are not
|
|
|
|
* in synth.h. They should be added as soon as a new development
|
|
|
|
* version is started.
|
|
|
|
*
|
|
|
|
************************************************************************/
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
int fluid_synth_program_select2(fluid_synth_t* synth,
|
|
|
|
int chan,
|
|
|
|
char* sfont_name,
|
|
|
|
unsigned int bank_num,
|
2004-02-28 13:20:07 +00:00
|
|
|
unsigned int preset_num);
|
|
|
|
|
|
|
|
fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name);
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
int fluid_synth_set_gen2(fluid_synth_t* synth, int chan,
|
|
|
|
int param, float value,
|
2004-03-03 11:14:25 +00:00
|
|
|
int absolute, int normalized);
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/***************************************************************
|
|
|
|
*
|
|
|
|
* GLOBAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* has the synth module been initialized? */
|
2007-03-04 16:45:05 +00:00
|
|
|
static int fluid_synth_initialized = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
static void fluid_synth_init(void);
|
2006-11-21 20:04:31 +00:00
|
|
|
static void init_dither(void);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* default modulators
|
|
|
|
* SF2.01 page 52 ff:
|
|
|
|
*
|
|
|
|
* There is a set of predefined default modulators. They have to be
|
2007-03-04 16:45:05 +00:00
|
|
|
* explicitly overridden by the sound font in order to turn them off.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */
|
2009-01-29 23:05:59 +00:00
|
|
|
fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */
|
|
|
|
fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */
|
|
|
|
fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */
|
|
|
|
fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */
|
|
|
|
fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */
|
|
|
|
fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */
|
|
|
|
fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */
|
|
|
|
fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */
|
|
|
|
|
|
|
|
/* reverb presets */
|
|
|
|
static fluid_revmodel_presets_t revmodel_preset[] = {
|
|
|
|
/* name */ /* roomsize */ /* damp */ /* width */ /* level */
|
2007-03-04 16:45:05 +00:00
|
|
|
{ "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 },
|
2003-03-11 16:56:45 +00:00
|
|
|
{ NULL, 0.0f, 0.0f, 0.0f, 0.0f }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
*
|
|
|
|
* INITIALIZATION & UTILITIES
|
|
|
|
*/
|
|
|
|
|
2003-04-03 21:32:56 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
void fluid_synth_settings(fluid_settings_t* settings)
|
|
|
|
{
|
|
|
|
fluid_settings_register_str(settings, "synth.verbose", "no", 0, NULL, NULL);
|
|
|
|
fluid_settings_register_str(settings, "synth.dump", "no", 0, NULL, NULL);
|
|
|
|
fluid_settings_register_str(settings, "synth.reverb.active", "yes", 0, NULL, NULL);
|
|
|
|
fluid_settings_register_str(settings, "synth.chorus.active", "yes", 0, NULL, NULL);
|
|
|
|
fluid_settings_register_str(settings, "synth.ladspa.active", "no", 0, NULL, NULL);
|
2008-09-15 04:17:24 +00:00
|
|
|
fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.polyphony",
|
2003-03-11 16:56:45 +00:00
|
|
|
256, 16, 4096, 0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.midi-channels",
|
2003-03-11 16:56:45 +00:00
|
|
|
16, 16, 256, 0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_num(settings, "synth.gain",
|
|
|
|
0.2f, 0.0f, 10.0f,
|
2003-03-11 16:56:45 +00:00
|
|
|
0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.audio-channels",
|
2003-03-11 16:56:45 +00:00
|
|
|
1, 1, 256, 0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.audio-groups",
|
2003-03-11 16:56:45 +00:00
|
|
|
1, 1, 256, 0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.effects-channels",
|
2003-03-11 16:56:45 +00:00
|
|
|
2, 2, 2, 0, NULL, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_num(settings, "synth.sample-rate",
|
|
|
|
44100.0f, 22050.0f, 96000.0f,
|
2003-03-11 16:56:45 +00:00
|
|
|
0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2003-03-13 08:07:16 +00:00
|
|
|
/*
|
|
|
|
* fluid_version
|
|
|
|
*/
|
|
|
|
void fluid_version(int *major, int *minor, int *micro)
|
|
|
|
{
|
|
|
|
*major = FLUIDSYNTH_VERSION_MAJOR;
|
|
|
|
*minor = FLUIDSYNTH_VERSION_MINOR;
|
|
|
|
*micro = FLUIDSYNTH_VERSION_MICRO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_version_str
|
|
|
|
*/
|
|
|
|
char* fluid_version_str(void)
|
|
|
|
{
|
|
|
|
return FLUIDSYNTH_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/*
|
|
|
|
* void fluid_synth_init
|
|
|
|
*
|
|
|
|
* Does all the initialization for this module.
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
static void
|
2009-06-07 05:16:47 +00:00
|
|
|
fluid_synth_init(void)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_synth_initialized++;
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
#ifdef TRAP_ON_FPE
|
|
|
|
/* Turn on floating point exception traps */
|
|
|
|
feenableexcept (FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID);
|
|
|
|
#endif
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_conversion_config();
|
|
|
|
|
2007-09-02 23:23:47 +00:00
|
|
|
fluid_dsp_float_config();
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_sys_config();
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
init_dither();
|
2006-11-21 20:04:31 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2007-03-04 16:45:05 +00:00
|
|
|
| FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */
|
2003-03-11 16:56:45 +00:00
|
|
|
| FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */
|
|
|
|
| FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */
|
|
|
|
);
|
|
|
|
fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */
|
|
|
|
fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
|
|
|
|
fluid_mod_set_amount(&default_vel2att_mod, 960.0); /* Modulation amount: 960 */
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-01-29 23:05:59 +00:00
|
|
|
/* 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 */
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_MOD_GC /* CC=0 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
2009-01-29 23:05:59 +00:00
|
|
|
| FLUID_MOD_NEGATIVE /* D=1 */
|
2003-03-11 16:56:45 +00:00
|
|
|
);
|
2009-01-29 23:05:59 +00:00
|
|
|
fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_MOD_GC /* CC=0 */
|
|
|
|
| FLUID_MOD_SWITCH /* type=3 */
|
2007-03-04 16:45:05 +00:00
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
2009-01-29 23:05:59 +00:00
|
|
|
// do not remove | FLUID_MOD_NEGATIVE /* D=1 */
|
|
|
|
| FLUID_MOD_POSITIVE /* D=0 */
|
2003-03-11 16:56:45 +00:00
|
|
|
);
|
2009-01-29 23:05:59 +00:00
|
|
|
fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */
|
|
|
|
fluid_mod_set_amount(&default_vel2filter_mod, -2400);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */
|
|
|
|
fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */
|
|
|
|
FLUID_MOD_GC /* CC=0 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
|
|
|
| FLUID_MOD_POSITIVE /* D=0 */
|
|
|
|
);
|
|
|
|
fluid_mod_set_source2(&default_at2viblfo_mod, 0,0); /* no second source */
|
|
|
|
fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */
|
|
|
|
fluid_mod_set_amount(&default_at2viblfo_mod, 50);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */
|
|
|
|
fluid_mod_set_source1(&default_mod2viblfo_mod, 1, /* Index=1 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
|
|
|
| FLUID_MOD_POSITIVE /* D=0 */
|
|
|
|
);
|
|
|
|
fluid_mod_set_source2(&default_mod2viblfo_mod, 0,0); /* no second source */
|
|
|
|
fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */
|
|
|
|
fluid_mod_set_amount(&default_mod2viblfo_mod, 50);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_source1(&default_att_mod, 7, /* index=7 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
|
|
|
| FLUID_MOD_CONCAVE /* type=1 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
|
|
|
| FLUID_MOD_NEGATIVE /* D=1 */
|
|
|
|
);
|
|
|
|
fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */
|
|
|
|
fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
|
|
|
|
fluid_mod_set_amount(&default_att_mod, 960.0); /* Amount: 960 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */
|
|
|
|
fluid_mod_set_source1(&default_pan_mod, 10, /* index=10 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
2007-03-04 16:45:05 +00:00
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
|
|
|
| FLUID_MOD_BIPOLAR /* P=1 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| 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. */
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_amount(&default_pan_mod, 500.0);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_source1(&default_expr_mod, 11, /* index=11 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
|
|
|
| FLUID_MOD_CONCAVE /* type=1 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| FLUID_MOD_UNIPOLAR /* P=0 */
|
|
|
|
| FLUID_MOD_NEGATIVE /* D=1 */
|
|
|
|
);
|
|
|
|
fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */
|
|
|
|
fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
|
|
|
|
fluid_mod_set_amount(&default_expr_mod, 960.0); /* Amount: 960 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_source1(&default_reverb_mod, 91, /* index=91 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| 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') */
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-08 09:42:17 +00:00
|
|
|
/* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_source1(&default_chorus_mod, 93, /* index=93 */
|
|
|
|
FLUID_MOD_CC /* CC=1 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| 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 ... */
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_MOD_GC /* CC =0 */
|
|
|
|
| FLUID_MOD_LINEAR /* type=0 */
|
2007-03-04 16:45:05 +00:00
|
|
|
| FLUID_MOD_BIPOLAR /* P=1 */
|
2003-03-11 16:56:45 +00:00
|
|
|
| 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 */
|
2007-03-04 16:45:05 +00:00
|
|
|
);
|
2003-03-11 16:56:45 +00:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-25 15:17:15 +00:00
|
|
|
/***************************************************************
|
|
|
|
* FLUID SAMPLE TIMERS
|
|
|
|
* Timers that use written audio data as timing reference
|
|
|
|
*/
|
|
|
|
struct _fluid_sample_timer_t
|
|
|
|
{
|
|
|
|
fluid_sample_timer_t* next; /* Single linked list of timers */
|
|
|
|
unsigned long starttick;
|
|
|
|
fluid_timer_callback_t callback;
|
|
|
|
void* data;
|
|
|
|
int isfinished;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fluid_sample_timer_process - called when synth->ticks is updated
|
|
|
|
*/
|
|
|
|
void fluid_sample_timer_process(fluid_synth_t* synth)
|
|
|
|
{
|
|
|
|
fluid_sample_timer_t* st;
|
|
|
|
long msec;
|
|
|
|
int cont;
|
|
|
|
|
|
|
|
for (st=synth->sample_timers; st; st=st->next) {
|
|
|
|
if (st->isfinished) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
msec = (long) (1000.0*((double) (synth->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 = synth->ticks;
|
|
|
|
result->isfinished = 0;
|
|
|
|
result->data = data;
|
|
|
|
result->callback = callback;
|
|
|
|
result->next = synth->sample_timers;
|
|
|
|
synth->sample_timers = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer)
|
|
|
|
{
|
|
|
|
fluid_sample_timer_t** ptr = &synth->sample_timers;
|
|
|
|
while (*ptr) {
|
|
|
|
if (*ptr == timer) {
|
|
|
|
*ptr = timer->next;
|
|
|
|
FLUID_FREE(timer);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
ptr = &((*ptr)->next);
|
|
|
|
}
|
|
|
|
FLUID_LOG(FLUID_ERR,"delete_fluid_sample_timer failed, no timer found");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/***************************************************************
|
|
|
|
*
|
|
|
|
* FLUID SYNTH
|
|
|
|
*/
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Create new FluidSynth instance.
|
|
|
|
* @param settings Configuration parameters to use (used directly).
|
|
|
|
* @return New FluidSynth instance or NULL on error
|
|
|
|
*
|
|
|
|
* NOTE: The settings parameter is used directly and should not be modified
|
|
|
|
* or freed independently.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_synth_t*
|
2003-03-11 16:56:45 +00:00
|
|
|
new_fluid_synth(fluid_settings_t *settings)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
fluid_synth_t* synth;
|
|
|
|
fluid_sfloader_t* loader;
|
|
|
|
|
|
|
|
/* initialize all the conversion tables and other stuff */
|
|
|
|
if (fluid_synth_initialized == 0) {
|
|
|
|
fluid_synth_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate a new synthesizer object */
|
|
|
|
synth = FLUID_NEW(fluid_synth_t);
|
|
|
|
if (synth == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_init(synth->mutex);
|
|
|
|
fluid_private_init(synth->thread_queues);
|
|
|
|
|
|
|
|
synth->return_queue = fluid_event_queue_new (FLUID_MAX_RETURN_EVENTS);
|
|
|
|
|
|
|
|
if (synth->return_queue == NULL) {
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
synth->settings = settings;
|
|
|
|
|
|
|
|
synth->with_reverb = fluid_settings_str_equal(settings, "synth.reverb.active", "yes");
|
|
|
|
synth->with_chorus = fluid_settings_str_equal(settings, "synth.chorus.active", "yes");
|
|
|
|
synth->verbose = fluid_settings_str_equal(settings, "synth.verbose", "yes");
|
|
|
|
synth->dump = fluid_settings_str_equal(settings, "synth.dump", "yes");
|
|
|
|
|
|
|
|
fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony);
|
|
|
|
fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate);
|
|
|
|
fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels);
|
|
|
|
fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);
|
|
|
|
fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);
|
|
|
|
fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels);
|
|
|
|
fluid_settings_getnum(settings, "synth.gain", &synth->gain);
|
|
|
|
|
2003-04-03 21:32:56 +00:00
|
|
|
/* register the callbacks */
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_num(settings, "synth.gain",
|
|
|
|
0.2f, 0.0f, 10.0f, 0,
|
2003-04-03 21:32:56 +00:00
|
|
|
(fluid_num_update_t) fluid_synth_update_gain, synth);
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_settings_register_int(settings, "synth.polyphony",
|
2005-06-08 03:51:13 +00:00
|
|
|
synth->polyphony, 16, 4096, 0,
|
|
|
|
(fluid_int_update_t) fluid_synth_update_polyphony,
|
|
|
|
synth);
|
2003-04-03 21:32:56 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* do some basic sanity checking on the settings */
|
2004-05-05 20:27:22 +00:00
|
|
|
|
|
|
|
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);
|
2007-03-04 16:45:05 +00:00
|
|
|
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.");
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (synth->audio_channels < 1) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. "
|
|
|
|
"Changing this setting to 1.");
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->audio_channels = 1;
|
|
|
|
} else if (synth->audio_channels > 128) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). "
|
|
|
|
"Limiting this setting to 128.", synth->audio_channels);
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->audio_channels = 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (synth->audio_groups < 1) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. "
|
|
|
|
"Changing this setting to 1.");
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->audio_groups = 1;
|
|
|
|
} else if (synth->audio_groups > 128) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). "
|
|
|
|
"Limiting this setting to 128.", synth->audio_groups);
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->audio_groups = 128;
|
|
|
|
}
|
|
|
|
|
2009-07-27 10:51:05 +00:00
|
|
|
if (synth->effects_channels < 2) {
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)."
|
|
|
|
"Setting effects channels to 2.", synth->effects_channels);
|
|
|
|
synth->effects_channels = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The number of buffers is determined by the higher number of nr
|
|
|
|
* groups / nr audio channels. If LADSPA is unused, they should be
|
|
|
|
* the same. */
|
|
|
|
synth->nbuf = synth->audio_channels;
|
|
|
|
if (synth->audio_groups > synth->nbuf) {
|
|
|
|
synth->nbuf = synth->audio_groups;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LADSPA
|
|
|
|
/* Create and initialize the Fx unit.*/
|
|
|
|
synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth);
|
|
|
|
#endif
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* as soon as the synth is created it starts playing. */
|
|
|
|
synth->state = FLUID_SYNTH_PLAYING;
|
2009-09-22 07:04:07 +00:00
|
|
|
synth->sfont_info = NULL;
|
|
|
|
synth->sfont_hash = new_fluid_hashtable (NULL, NULL);
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->noteid = 0;
|
|
|
|
synth->ticks = 0;
|
|
|
|
synth->tuning = NULL;
|
|
|
|
|
|
|
|
/* allocate and add the default sfont loader */
|
|
|
|
loader = new_fluid_defsfloader();
|
|
|
|
|
|
|
|
if (loader == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader");
|
2003-03-11 16:56:45 +00:00
|
|
|
} 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) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
for (i = 0; i < synth->midi_channels; i++) {
|
|
|
|
synth->channel[i] = new_fluid_channel(synth, i);
|
|
|
|
if (synth->channel[i] == NULL) {
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate all synthesis processes */
|
|
|
|
synth->nvoice = synth->polyphony;
|
|
|
|
synth->voice = FLUID_ARRAY(fluid_voice_t*, synth->nvoice);
|
|
|
|
if (synth->voice == NULL) {
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
for (i = 0; i < synth->nvoice; i++) {
|
|
|
|
synth->voice[i] = new_fluid_voice(synth->sample_rate);
|
|
|
|
if (synth->voice[i] == NULL) {
|
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
/* Allocate the sample buffers */
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->left_buf = NULL;
|
|
|
|
synth->right_buf = NULL;
|
|
|
|
synth->fx_left_buf = NULL;
|
|
|
|
synth->fx_right_buf = NULL;
|
|
|
|
|
|
|
|
/* Left and right audio buffers */
|
|
|
|
|
|
|
|
synth->left_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf);
|
|
|
|
synth->right_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf);
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if ((synth->left_buf == NULL) || (synth->right_buf == NULL)) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
FLUID_MEMSET(synth->left_buf, 0, synth->nbuf * sizeof(fluid_real_t*));
|
|
|
|
FLUID_MEMSET(synth->right_buf, 0, synth->nbuf * sizeof(fluid_real_t*));
|
|
|
|
|
|
|
|
for (i = 0; i < synth->nbuf; i++) {
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
synth->left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
|
|
|
|
synth->right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if ((synth->left_buf[i] == NULL) || (synth->right_buf[i] == NULL)) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Effects audio buffers */
|
|
|
|
|
|
|
|
synth->fx_left_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels);
|
|
|
|
synth->fx_right_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels);
|
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if ((synth->fx_left_buf == NULL) || (synth->fx_right_buf == NULL)) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-07-27 10:51:05 +00:00
|
|
|
FLUID_MEMSET(synth->fx_left_buf, 0, synth->effects_channels * sizeof(fluid_real_t*));
|
|
|
|
FLUID_MEMSET(synth->fx_right_buf, 0, synth->effects_channels * sizeof(fluid_real_t*));
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
for (i = 0; i < synth->effects_channels; i++) {
|
2007-11-11 20:52:56 +00:00
|
|
|
synth->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
|
|
|
|
synth->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if ((synth->fx_left_buf[i] == NULL) || (synth->fx_right_buf[i] == NULL)) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
synth->cur = FLUID_BUFSIZE;
|
2006-11-21 20:04:31 +00:00
|
|
|
synth->dither_index = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* allocate the reverb module */
|
|
|
|
synth->reverb = new_fluid_revmodel();
|
|
|
|
if (synth->reverb == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE;
|
|
|
|
synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP;
|
|
|
|
synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH;
|
|
|
|
synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL;
|
|
|
|
|
|
|
|
fluid_revmodel_set (synth->reverb, FLUID_REVMODEL_SET_ALL,
|
|
|
|
synth->reverb_roomsize, synth->reverb_damping,
|
|
|
|
synth->reverb_width, synth->reverb_level);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* allocate the chorus module */
|
|
|
|
synth->chorus = new_fluid_chorus(synth->sample_rate);
|
|
|
|
if (synth->chorus == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
2003-03-11 16:56:45 +00:00
|
|
|
goto error_recovery;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME */
|
|
|
|
synth->start = fluid_curtime();
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Spawn low priority periodic timer thread to process synth thread return events */
|
|
|
|
synth->return_queue_timer = new_fluid_timer (200, fluid_synth_return_event_process_callback,
|
|
|
|
synth, TRUE, TRUE, FALSE);
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth;
|
|
|
|
|
|
|
|
error_recovery:
|
|
|
|
delete_fluid_synth(synth);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Callback to process synthesis thread return events */
|
|
|
|
static int
|
|
|
|
fluid_synth_return_event_process_callback (void* data, unsigned int msec)
|
|
|
|
{
|
|
|
|
fluid_synth_t *synth = data;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
fluid_preset_t *preset;
|
|
|
|
|
|
|
|
while ((event = fluid_event_queue_get_outptr (synth->return_queue)))
|
|
|
|
{
|
|
|
|
switch (event->type)
|
|
|
|
{
|
|
|
|
case FLUID_EVENT_QUEUE_ELEM_GAIN: /* Sync gain variable */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock gain variable */
|
|
|
|
synth->gain = event->dval;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
break;
|
|
|
|
case FLUID_EVENT_QUEUE_ELEM_REVERB: /* Sync reverb shadow variables */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock reverb shadow variables */
|
|
|
|
synth->reverb_roomsize = event->reverb.roomsize;
|
|
|
|
synth->reverb_damping = event->reverb.damping;
|
|
|
|
synth->reverb_width = event->reverb.width;
|
|
|
|
synth->reverb_level = event->reverb.level;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
break;
|
|
|
|
case FLUID_EVENT_QUEUE_ELEM_CHORUS: /* Sync chorus shadow variables */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus shadow variables */
|
|
|
|
synth->chorus_nr = event->chorus.nr;
|
|
|
|
synth->chorus_level = event->chorus.level;
|
|
|
|
synth->chorus_speed = event->chorus.speed;
|
|
|
|
synth->chorus_depth = event->chorus.depth;
|
|
|
|
synth->chorus_type = event->chorus.type;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
break;
|
|
|
|
case FLUID_EVENT_QUEUE_ELEM_FREE_PRESET: /* Preset free event */
|
|
|
|
preset = (fluid_preset_t *)(event->pval);
|
|
|
|
fluid_synth_sfont_unref (synth, preset->sfont); /* -- unref preset's SoundFont */
|
|
|
|
delete_fluid_preset (preset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_event_queue_next_outptr (synth->return_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE; /* Keep this timer callback active */
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a FluidSynth instance.
|
|
|
|
* @param synth FluidSynth instance to delete
|
|
|
|
* @return FLUID_OK
|
|
|
|
*
|
|
|
|
* NOTE: Other users of a synthesizer instance, such as audio and MIDI drivers,
|
|
|
|
* should be deleted prior to freeing the FluidSynth instance.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
|
|
|
delete_fluid_synth(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int i, k;
|
|
|
|
fluid_list_t *list;
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t* sfont_info;
|
|
|
|
fluid_event_queue_t* queue;
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_sfloader_t* loader;
|
|
|
|
|
|
|
|
if (synth == NULL) {
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_profiling_print();
|
|
|
|
|
|
|
|
synth->state = FLUID_SYNTH_STOPPED;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Stop return event queue thread, and process remaining events */
|
|
|
|
if (synth->return_queue_timer)
|
|
|
|
delete_fluid_timer (synth->return_queue_timer);
|
|
|
|
|
|
|
|
if (synth->return_queue) {
|
|
|
|
fluid_synth_return_event_process_callback(synth, 0);
|
|
|
|
fluid_event_queue_free(synth->return_queue);
|
|
|
|
}
|
|
|
|
|
2006-12-10 16:02:04 +00:00
|
|
|
/* turn off all voices, needed to unload SoundFont data */
|
|
|
|
if (synth->voice != NULL) {
|
|
|
|
for (i = 0; i < synth->nvoice; i++) {
|
|
|
|
if (synth->voice[i] && fluid_voice_is_playing (synth->voice[i]))
|
|
|
|
fluid_voice_off (synth->voice[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* delete all the SoundFonts */
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
|
|
|
delete_fluid_sfont (sfont_info->sfont);
|
|
|
|
FLUID_FREE (sfont_info);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
delete_fluid_list(synth->sfont_info);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Delete the SoundFont info hash */
|
|
|
|
if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* delete all the SoundFont loaders */
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
for (list = synth->loaders; list; list = fluid_list_next(list)) {
|
|
|
|
loader = (fluid_sfloader_t*) fluid_list_get(list);
|
|
|
|
fluid_sfloader_delete(loader);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete_fluid_list(synth->loaders);
|
|
|
|
|
|
|
|
|
|
|
|
if (synth->channel != NULL) {
|
|
|
|
for (i = 0; i < synth->midi_channels; i++) {
|
|
|
|
if (synth->channel[i] != NULL) {
|
|
|
|
delete_fluid_channel(synth->channel[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(synth->channel);
|
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (synth->voice != NULL) {
|
|
|
|
for (i = 0; i < synth->nvoice; i++) {
|
|
|
|
if (synth->voice[i] != NULL) {
|
|
|
|
delete_fluid_voice(synth->voice[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(synth->voice);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free all the sample buffers */
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->left_buf != NULL) {
|
2003-03-11 16:56:45 +00:00
|
|
|
for (i = 0; i < synth->nbuf; i++) {
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->left_buf[i] != NULL) {
|
|
|
|
FLUID_FREE(synth->left_buf[i]);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-11 20:52:56 +00:00
|
|
|
FLUID_FREE(synth->left_buf);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->right_buf != NULL) {
|
2003-03-11 16:56:45 +00:00
|
|
|
for (i = 0; i < synth->nbuf; i++) {
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->right_buf[i] != NULL) {
|
|
|
|
FLUID_FREE(synth->right_buf[i]);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-11 20:52:56 +00:00
|
|
|
FLUID_FREE(synth->right_buf);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->fx_left_buf != NULL) {
|
2009-07-27 10:51:05 +00:00
|
|
|
for (i = 0; i < synth->effects_channels; i++) {
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->fx_left_buf[i] != NULL) {
|
|
|
|
FLUID_FREE(synth->fx_left_buf[i]);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-11 20:52:56 +00:00
|
|
|
FLUID_FREE(synth->fx_left_buf);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2006-11-23 18:49:36 +00:00
|
|
|
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->fx_right_buf != NULL) {
|
2009-07-27 10:51:05 +00:00
|
|
|
for (i = 0; i < synth->effects_channels; i++) {
|
2007-11-11 20:52:56 +00:00
|
|
|
if (synth->fx_right_buf[i] != NULL) {
|
|
|
|
FLUID_FREE(synth->fx_right_buf[i]);
|
2006-11-23 18:49:36 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-11 20:52:56 +00:00
|
|
|
FLUID_FREE(synth->fx_right_buf);
|
2006-11-23 18:49:36 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* release the reverb module */
|
|
|
|
if (synth->reverb != NULL) {
|
|
|
|
delete_fluid_revmodel(synth->reverb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* release the chorus module */
|
|
|
|
if (synth->chorus != NULL) {
|
|
|
|
delete_fluid_chorus(synth->chorus);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free the tunings, if any */
|
|
|
|
if (synth->tuning != NULL) {
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
|
|
if (synth->tuning[i] != NULL) {
|
|
|
|
for (k = 0; k < 128; k++) {
|
|
|
|
if (synth->tuning[i][k] != NULL) {
|
2009-09-22 07:04:07 +00:00
|
|
|
delete_fluid_tuning(synth->tuning[i][k]);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(synth->tuning[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FLUID_FREE(synth->tuning);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LADSPA
|
|
|
|
/* Release the LADSPA Fx unit */
|
|
|
|
fluid_LADSPA_shutdown(synth->LADSPA_FxUnit);
|
|
|
|
FLUID_FREE(synth->LADSPA_FxUnit);
|
|
|
|
#endif
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* free any queues in pool */
|
|
|
|
for (list = synth->queue_pool; list; list = list->next) {
|
|
|
|
queue = (fluid_event_queue_t *)(list->data);
|
|
|
|
/* Prevent double-free later */
|
|
|
|
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
|
|
|
|
if (synth->queues[i] == queue) synth->queues[i] = NULL;
|
|
|
|
fluid_event_queue_free (queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free remaining event queues, if any */
|
|
|
|
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
|
|
|
|
if (synth->queues[i]) fluid_event_queue_free (synth->queues[i]);
|
|
|
|
|
|
|
|
|
|
|
|
delete_fluid_list (synth->queue_pool);
|
|
|
|
|
|
|
|
fluid_mutex_destroy(synth->mutex);
|
2004-05-05 20:27:22 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_FREE(synth);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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.
|
|
|
|
*/
|
|
|
|
/* FIXME - The error messages are not thread-safe, yet. They are still stored
|
|
|
|
* in a global message buffer (see fluid_sys.c). */
|
2007-03-04 16:45:05 +00:00
|
|
|
char*
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_error(fluid_synth_t* synth)
|
|
|
|
{
|
|
|
|
return fluid_error();
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Get event queue for the current thread (create if necessary) */
|
|
|
|
static fluid_event_queue_t *
|
|
|
|
fluid_synth_get_event_queue (fluid_synth_t* synth)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
queue = fluid_private_get (synth->thread_queues); /* Get event queue for this thread */
|
|
|
|
|
|
|
|
if (!queue) /* This thread has no queue yet? */
|
|
|
|
{
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock queue_pool */
|
|
|
|
|
|
|
|
/* Use an unclaimed queue, if any (it will already be in synth->queues[] in that case) */
|
|
|
|
if (synth->queue_pool)
|
|
|
|
{
|
|
|
|
fluid_list_t *p;
|
|
|
|
|
|
|
|
queue = synth->queue_pool->data;
|
|
|
|
|
|
|
|
/* Remove from queue_pool list */
|
|
|
|
p = synth->queue_pool;
|
|
|
|
synth->queue_pool = fluid_list_remove_link (p, p);
|
|
|
|
delete1_fluid_list (p);
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock queue_pool */
|
|
|
|
|
|
|
|
if (!queue) /* Create event queue, if one wasn't re-claimed */
|
|
|
|
{
|
|
|
|
queue = fluid_event_queue_new (FLUID_MAX_EVENTS_PER_BUFSIZE);
|
|
|
|
if (!queue) return NULL; /* Error has already been logged */
|
|
|
|
|
|
|
|
queue->synth = synth;
|
|
|
|
|
|
|
|
/* Atomicly and in a lock free fashion, put queue pointer in queues[] array */
|
|
|
|
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
|
|
|
|
{
|
|
|
|
if (!fluid_atomic_pointer_get (&synth->queues[i]))
|
|
|
|
{
|
|
|
|
if (fluid_atomic_pointer_compare_and_exchange ((void **)&synth->queues[i],
|
|
|
|
NULL, (void *)queue))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == FLUID_MAX_EVENT_QUEUES)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Maximum thread event queues exceeded");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_private_set (synth->thread_queues, queue, fluid_synth_thread_queue_destroy_notify);
|
|
|
|
}
|
|
|
|
|
|
|
|
return queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queues a MIDI event to the FluidSynth synthesis thread.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param type MIDI event type (#fluid_midi_event_type)
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param param1 MIDI event first parameter (depends on type)
|
|
|
|
* @param param2 MIDI event second parameter (depends on type)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
static int
|
|
|
|
fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan,
|
|
|
|
int param1, int param2)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
2004-05-05 20:27:22 +00:00
|
|
|
return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_MIDI;
|
|
|
|
event->midi.type = type;
|
|
|
|
event->midi.channel = chan;
|
|
|
|
event->midi.param1 = param1;
|
|
|
|
event->midi.param2 = param2;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queues a generator assignment event to the FluidSynth synthesis thread.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param param Generator ID (#fluid_gen_type)
|
|
|
|
* @param value Value to assign to generator
|
|
|
|
* @param absolute TRUE if value is an absolute assignment, FALSE for relative
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan,
|
|
|
|
int param, float value, int absolute)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_GEN;
|
|
|
|
event->gen.channel = chan;
|
|
|
|
event->gen.param = param;
|
|
|
|
event->gen.value = value;
|
|
|
|
event->gen.absolute = absolute;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queues an event with a floating point value payload.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param type Event type (#fluid_event_queue_elem)
|
|
|
|
* @param val Event value
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fluid_synth_queue_float_event (fluid_synth_t* synth, int type, float val)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = type;
|
|
|
|
event->dval = val;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queues an event with an integer value payload.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param type Event type (#fluid_event_queue_elem)
|
|
|
|
* @param val Event value
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
event->type = type;
|
|
|
|
event->ival = val;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gets called when a thread ends, which has been assigned a queue */
|
|
|
|
static void
|
|
|
|
fluid_synth_thread_queue_destroy_notify (void *data)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue = data;
|
|
|
|
fluid_synth_t *synth = queue->synth;
|
|
|
|
|
|
|
|
/* Queues are not freed (can't be thread safe without locking in synth thread),
|
|
|
|
* added to pool for potential future use */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock queue_pool */
|
|
|
|
synth->queue_pool = fluid_list_prepend (synth->queue_pool, queue);
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock queue_pool */
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
|
|
|
|
{
|
|
|
|
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 (key >= 0 && key <= 127, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, NOTE_ON, chan, key, vel);
|
|
|
|
else return fluid_synth_noteon_LOCAL (synth, chan, key, vel);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local synthesis thread variant of fluid_synth_noteon */
|
|
|
|
static int
|
|
|
|
fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel)
|
|
|
|
{
|
|
|
|
fluid_channel_t* channel;
|
|
|
|
|
|
|
|
/* notes with velocity zero go to noteoff */
|
|
|
|
if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
channel = synth->channel[chan];
|
|
|
|
|
|
|
|
/* make sure this channel has a preset */
|
|
|
|
if (channel->preset == NULL) {
|
|
|
|
if (synth->verbose) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s",
|
|
|
|
chan, key, vel, 0,
|
|
|
|
(float) synth->ticks / 44100.0f,
|
2003-03-11 16:56:45 +00:00
|
|
|
(fluid_curtime() - synth->start) / 1000.0f,
|
|
|
|
0.0f, 0, "channel has no preset");
|
|
|
|
}
|
2004-05-05 20:27:22 +00:00
|
|
|
return FLUID_FAILED;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2004-08-06 09:56:24 +00:00
|
|
|
/* If there is another voice process on the same channel and key,
|
|
|
|
advance it to the release phase. */
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key);
|
|
|
|
|
|
|
|
synth->storeid = synth->noteid++;
|
2004-08-06 09:56:24 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Send a note-off event to a FluidSynth object.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param key MIDI note number (0-127)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no
|
|
|
|
* voices matched the note off event)
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key)
|
2009-09-22 07:04:07 +00:00
|
|
|
{
|
|
|
|
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 (key >= 0 && key <= 127, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, NOTE_OFF, chan, key, 0);
|
|
|
|
else return fluid_synth_noteoff_LOCAL (synth, chan, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local synthesis thread variant of fluid_synth_noteoff */
|
|
|
|
static int
|
|
|
|
fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
fluid_voice_t* voice;
|
|
|
|
int status = FLUID_FAILED;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
|
|
|
if (_ON(voice) && (voice->chan == chan) && (voice->key == key)) {
|
|
|
|
if (synth->verbose) {
|
|
|
|
int used_voices = 0;
|
|
|
|
int k;
|
2005-06-08 03:51:13 +00:00
|
|
|
for (k = 0; k < synth->polyphony; k++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
if (!_AVAILABLE(synth->voice[k])) {
|
|
|
|
used_voices++;
|
|
|
|
}
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",
|
|
|
|
voice->chan, voice->key, 0, voice->id,
|
2004-03-29 10:05:18 +00:00
|
|
|
(float) (voice->start_time + voice->ticks) / 44100.0f,
|
2003-03-11 16:56:45 +00:00
|
|
|
(fluid_curtime() - synth->start) / 1000.0f,
|
|
|
|
(float) voice->ticks / 44100.0f,
|
|
|
|
used_voices);
|
|
|
|
} /* if verbose */
|
2009-09-22 07:04:07 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_noteoff(voice);
|
|
|
|
status = FLUID_OK;
|
|
|
|
} /* if voice on */
|
|
|
|
} /* for all voices */
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Damp all voices on a channel (turn notes off) */
|
|
|
|
static int
|
|
|
|
fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if ((voice->chan == chan) && _SUSTAINED(voice))
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_noteoff(voice);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED);
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Process bank MSB/LSB events immediately to prevent out of order issues with program change */
|
|
|
|
if (fluid_synth_should_queue (synth) && num != BANK_SELECT_MSB && num != BANK_SELECT_LSB)
|
|
|
|
return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan, num, val);
|
|
|
|
else return fluid_synth_cc_LOCAL (synth, chan, num, val);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local synthesis thread variant of MIDI CC set function.
|
|
|
|
* NOTE: Gets called out of synthesis context for BANK_SELECT_MSB and
|
|
|
|
* BANK_SELECT_LSB events, since they should be processed immediately. */
|
|
|
|
static int
|
|
|
|
fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num, int value)
|
|
|
|
{
|
|
|
|
fluid_channel_t* chan = synth->channel[channum];
|
|
|
|
int banknum, nrpn_select;
|
|
|
|
|
|
|
|
if (synth->verbose)
|
|
|
|
FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", channum, num, value);
|
|
|
|
|
|
|
|
fluid_channel_set_cc (chan, num, value);
|
|
|
|
|
|
|
|
switch (num) {
|
|
|
|
case SUSTAIN_SWITCH:
|
|
|
|
if (value < 64) fluid_synth_damp_voices_LOCAL (synth, channum);
|
|
|
|
break;
|
|
|
|
case BANK_SELECT_MSB:
|
|
|
|
fluid_channel_set_bank_msb (chan, value & 0x7F);
|
|
|
|
break;
|
|
|
|
case BANK_SELECT_LSB:
|
|
|
|
fluid_channel_set_bank_lsb (chan, value & 0x7F);
|
|
|
|
break;
|
|
|
|
case ALL_NOTES_OFF:
|
|
|
|
fluid_synth_all_notes_off_LOCAL (synth, channum);
|
|
|
|
break;
|
|
|
|
case ALL_SOUND_OFF:
|
|
|
|
fluid_synth_all_sounds_off_LOCAL (synth, channum);
|
|
|
|
break;
|
|
|
|
case ALL_CTRL_OFF:
|
|
|
|
fluid_channel_init_ctrl (chan, 1);
|
|
|
|
fluid_synth_modulate_voices_all_LOCAL (synth, channum);
|
|
|
|
break;
|
|
|
|
case DATA_ENTRY_MSB:
|
|
|
|
{
|
|
|
|
int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB);
|
|
|
|
|
|
|
|
if (fluid_atomic_int_get (&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 = fluid_atomic_int_get (&chan->nrpn_select);
|
|
|
|
|
|
|
|
if (nrpn_select < GEN_LAST)
|
|
|
|
{
|
|
|
|
float val = fluid_gen_scale_nrpn (nrpn_select, data);
|
|
|
|
fluid_synth_set_gen_LOCAL (synth, channum, nrpn_select, val, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_atomic_int_set (&chan->nrpn_select, 0); /* Reset to 0 */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */
|
|
|
|
{
|
|
|
|
switch (chan->cc[RPN_LSB])
|
|
|
|
{
|
|
|
|
case RPN_PITCH_BEND_RANGE:
|
|
|
|
fluid_synth_pitch_wheel_sens_LOCAL (synth, channum, value); /* Set bend range in semitones */
|
|
|
|
/* FIXME - Handle LSB? (Fine bend range in cents) */
|
|
|
|
break;
|
|
|
|
case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over 1 semitone (+/- 50 cents, 8192 = center) */
|
|
|
|
fluid_synth_set_gen_LOCAL (synth, channum, GEN_FINETUNE,
|
|
|
|
(data - 8192) / 8192.0 * 50.0, FALSE);
|
|
|
|
break;
|
|
|
|
case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */
|
|
|
|
fluid_synth_set_gen_LOCAL (synth, channum, GEN_COARSETUNE,
|
|
|
|
value - 64, FALSE);
|
|
|
|
break;
|
|
|
|
case RPN_TUNING_PROGRAM_CHANGE:
|
|
|
|
break;
|
|
|
|
case RPN_TUNING_BANK_SELECT:
|
|
|
|
break;
|
|
|
|
case RPN_MODULATION_DEPTH_RANGE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NRPN_MSB:
|
|
|
|
fluid_channel_set_cc (chan, NRPN_LSB, 0);
|
|
|
|
fluid_atomic_int_set (&chan->nrpn_select, 0);
|
|
|
|
fluid_atomic_int_set (&chan->nrpn_active, 1);
|
|
|
|
break;
|
|
|
|
case NRPN_LSB:
|
|
|
|
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
|
|
|
|
if (fluid_channel_get_cc (chan, NRPN_MSB) == 120) {
|
|
|
|
if (value == 100) {
|
|
|
|
fluid_atomic_int_add (&chan->nrpn_select, 100);
|
|
|
|
} else if (value == 101) {
|
|
|
|
fluid_atomic_int_add (&chan->nrpn_select, 1000);
|
|
|
|
} else if (value == 102) {
|
|
|
|
fluid_atomic_int_add (&chan->nrpn_select, 10000);
|
|
|
|
} else if (value < 100) {
|
|
|
|
fluid_atomic_int_add (&chan->nrpn_select, value);
|
|
|
|
}
|
|
|
|
}
|
2003-04-03 21:32:56 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_atomic_int_set (&chan->nrpn_active, 1);
|
|
|
|
break;
|
|
|
|
case RPN_MSB:
|
|
|
|
case RPN_LSB:
|
|
|
|
fluid_atomic_int_set (&chan->nrpn_active, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num);
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (num >= 0 && num < 128, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (pval != NULL, FLUID_FAILED);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
*pval = fluid_channel_get_cc (synth->channel[chan], num);
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_all_notes_off(fluid_synth_t* synth, int chan)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan,
|
|
|
|
ALL_NOTES_OFF, 0);
|
|
|
|
else return fluid_synth_all_notes_off_LOCAL (synth, chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local synthesis thread variant of all notes off */
|
|
|
|
static int
|
|
|
|
fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan)
|
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (_PLAYING(voice) && (voice->chan == chan))
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_noteoff(voice);
|
|
|
|
}
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan,
|
|
|
|
ALL_SOUND_OFF, 0);
|
|
|
|
else return fluid_synth_all_sounds_off_LOCAL (synth, chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local synthesis thread variant of all sounds off */
|
|
|
|
static int
|
|
|
|
fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan)
|
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (_PLAYING(voice) && (voice->chan == chan))
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_off(voice);
|
|
|
|
}
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Send MIDI system reset command (big red 'panic' button), turns off notes and
|
|
|
|
* resets controllers.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_system_reset(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, MIDI_SYSTEM_RESET, 0, 0, 0);
|
|
|
|
else return fluid_synth_system_reset_LOCAL (synth);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local variant of the system reset command */
|
|
|
|
static int
|
|
|
|
fluid_synth_system_reset_LOCAL(fluid_synth_t* synth)
|
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (_PLAYING(voice))
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_off(voice);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (i = 0; i < synth->midi_channels; i++)
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_channel_reset(synth->channel[i]);
|
|
|
|
|
|
|
|
fluid_chorus_reset(synth->chorus);
|
|
|
|
fluid_revmodel_reset(synth->reverb);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
static int
|
|
|
|
fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (voice->chan == chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_modulate(voice, is_cc, ctrl);
|
|
|
|
}
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @param is_cc Boolean value indicating if ctrl is a CC controller or not
|
|
|
|
* @param ctrl MIDI controller value
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (voice->chan == chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_modulate_all(voice);
|
|
|
|
}
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-01-22 16:47:21 +00:00
|
|
|
/**
|
|
|
|
* Set the MIDI channel pressure controller value.
|
|
|
|
* @param synth FluidSynth instance
|
2009-09-22 07:04:07 +00:00
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param val MIDI channel pressure value (0-127)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2009-01-22 16:47:21 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (val >= 0 && val <= 127, FLUID_FAILED);
|
2009-01-22 16:47:21 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, CHANNEL_PRESSURE, chan, val, 0);
|
|
|
|
else return fluid_synth_channel_pressure_LOCAL (synth, chan, val);
|
|
|
|
}
|
2009-01-22 16:47:21 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread variant of channel pressure set */
|
|
|
|
static int
|
|
|
|
fluid_synth_channel_pressure_LOCAL(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
|
|
|
if (synth->verbose)
|
2009-01-22 16:47:21 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_atomic_int_set (&synth->channel[chan]->channel_pressure, val);
|
|
|
|
return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE);
|
2009-01-22 16:47:21 +00:00
|
|
|
}
|
|
|
|
|
2008-12-23 10:15:01 +00:00
|
|
|
/**
|
2009-09-22 07:04:07 +00:00
|
|
|
* Set the MIDI pitch bend controller value on a MIDI channel.
|
2008-12-23 10:15:01 +00:00
|
|
|
* @param synth FluidSynth instance
|
2009-09-22 07:04:07 +00:00
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param val MIDI pitch bend value (0-16383 with 8192 being center)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (val >= 0 && val <= 16383, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, PITCH_BEND, chan, val, 0);
|
|
|
|
else return fluid_synth_pitch_bend_LOCAL (synth, chan, val);
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread variant of pitch bend */
|
|
|
|
static int
|
|
|
|
fluid_synth_pitch_bend_LOCAL(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
|
|
|
if (synth->verbose)
|
2003-04-03 21:32:56 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val);
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_atomic_int_set (&synth->channel[chan]->pitch_bend, val);
|
|
|
|
return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (ppitch_bend != NULL, FLUID_FAILED);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
*ppitch_bend = fluid_atomic_int_get (&synth->channel[chan]->pitch_bend);
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_midi_event (synth, RPN_LSB, chan,
|
|
|
|
RPN_PITCH_BEND_RANGE, val);
|
|
|
|
else return fluid_synth_pitch_wheel_sens_LOCAL (synth, chan, val);
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread variant of set pitch wheel sensitivity */
|
|
|
|
static int
|
|
|
|
fluid_synth_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan, int val)
|
|
|
|
{
|
|
|
|
if (synth->verbose)
|
2003-04-03 21:32:56 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_atomic_int_set (&synth->channel[chan]->pitch_wheel_sensitivity, val);
|
|
|
|
return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEELSENS);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
* @since: ?? Seems it was added sometime AFTER v1.0 API freeze.
|
2003-05-14 00:37:01 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-05-14 00:37:01 +00:00
|
|
|
fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (pval != NULL, FLUID_FAILED);
|
2003-05-14 00:37:01 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
*pval = fluid_atomic_int_get (&synth->channel[chan]->pitch_wheel_sensitivity);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
{
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_PRESET;
|
|
|
|
event->preset.channel = chan;
|
|
|
|
event->preset.preset = preset;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
return FLUID_OK;
|
2003-05-14 00:37:01 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
else return fluid_synth_set_preset_LOCAL (synth, chan, preset);
|
|
|
|
}
|
2003-05-14 00:37:01 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread variant of set channel preset */
|
|
|
|
static int
|
|
|
|
fluid_synth_set_preset_LOCAL (fluid_synth_t *synth, int chan,
|
|
|
|
fluid_preset_t *preset)
|
|
|
|
{
|
|
|
|
fluid_channel_t *channel;
|
2003-05-14 00:37:01 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
channel = synth->channel[chan];
|
|
|
|
fluid_channel_set_preset (channel, preset);
|
2003-05-14 00:37:01 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Get a preset by SoundFont, bank and program numbers.
|
|
|
|
* Returns preset pointer or NULL.
|
|
|
|
*
|
|
|
|
* NOTE: The returned preset has been allocated, caller owns it and should
|
|
|
|
* free it when finished using it.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_preset_t*
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
|
2009-09-22 07:04:07 +00:00
|
|
|
unsigned int banknum, unsigned int prognum)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_preset_t *preset = NULL;
|
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum)
|
|
|
|
{
|
|
|
|
preset = fluid_sfont_get_preset (sfont_info->sfont,
|
|
|
|
banknum - sfont_info->bankofs, prognum);
|
|
|
|
if (preset) sfont_info->refcount++; /* Add reference to SoundFont */
|
|
|
|
break;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return preset;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Get a preset by SoundFont name, bank and program.
|
|
|
|
* Returns preset pointer or NULL.
|
|
|
|
*
|
|
|
|
* NOTE: The returned preset has been allocated, caller owns it and should
|
|
|
|
* free it when finished using it.
|
2004-02-28 13:20:07 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_preset_t*
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname,
|
|
|
|
unsigned int banknum, unsigned int prognum)
|
2004-02-28 13:20:07 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_preset_t *preset = NULL;
|
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */
|
2004-02-28 13:20:07 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0)
|
|
|
|
{
|
|
|
|
preset = fluid_sfont_get_preset (sfont_info->sfont,
|
|
|
|
banknum - sfont_info->bankofs, prognum);
|
|
|
|
if (preset) sfont_info->refcount++; /* Add reference to SoundFont */
|
|
|
|
break;
|
2004-02-28 13:20:07 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return preset;
|
2004-02-28 13:20:07 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Find a preset by bank and program numbers.
|
|
|
|
* Returns preset pointer or NULL.
|
|
|
|
*
|
|
|
|
* NOTE: The returned preset has been allocated, caller owns it and should
|
|
|
|
* free it when finished using it. */
|
|
|
|
fluid_preset_t*
|
|
|
|
fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum,
|
|
|
|
unsigned int prognum)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_preset_t *preset = NULL;
|
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
|
|
|
int ofs;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
preset = fluid_sfont_get_preset (sfont_info->sfont,
|
|
|
|
banknum - sfont_info->bankofs, prognum);
|
|
|
|
if (preset)
|
|
|
|
{
|
|
|
|
sfont_info->refcount++; /* Add reference to SoundFont */
|
|
|
|
break;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return preset;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum)
|
|
|
|
{
|
|
|
|
fluid_preset_t* preset = NULL;
|
|
|
|
fluid_channel_t* channel;
|
2009-09-22 07:04:07 +00:00
|
|
|
int subst_bank, subst_prog, banknum;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (prognum >= 0 && prognum <= FLUID_NUM_PROGRAMS, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-03-16 01:37:21 +00:00
|
|
|
channel = synth->channel[chan];
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-03-16 01:37:21 +00:00
|
|
|
if (synth->verbose)
|
|
|
|
FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-03-16 01:37:21 +00:00
|
|
|
/* Special handling of channel 10 (or 9 counting from 0). channel
|
|
|
|
* 10 is the percussion channel.
|
|
|
|
*
|
|
|
|
* FIXME - Shouldn't hard code bank selection for channel 10. I think this
|
|
|
|
* is a hack for MIDI files that do bank changes in GM mode. Proper way to
|
2009-09-22 07:04:07 +00:00
|
|
|
* handle this would probably be to ignore bank changes when in GM mode. - JG
|
2009-03-16 01:37:21 +00:00
|
|
|
*/
|
|
|
|
if (channel->channum == 9)
|
|
|
|
preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, prognum);
|
|
|
|
else preset = fluid_synth_find_preset(synth, banknum, prognum);
|
|
|
|
|
|
|
|
/* Fallback to another preset if not found */
|
|
|
|
if (!preset)
|
|
|
|
{
|
|
|
|
subst_bank = banknum;
|
|
|
|
subst_prog = prognum;
|
|
|
|
|
|
|
|
/* Melodic instrument? */
|
|
|
|
if (channel->channum != 9 && banknum != DRUM_INST_BANK)
|
|
|
|
{
|
|
|
|
subst_bank = 0;
|
|
|
|
|
|
|
|
/* Fallback first to bank 0:prognum */
|
|
|
|
preset = fluid_synth_find_preset(synth, 0, prognum);
|
|
|
|
|
|
|
|
/* Fallback to first preset in bank 0 */
|
|
|
|
if (!preset && prognum != 0)
|
|
|
|
{
|
|
|
|
preset = fluid_synth_find_preset(synth, 0, 0);
|
|
|
|
subst_prog = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-03-16 01:37:21 +00:00
|
|
|
}
|
|
|
|
else /* Percussion: Fallback to preset 0 in percussion bank */
|
|
|
|
{
|
|
|
|
preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, 0);
|
|
|
|
subst_prog = 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-03-16 01:37:21 +00:00
|
|
|
if (preset)
|
2009-04-12 05:11:20 +00:00
|
|
|
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);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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);
|
|
|
|
return fluid_synth_set_preset (synth, chan, preset);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
|
|
|
|
fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id,
|
|
|
|
unsigned int* bank_num, unsigned int* preset_num)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_channel_t* channel;
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
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 (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);
|
|
|
|
|
|
|
|
channel = synth->channel[chan];
|
|
|
|
fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num,
|
|
|
|
(int *)preset_num);
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id,
|
|
|
|
unsigned int bank_num, unsigned int preset_num)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_preset_t* preset = NULL;
|
|
|
|
fluid_channel_t* channel;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
channel = synth->channel[chan];
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* ++ Allocate preset */
|
|
|
|
preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (preset == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR,
|
2009-09-22 07:04:07 +00:00
|
|
|
"There is no preset with bank number %d and preset number %d in SoundFont %d",
|
|
|
|
bank_num, preset_num, sfont_id);
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00: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);
|
|
|
|
return fluid_synth_set_preset (synth, chan, preset);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2004-02-28 13:20:07 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_program_select2(fluid_synth_t* synth, int chan, char* sfont_name,
|
|
|
|
unsigned int bank_num, unsigned int preset_num)
|
2004-02-28 13:20:07 +00:00
|
|
|
{
|
|
|
|
fluid_preset_t* preset = NULL;
|
|
|
|
fluid_channel_t* channel;
|
|
|
|
fluid_sfont_t* sfont = NULL;
|
2004-08-18 15:11:47 +00:00
|
|
|
int offset;
|
2004-02-28 13:20:07 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (sfont_name != NULL, FLUID_FAILED);
|
2004-02-28 13:20:07 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
channel = synth->channel[chan];
|
2004-02-28 13:20:07 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* ++ Allocate preset */
|
|
|
|
preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num,
|
|
|
|
preset_num);
|
2004-02-28 13:20:07 +00:00
|
|
|
if (preset == NULL) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR,
|
|
|
|
"There is no preset with bank number %d and preset number %d in SoundFont %s",
|
2004-02-28 13:20:07 +00:00
|
|
|
bank_num, preset_num, sfont_name);
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* 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);
|
|
|
|
return fluid_synth_set_preset (synth, chan, preset);
|
2004-02-28 13:20:07 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/*
|
2009-09-22 07:04:07 +00:00
|
|
|
* This function assures that every MIDI channel has a valid preset
|
2009-06-07 05:16:47 +00:00
|
|
|
* (NULL is okay). This function is called after a SoundFont is
|
|
|
|
* unloaded or reloaded.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static void
|
|
|
|
fluid_synth_update_presets(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_t *channel;
|
|
|
|
fluid_preset_t *preset;
|
|
|
|
int sfont, bank, prog;
|
2003-03-11 16:56:45 +00:00
|
|
|
int chan;
|
|
|
|
|
|
|
|
for (chan = 0; chan < synth->midi_channels; chan++) {
|
|
|
|
channel = synth->channel[chan];
|
2009-09-22 07:04:07 +00:00
|
|
|
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);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Handler for synth.gain setting. */
|
2009-06-07 05:16:47 +00:00
|
|
|
static int
|
|
|
|
fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value)
|
2003-04-03 21:32:56 +00:00
|
|
|
{
|
|
|
|
fluid_synth_set_gain(synth, (float) value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
void
|
|
|
|
fluid_synth_set_gain(fluid_synth_t* synth, float gain)
|
|
|
|
{
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
|
|
|
|
fluid_clip (gain, 0.0f, 10.0f);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
fluid_synth_queue_float_event (synth, FLUID_EVENT_QUEUE_ELEM_GAIN, gain);
|
|
|
|
else fluid_synth_set_gain_LOCAL (synth, gain);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by synthesis thread to update the gain in all voices */
|
|
|
|
static void
|
|
|
|
fluid_synth_set_gain_LOCAL(fluid_synth_t* synth, float gain)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_event_queue_elem_t *event;
|
2003-03-11 16:56:45 +00:00
|
|
|
int i;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
synth->st_gain = gain; /* Set Synth Thread gain value */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_voice_t* voice = synth->voice[i];
|
2003-03-11 16:56:45 +00:00
|
|
|
if (_PLAYING(voice)) {
|
|
|
|
fluid_voice_set_gain(voice, gain);
|
|
|
|
}
|
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
/* Send a return gain event to sync synth's copy of gain */
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (synth->return_queue);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
{
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_GAIN;
|
|
|
|
event->dval = gain;
|
|
|
|
fluid_event_queue_next_inptr (synth->return_queue);
|
|
|
|
}
|
|
|
|
else FLUID_LOG (FLUID_ERR, "Synth return event queue full");
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get synth output gain value.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Synth gain value (0.0 to 10.0)
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
float
|
|
|
|
fluid_synth_get_gain(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
float gain;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex);
|
|
|
|
gain = synth->gain;
|
|
|
|
fluid_mutex_unlock (synth->mutex);
|
|
|
|
|
|
|
|
return (gain);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
/*
|
2009-09-22 07:04:07 +00:00
|
|
|
* Handler for synth.polyphony setting.
|
2005-06-08 03:51:13 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static int
|
|
|
|
fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value)
|
2005-06-08 03:51:13 +00:00
|
|
|
{
|
|
|
|
fluid_synth_set_polyphony(synth, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Set synthesizer polyphony (max number of voices).
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param polyphony Polyphony to assign
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
* @since 1.0.6
|
2005-06-08 03:51:13 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony)
|
|
|
|
{
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (polyphony >= 1 && polyphony <= synth->nvoice, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_POLYPHONY,
|
|
|
|
polyphony);
|
|
|
|
else return fluid_synth_set_polyphony_LOCAL (synth, polyphony);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by synthesis thread to update the polyphony value */
|
|
|
|
static int
|
|
|
|
fluid_synth_set_polyphony_LOCAL(fluid_synth_t* synth, int polyphony)
|
2005-06-08 03:51:13 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_atomic_int_set (&synth->polyphony, polyphony);
|
2005-06-08 03:51:13 +00:00
|
|
|
|
|
|
|
/* turn off any voices above the new limit */
|
|
|
|
for (i = polyphony; i < synth->nvoice; i++) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_voice_t* voice = synth->voice[i];
|
2005-06-08 03:51:13 +00:00
|
|
|
if (_PLAYING(voice)) {
|
|
|
|
fluid_voice_off(voice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get current synthesizer polyphony (max number of voices).
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Synth polyphony value.
|
|
|
|
* @since 1.0.6
|
2005-06-08 03:51:13 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_get_polyphony(fluid_synth_t* synth)
|
2005-06-08 03:51:13 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
return fluid_atomic_int_get (&synth->polyphony);
|
2005-06-08 03:51:13 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_get_internal_bufsize(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
return FLUID_BUFSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-09-22 07:04:07 +00:00
|
|
|
* Resend a bank select and a program change for every channel.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*
|
2009-09-22 07:04:07 +00:00
|
|
|
* This function is called mainly after a SoundFont has been loaded,
|
|
|
|
* unloaded or reloaded.
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_program_reset(fluid_synth_t* synth)
|
2007-03-04 16:45:05 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
int i, prog;
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* try to set the correct presets */
|
|
|
|
for (i = 0; i < synth->midi_channels; i++){
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_get_sfont_bank_prog (synth->channel[i], NULL, NULL, &prog);
|
|
|
|
fluid_synth_program_change(synth, i, prog);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Synthesize a block of floating point audio to audio buffers.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param len Count of audio frames to synthesize
|
|
|
|
* @param left Array of floats to store left channel of audio (len in size)
|
|
|
|
* @param right Array of floats to store right channel of audio (len in size)
|
|
|
|
* @param fx_left Not currently used
|
|
|
|
* @param fx_right Not currently used
|
|
|
|
* @return FLUID_OK on success, FLUID_FAIL otherwise
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from synthesis thread.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
|
|
|
|
float** left, float** right,
|
|
|
|
float** fx_left, float** fx_right)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_real_t** left_in = synth->left_buf;
|
|
|
|
fluid_real_t** right_in = synth->right_buf;
|
|
|
|
double time = fluid_utime();
|
|
|
|
int i, num, available, count, bytes;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* make sure we're playing */
|
2009-09-22 07:04:07 +00:00
|
|
|
if (synth->state != FLUID_SYNTH_PLAYING)
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* First, take what's still available in the buffer */
|
|
|
|
count = 0;
|
2005-06-08 03:51:13 +00:00
|
|
|
num = synth->cur;
|
|
|
|
if (synth->cur < FLUID_BUFSIZE) {
|
|
|
|
available = FLUID_BUFSIZE - synth->cur;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
num = (available > len)? len : available;
|
|
|
|
bytes = num * sizeof(float);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->audio_channels; i++) {
|
|
|
|
FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes);
|
|
|
|
FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes);
|
|
|
|
}
|
|
|
|
count += num;
|
|
|
|
num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Then, run one_block() and copy till we have 'len' samples */
|
|
|
|
while (count < len) {
|
2004-03-19 11:52:56 +00:00
|
|
|
fluid_synth_one_block(synth, 1);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2004-03-19 11:52:56 +00:00
|
|
|
num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
|
|
|
|
bytes = num * sizeof(float);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2004-03-19 11:52:56 +00:00
|
|
|
for (i = 0; i < synth->audio_channels; i++) {
|
|
|
|
FLUID_MEMCPY(left[i] + count, left_in[i], bytes);
|
|
|
|
FLUID_MEMCPY(right[i] + count, right_in[i], bytes);
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2004-03-19 11:52:56 +00:00
|
|
|
count += num;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
synth->cur = num;
|
|
|
|
|
|
|
|
time = fluid_utime() - time;
|
|
|
|
synth->cpu_load = 0.5 * (synth->cpu_load +
|
|
|
|
time * synth->sample_rate / len / 10000.0);
|
|
|
|
|
|
|
|
/* printf("CPU: %.2f\n", synth->cpu_load); */
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Synthesize floating point audio to audio buffers.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param len Count of audio frames to synthesize
|
|
|
|
* @param nin Ignored
|
|
|
|
* @param in Ignored
|
|
|
|
* @param nout Count of arrays in 'out'
|
|
|
|
* @param out Array of arrays to store audio to
|
|
|
|
* @return FLUID_OK on success, FLUID_FAIL otherwise
|
|
|
|
*
|
|
|
|
* This function implements the default interface defined in fluidsynth/audio.h.
|
|
|
|
* NOTE: Should only be called from synthesis thread.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* FIXME: Currently if nout != 2 memory allocation will occur!
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in,
|
|
|
|
int nout, float** out)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-02-22 09:52:32 +00:00
|
|
|
if (nout==2) {
|
|
|
|
return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
float **left, **right;
|
|
|
|
int i;
|
|
|
|
left = FLUID_ARRAY(float*, nout/2);
|
|
|
|
right = FLUID_ARRAY(float*, nout/2);
|
|
|
|
for(i=0; i<nout/2; i++) {
|
|
|
|
left[i] = out[2*i];
|
|
|
|
right[i] = out[2*i+1];
|
|
|
|
}
|
|
|
|
fluid_synth_nwrite_float(synth, len, left, right, NULL, NULL);
|
|
|
|
FLUID_FREE(left);
|
|
|
|
FLUID_FREE(right);
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
2009-02-22 09:52:32 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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'
|
|
|
|
* @return FLUID_OK on success, FLUID_FAIL otherwise
|
|
|
|
*
|
|
|
|
* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
|
|
|
|
* lincr = 2, rincr = 2).
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from synthesis thread.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
|
|
|
fluid_synth_write_float(fluid_synth_t* synth, int len,
|
2009-09-22 07:04:07 +00:00
|
|
|
void* lout, int loff, int lincr,
|
|
|
|
void* rout, int roff, int rincr)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int i, j, k, l;
|
|
|
|
float* left_out = (float*) lout;
|
|
|
|
float* right_out = (float*) rout;
|
|
|
|
fluid_real_t* left_in = synth->left_buf[0];
|
|
|
|
fluid_real_t* right_in = synth->right_buf[0];
|
|
|
|
double time = fluid_utime();
|
|
|
|
|
|
|
|
/* make sure we're playing */
|
2009-09-22 07:04:07 +00:00
|
|
|
if (synth->state != FLUID_SYNTH_PLAYING)
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
l = synth->cur;
|
|
|
|
|
|
|
|
for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) {
|
|
|
|
/* fill up the buffers as needed */
|
|
|
|
if (l == FLUID_BUFSIZE) {
|
|
|
|
fluid_synth_one_block(synth, 0);
|
|
|
|
l = 0;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
left_out[j] = (float) left_in[l];
|
|
|
|
right_out[k] = (float) right_in[l];
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->cur = l;
|
|
|
|
|
|
|
|
time = fluid_utime() - time;
|
|
|
|
synth->cpu_load = 0.5 * (synth->cpu_load +
|
|
|
|
time * synth->sample_rate / len / 10000.0);
|
|
|
|
|
|
|
|
/* printf("CPU: %.2f\n", synth->cpu_load); */
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2006-11-21 20:04:31 +00:00
|
|
|
#define DITHER_SIZE 48000
|
|
|
|
#define DITHER_CHANNELS 2
|
|
|
|
|
|
|
|
static float rand_table[DITHER_CHANNELS][DITHER_SIZE];
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Init dither table */
|
2009-06-07 05:16:47 +00:00
|
|
|
static void
|
|
|
|
init_dither(void)
|
2006-11-21 20:04:31 +00:00
|
|
|
{
|
|
|
|
float d, dp;
|
|
|
|
int c, i;
|
|
|
|
|
|
|
|
for (c = 0; c < DITHER_CHANNELS; c++) {
|
|
|
|
dp = 0;
|
|
|
|
for (i = 0; i < DITHER_SIZE-1; i++) {
|
|
|
|
d = rand() / (float)RAND_MAX - 0.5f;
|
|
|
|
rand_table[c][i] = d - dp;
|
|
|
|
dp = d;
|
|
|
|
}
|
|
|
|
rand_table[c][DITHER_SIZE-1] = 0 - dp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-14 17:48:51 +00:00
|
|
|
/* A portable replacement for roundf(), seems it may actually be faster too! */
|
|
|
|
static inline int
|
|
|
|
roundi (float x)
|
|
|
|
{
|
|
|
|
if (x >= 0.0f)
|
|
|
|
return (int)(x+0.5f);
|
|
|
|
else
|
|
|
|
return (int)(x-0.5f);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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'
|
|
|
|
* @return FLUID_OK on success, FLUID_FAIL otherwise
|
|
|
|
*
|
|
|
|
* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
|
|
|
|
* lincr = 2, rincr = 2).
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from synthesis thread.
|
|
|
|
* NOTE: Dithering is performed when converting from internal floating point to
|
|
|
|
* 16 bit audio.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
|
|
|
fluid_synth_write_s16(fluid_synth_t* synth, int len,
|
2009-09-22 07:04:07 +00:00
|
|
|
void* lout, int loff, int lincr,
|
|
|
|
void* rout, int roff, int rincr)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int i, j, k, cur;
|
|
|
|
signed short* left_out = (signed short*) lout;
|
|
|
|
signed short* right_out = (signed short*) rout;
|
|
|
|
fluid_real_t* left_in = synth->left_buf[0];
|
|
|
|
fluid_real_t* right_in = synth->right_buf[0];
|
|
|
|
double prof_ref = fluid_profile_ref();
|
|
|
|
fluid_real_t left_sample;
|
|
|
|
fluid_real_t right_sample;
|
|
|
|
double time = fluid_utime();
|
2006-11-21 20:04:31 +00:00
|
|
|
int di = synth->dither_index;
|
2007-01-14 17:48:51 +00:00
|
|
|
double prof_ref_on_block;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* make sure we're playing */
|
|
|
|
if (synth->state != FLUID_SYNTH_PLAYING) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cur = synth->cur;
|
|
|
|
|
|
|
|
for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) {
|
|
|
|
|
|
|
|
/* fill up the buffers as needed */
|
|
|
|
if (cur == FLUID_BUFSIZE) {
|
2007-01-14 17:48:51 +00:00
|
|
|
prof_ref_on_block = fluid_profile_ref();
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_synth_one_block(synth, 0);
|
|
|
|
cur = 0;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
|
|
|
|
}
|
|
|
|
|
2007-01-14 17:48:51 +00:00
|
|
|
left_sample = roundi (left_in[cur] * 32766.0f + rand_table[0][di]);
|
|
|
|
right_sample = roundi (right_in[cur] * 32766.0f + rand_table[1][di]);
|
2006-11-21 20:04:31 +00:00
|
|
|
|
|
|
|
di++;
|
|
|
|
if (di >= DITHER_SIZE) di = 0;
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* digital clipping */
|
2006-11-21 20:04:31 +00:00
|
|
|
if (left_sample > 32767.0f) left_sample = 32767.0f;
|
|
|
|
if (left_sample < -32768.0f) left_sample = -32768.0f;
|
|
|
|
if (right_sample > 32767.0f) right_sample = 32767.0f;
|
|
|
|
if (right_sample < -32768.0f) right_sample = -32768.0f;
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
left_out[j] = (signed short) left_sample;
|
|
|
|
right_out[k] = (signed short) right_sample;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->cur = cur;
|
2006-11-21 20:04:31 +00:00
|
|
|
synth->dither_index = di; /* keep dither buffer continous */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
|
|
|
|
|
|
|
|
|
|
|
|
time = fluid_utime() - time;
|
|
|
|
synth->cpu_load = 0.5 * (synth->cpu_load +
|
|
|
|
time * synth->sample_rate / len / 10000.0);
|
|
|
|
|
|
|
|
/* printf("CPU: %.2f\n", synth->cpu_load); */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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'
|
|
|
|
*
|
|
|
|
* NOTE: Currently private to libfluidsynth.
|
2006-11-21 20:04:31 +00:00
|
|
|
*/
|
|
|
|
void
|
2007-09-20 07:47:38 +00:00
|
|
|
fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
|
2006-11-21 20:04:31 +00:00
|
|
|
void* lout, int loff, int lincr,
|
|
|
|
void* rout, int roff, int rincr)
|
|
|
|
{
|
|
|
|
int i, j, k;
|
|
|
|
signed short* left_out = (signed short*) lout;
|
|
|
|
signed short* right_out = (signed short*) rout;
|
|
|
|
double prof_ref = fluid_profile_ref();
|
|
|
|
fluid_real_t left_sample;
|
|
|
|
fluid_real_t right_sample;
|
2007-09-20 07:47:38 +00:00
|
|
|
int di = *dither_index;
|
2006-11-21 20:04:31 +00:00
|
|
|
|
|
|
|
for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) {
|
|
|
|
|
2007-01-14 17:48:51 +00:00
|
|
|
left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]);
|
|
|
|
right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]);
|
2006-11-21 20:04:31 +00:00
|
|
|
|
|
|
|
di++;
|
|
|
|
if (di >= DITHER_SIZE) di = 0;
|
|
|
|
|
|
|
|
/* digital clipping */
|
|
|
|
if (left_sample > 32767.0f) left_sample = 32767.0f;
|
|
|
|
if (left_sample < -32768.0f) left_sample = -32768.0f;
|
|
|
|
if (right_sample > 32767.0f) right_sample = 32767.0f;
|
|
|
|
if (right_sample < -32768.0f) right_sample = -32768.0f;
|
|
|
|
|
|
|
|
left_out[j] = (signed short) left_sample;
|
|
|
|
right_out[k] = (signed short) right_sample;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2007-09-20 07:47:38 +00:00
|
|
|
*dither_index = di; /* keep dither buffer continous */
|
2006-11-21 20:04:31 +00:00
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
/*
|
2009-09-22 07:04:07 +00:00
|
|
|
* Process a single block (FLUID_BUFSIZE) of audio.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
|
|
|
|
{
|
|
|
|
int i, auchan;
|
|
|
|
fluid_voice_t* voice;
|
|
|
|
fluid_real_t* left_buf;
|
|
|
|
fluid_real_t* right_buf;
|
|
|
|
fluid_real_t* reverb_buf;
|
|
|
|
fluid_real_t* chorus_buf;
|
|
|
|
int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
|
|
|
|
double prof_ref = fluid_profile_ref();
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Assign ID of synthesis thread, if not already set */
|
|
|
|
if (synth->synth_thread_id == FLUID_THREAD_ID_NULL)
|
|
|
|
synth->synth_thread_id = fluid_thread_get_id ();
|
2007-03-04 16:45:05 +00:00
|
|
|
|
|
|
|
fluid_check_fpe("??? Just starting up ???");
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-06-07 08:10:09 +00:00
|
|
|
fluid_sample_timer_process(synth);
|
|
|
|
|
|
|
|
fluid_check_fpe("fluid_sample_timer_process");
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Process queued events */
|
|
|
|
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
|
|
|
|
{
|
|
|
|
if (synth->queues[i])
|
|
|
|
fluid_synth_process_event_queue_LOCAL (synth, synth->queues[i]);
|
|
|
|
else break; /* First NULL ends the array (values are never set to NULL) */
|
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* clean the audio buffers */
|
|
|
|
for (i = 0; i < synth->nbuf; i++) {
|
|
|
|
FLUID_MEMSET(synth->left_buf[i], 0, byte_size);
|
|
|
|
FLUID_MEMSET(synth->right_buf[i], 0, byte_size);
|
|
|
|
}
|
2007-09-20 07:47:38 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
for (i = 0; i < synth->effects_channels; i++) {
|
|
|
|
FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size);
|
|
|
|
FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up the reverb / chorus buffers only, when the effect is
|
|
|
|
* enabled on synth level. Nonexisting buffers are detected in the
|
|
|
|
* DSP loop. Not sending the reverb / chorus signal saves some time
|
|
|
|
* in that case. */
|
2009-07-27 10:51:05 +00:00
|
|
|
reverb_buf = synth->with_reverb ? synth->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL;
|
|
|
|
chorus_buf = synth->with_chorus ? synth->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
|
|
|
|
|
|
|
|
/* call all playing synthesis processes */
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (_PLAYING(voice)) {
|
|
|
|
double prof_ref_voice = fluid_profile_ref();
|
|
|
|
|
2004-03-19 11:52:56 +00:00
|
|
|
/* The output associated with a MIDI channel is wrapped around
|
|
|
|
* using the number of audio groups as modulo divider. This is
|
|
|
|
* typically the number of output channels on the 'sound card',
|
|
|
|
* as long as the LADSPA Fx unit is not used. In case of LADSPA
|
|
|
|
* unit, think of it as subgroups on a mixer.
|
|
|
|
*
|
2003-03-11 16:56:45 +00:00
|
|
|
* For example: Assume that the number of groups is set to 2.
|
2004-03-19 11:52:56 +00:00
|
|
|
* Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2,
|
|
|
|
* 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI
|
|
|
|
* channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to
|
|
|
|
* output 2, 3, 6, 9, 12 etc to output 3.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
auchan = fluid_channel_get_num(fluid_voice_get_channel(voice));
|
|
|
|
auchan %= synth->audio_groups;
|
|
|
|
left_buf = synth->left_buf[auchan];
|
|
|
|
right_buf = synth->right_buf[auchan];
|
|
|
|
|
|
|
|
fluid_voice_write(voice, left_buf, right_buf, reverb_buf, chorus_buf);
|
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice);
|
|
|
|
}
|
|
|
|
}
|
2007-09-02 23:23:47 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_check_fpe("Synthesis processes");
|
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref);
|
|
|
|
|
|
|
|
/* if multi channel output, don't mix the output of the chorus and
|
|
|
|
reverb in the final output. The effects outputs are send
|
|
|
|
separately. */
|
|
|
|
|
|
|
|
if (do_not_mix_fx_to_out) {
|
|
|
|
|
|
|
|
/* send to reverb */
|
|
|
|
if (reverb_buf) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_revmodel_processreplace(synth->reverb, reverb_buf,
|
2009-07-27 10:51:05 +00:00
|
|
|
synth->fx_left_buf[SYNTH_REVERB_CHANNEL],
|
|
|
|
synth->fx_right_buf[SYNTH_REVERB_CHANNEL]);
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_check_fpe("Reverb");
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* send to chorus */
|
|
|
|
if (chorus_buf) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_chorus_processreplace(synth->chorus, chorus_buf,
|
2009-07-27 10:51:05 +00:00
|
|
|
synth->fx_left_buf[SYNTH_CHORUS_CHANNEL],
|
|
|
|
synth->fx_right_buf[SYNTH_CHORUS_CHANNEL]);
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_check_fpe("Chorus");
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* send to reverb */
|
|
|
|
if (reverb_buf) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_revmodel_processmix(synth->reverb, reverb_buf,
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->left_buf[0], synth->right_buf[0]);
|
|
|
|
fluid_check_fpe("Reverb");
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* send to chorus */
|
|
|
|
if (chorus_buf) {
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_chorus_processmix(synth->chorus, chorus_buf,
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->left_buf[0], synth->right_buf[0]);
|
|
|
|
fluid_check_fpe("Chorus");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref);
|
|
|
|
|
|
|
|
#ifdef LADSPA
|
|
|
|
/* Run the signal through the LADSPA Fx unit */
|
|
|
|
fluid_LADSPA_run(synth->LADSPA_FxUnit, synth->left_buf, synth->right_buf, synth->fx_left_buf, synth->fx_right_buf);
|
|
|
|
fluid_check_fpe("LADSPA");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
synth->ticks += FLUID_BUFSIZE;
|
|
|
|
|
|
|
|
/* Testcase, that provokes a denormal floating point error */
|
|
|
|
#if 0
|
|
|
|
{float num=1;while (num != 0){num*=0.5;};};
|
|
|
|
#endif
|
|
|
|
fluid_check_fpe("??? Remainder of synth_one_block ???");
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
return 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Process events in an event queue */
|
|
|
|
static FLUID_INLINE void
|
|
|
|
fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth,
|
|
|
|
fluid_event_queue_t *queue)
|
|
|
|
{
|
|
|
|
fluid_event_queue_elem_t *event;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
while ((event = fluid_event_queue_get_outptr (queue)))
|
|
|
|
{
|
|
|
|
if (event->type == FLUID_EVENT_QUEUE_ELEM_MIDI)
|
|
|
|
{
|
|
|
|
switch (event->midi.type)
|
|
|
|
{
|
|
|
|
case NOTE_ON:
|
|
|
|
fluid_synth_noteon_LOCAL (synth, event->midi.channel,
|
|
|
|
event->midi.param1, event->midi.param2);
|
|
|
|
break;
|
|
|
|
case NOTE_OFF:
|
|
|
|
fluid_synth_noteoff_LOCAL (synth, event->midi.channel,
|
|
|
|
event->midi.param1);
|
|
|
|
break;
|
|
|
|
case CONTROL_CHANGE:
|
|
|
|
fluid_synth_cc_LOCAL (synth, event->midi.channel, event->midi.param1,
|
|
|
|
event->midi.param2);
|
|
|
|
break;
|
|
|
|
case MIDI_SYSTEM_RESET:
|
|
|
|
fluid_synth_system_reset_LOCAL (synth);
|
|
|
|
break;
|
|
|
|
case CHANNEL_PRESSURE:
|
|
|
|
fluid_synth_channel_pressure_LOCAL (synth, event->midi.channel,
|
|
|
|
event->midi.param1);
|
|
|
|
break;
|
|
|
|
case PITCH_BEND:
|
|
|
|
fluid_synth_pitch_bend_LOCAL (synth, event->midi.channel,
|
|
|
|
event->midi.param1);
|
|
|
|
break;
|
|
|
|
case RPN_LSB:
|
|
|
|
switch (event->midi.param1)
|
|
|
|
{
|
|
|
|
case RPN_PITCH_BEND_RANGE:
|
|
|
|
fluid_synth_pitch_wheel_sens_LOCAL (synth, event->midi.channel,
|
|
|
|
event->midi.param2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_GAIN)
|
|
|
|
fluid_synth_set_gain_LOCAL (synth, event->dval);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_POLYPHONY)
|
|
|
|
fluid_synth_set_polyphony_LOCAL (synth, event->ival);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_GEN)
|
|
|
|
fluid_synth_set_gen_LOCAL (synth, event->gen.channel, event->gen.param,
|
|
|
|
event->gen.value, event->gen.absolute);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_PRESET)
|
|
|
|
fluid_synth_set_preset_LOCAL (synth, event->preset.channel,
|
|
|
|
event->preset.preset);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_STOP_VOICES)
|
|
|
|
fluid_synth_stop_LOCAL (synth, event->ival);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_REVERB)
|
|
|
|
fluid_synth_set_reverb_LOCAL (synth, event->reverb.set, event->reverb.roomsize,
|
|
|
|
event->reverb.damping, event->reverb.width,
|
|
|
|
event->reverb.level);
|
|
|
|
else if (event->type == FLUID_EVENT_QUEUE_ELEM_CHORUS)
|
|
|
|
fluid_synth_set_chorus_LOCAL (synth, event->chorus.set, event->chorus.nr,
|
|
|
|
event->chorus.type, event->chorus.level,
|
|
|
|
event->chorus.speed, event->chorus.depth);
|
|
|
|
|
|
|
|
fluid_event_queue_next_outptr (queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Selects a voice for killing. */
|
2009-06-07 05:16:47 +00:00
|
|
|
static fluid_voice_t*
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int i;
|
2004-05-05 20:27:22 +00:00
|
|
|
fluid_real_t best_prio = 999999.;
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_real_t this_voice_prio;
|
|
|
|
fluid_voice_t* voice;
|
|
|
|
int best_voice_index=-1;
|
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* safeguard against an available voice. */
|
2004-05-05 20:27:22 +00:00
|
|
|
if (_AVAILABLE(voice)) {
|
2003-03-11 16:56:45 +00:00
|
|
|
return voice;
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* Determine, how 'important' a voice is.
|
|
|
|
* Start with an arbitrary number */
|
2007-03-04 16:45:05 +00:00
|
|
|
this_voice_prio = 10000.;
|
|
|
|
|
|
|
|
/* Is this voice on the drum channel?
|
2003-03-11 16:56:45 +00:00
|
|
|
* Then it is very important.
|
|
|
|
* Also, forget about the released-note condition:
|
|
|
|
* Typically, drum notes are triggered only very briefly, they run most
|
|
|
|
* of the time in release phase.
|
|
|
|
*/
|
|
|
|
if (voice->chan == 9){
|
2004-05-05 20:27:22 +00:00
|
|
|
this_voice_prio += 4000;
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
} else if (_RELEASED(voice)){
|
|
|
|
/* The key for this voice has been released. Consider it much less important
|
|
|
|
* than a voice, which is still held.
|
|
|
|
*/
|
2004-05-05 20:27:22 +00:00
|
|
|
this_voice_prio -= 2000.;
|
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (_SUSTAINED(voice)){
|
|
|
|
/* The sustain pedal is held down on this channel.
|
|
|
|
* Consider it less important than non-sustained channels.
|
|
|
|
* This decision is somehow subjective. But usually the sustain pedal
|
|
|
|
* is used to play 'more-voices-than-fingers', so it shouldn't hurt
|
|
|
|
* if we kill one voice.
|
|
|
|
*/
|
2004-05-05 20:27:22 +00:00
|
|
|
this_voice_prio -= 1000;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* We are not enthusiastic about releasing voices, which have just been started.
|
|
|
|
* Otherwise hitting a chord may result in killing notes belonging to that very same
|
2007-03-04 16:45:05 +00:00
|
|
|
* chord.
|
2003-03-11 16:56:45 +00:00
|
|
|
* So subtract the age of the voice from the priority - an older voice is just a little
|
2007-03-04 16:45:05 +00:00
|
|
|
* bit less important than a younger voice.
|
2003-03-11 16:56:45 +00:00
|
|
|
* This is a number between roughly 0 and 100.*/
|
2004-05-05 20:27:22 +00:00
|
|
|
this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* take a rough estimate of loudness into account. Louder voices are more important. */
|
|
|
|
if (voice->volenv_section != FLUID_VOICE_ENVATTACK){
|
2004-05-05 20:27:22 +00:00
|
|
|
this_voice_prio += voice->volenv_val * 1000.;
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2004-05-05 20:27:22 +00:00
|
|
|
if (best_voice_index < 0) {
|
2003-03-11 16:56:45 +00:00
|
|
|
return NULL;
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[best_voice_index];
|
2004-05-05 20:27:22 +00:00
|
|
|
fluid_voice_off(voice);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return voice;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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.
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from within synthesis thread, which includes
|
|
|
|
* SoundFont loader preset noteon method.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
fluid_voice_t*
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel)
|
|
|
|
{
|
|
|
|
int i, k;
|
|
|
|
fluid_voice_t* voice = NULL;
|
2004-05-05 20:27:22 +00:00
|
|
|
fluid_channel_t* channel = NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (sample != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL);
|
|
|
|
fluid_return_val_if_fail (fluid_synth_is_synth_thread (synth), NULL);
|
2003-05-29 15:43:52 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* check if there's an available synthesis process */
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
if (_AVAILABLE(synth->voice[i])) {
|
|
|
|
voice = synth->voice[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No success yet? Then stop a running voice. */
|
|
|
|
if (voice == NULL) {
|
2009-09-22 07:04:07 +00:00
|
|
|
voice = fluid_synth_free_voice_by_kill_LOCAL(synth);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (voice == NULL) {
|
|
|
|
FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);
|
|
|
|
return NULL;
|
2007-03-04 16:45:05 +00:00
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
if (synth->verbose) {
|
|
|
|
k = 0;
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
if (!_AVAILABLE(synth->voice[i])) {
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",
|
|
|
|
chan, key, vel, synth->storeid,
|
|
|
|
(float) synth->ticks / 44100.0f,
|
2003-03-11 16:56:45 +00:00
|
|
|
(fluid_curtime() - synth->start) / 1000.0f,
|
|
|
|
0.0f,
|
|
|
|
k);
|
|
|
|
}
|
|
|
|
|
2004-05-05 20:27:22 +00:00
|
|
|
if (chan >= 0) {
|
|
|
|
channel = synth->channel[chan];
|
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
if (fluid_voice_init(voice, sample, channel, key, vel,
|
2004-05-05 20:27:22 +00:00
|
|
|
synth->storeid, synth->ticks, synth->gain) != FLUID_OK) {
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* add the default modulators to the synthesis process. */
|
|
|
|
fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */
|
2009-01-29 23:05:59 +00:00
|
|
|
fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */
|
|
|
|
fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */
|
|
|
|
fluid_voice_add_mod(voice, &default_att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.5 */
|
|
|
|
fluid_voice_add_mod(voice, &default_pan_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.6 */
|
|
|
|
fluid_voice_add_mod(voice, &default_expr_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.7 */
|
|
|
|
fluid_voice_add_mod(voice, &default_reverb_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.8 */
|
|
|
|
fluid_voice_add_mod(voice, &default_chorus_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.9 */
|
|
|
|
fluid_voice_add_mod(voice, &default_pitch_bend_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.10 */
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return voice;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Kill all voices on a given channel, which have the same exclusive class
|
|
|
|
* generator as new_voice.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static void
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth,
|
|
|
|
fluid_voice_t* new_voice)
|
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS);
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_voice_t* existing_voice;
|
|
|
|
int i;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
/* Excl. class 0: No exclusive class */
|
2009-09-22 07:04:07 +00:00
|
|
|
if (excl_class == 0) return;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Kill all notes on the same channel with the same exclusive class */
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2009-09-22 07:04:07 +00:00
|
|
|
existing_voice = synth->voice[i];
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00: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 */
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (_PLAYING(existing_voice)
|
|
|
|
&& existing_voice->chan == new_voice->chan
|
|
|
|
&& (int)_GEN (existing_voice, GEN_EXCLUSIVECLASS) == excl_class
|
|
|
|
&& fluid_voice_get_id (existing_voice) != fluid_voice_get_id(new_voice))
|
|
|
|
fluid_voice_kill_excl(existing_voice);
|
|
|
|
}
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00: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.
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from within synthesis thread, which includes
|
|
|
|
* SoundFont loader preset noteon method.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
void
|
|
|
|
fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
fluid_return_if_fail (voice != NULL);
|
|
|
|
fluid_return_if_fail (fluid_synth_is_synth_thread (synth));
|
2003-05-29 15:43:52 +00:00
|
|
|
|
2003-03-11 16:56:45 +00: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. */
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);
|
2003-05-29 15:43:52 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_voice_start(voice); /* Start the new voice */
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Add a SoundFont loader interface.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param loader Loader API structure, used directly and should remain allocated
|
|
|
|
* as long as the synth instance is used.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called before any SoundFont files are loaded.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
void
|
|
|
|
fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
gboolean sfont_already_loaded;
|
|
|
|
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
fluid_return_if_fail (loader != NULL);
|
|
|
|
sfont_already_loaded = synth->sfont_info != NULL;
|
|
|
|
fluid_return_if_fail (!sfont_already_loaded);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex);
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->loaders = fluid_list_prepend(synth->loaders, loader);
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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.
|
|
|
|
*
|
|
|
|
* @param synth SoundFont instance
|
|
|
|
* @param filename File to load
|
|
|
|
* @param reset_presets TRUE to re-assign presets for all MIDI channels
|
|
|
|
* @return SoundFont ID on success, FLUID_FAILED on error
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_sfont_t *sfont;
|
|
|
|
fluid_list_t *list;
|
|
|
|
fluid_sfloader_t *loader;
|
|
|
|
unsigned int sfont_id;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (filename != NULL, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* MT NOTE: Loaders list should not change. */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
for (list = synth->loaders; list; list = fluid_list_next(list)) {
|
|
|
|
loader = (fluid_sfloader_t*) fluid_list_get(list);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
sfont = fluid_sfloader_load(loader, filename);
|
|
|
|
|
|
|
|
if (sfont != NULL) {
|
2009-09-22 07:04:07 +00:00
|
|
|
sfont_info = new_fluid_sfont_info (synth, sfont);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (!sfont_info)
|
|
|
|
{
|
|
|
|
delete_fluid_sfont (sfont_info->sfont); /* FIXME - Shouldn't fail right? - JG */
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_id and sfont list */
|
|
|
|
sfont->id = sfont_id = ++synth->sfont_id;
|
|
|
|
synth->sfont_info = fluid_list_prepend(synth->sfont_info, sfont_info); /* prepend to list */
|
|
|
|
fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* reset the presets for all channels if requested */
|
|
|
|
if (reset_presets) fluid_synth_program_reset(synth);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return (int)sfont_id;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Create a new SoundFont info structure, free with FLUID_FREE */
|
|
|
|
static fluid_sfont_info_t *
|
|
|
|
new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
sfont_info = FLUID_NEW (fluid_sfont_info_t);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (!sfont_info)
|
|
|
|
{
|
|
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
|
|
return NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
sfont_info->sfont = sfont;
|
|
|
|
sfont_info->synth = synth;
|
|
|
|
sfont_info->refcount = 1; /* Start with refcount of 1 for owning synth */
|
|
|
|
sfont_info->bankofs = 0;
|
|
|
|
|
|
|
|
return (sfont_info);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Unload a SoundFont.
|
|
|
|
* @param synth SoundFont instance
|
|
|
|
* @param id ID of SoundFont to unload
|
|
|
|
* @param reset_presets TRUE to re-assign presets for all MIDI channels
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED on error
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* remove the SoundFont from the list */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */
|
|
|
|
|
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t*) fluid_list_get(list);
|
|
|
|
|
|
|
|
if (fluid_sfont_get_id (sfont_info->sfont) == id)
|
|
|
|
{
|
|
|
|
synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
if (!list) {
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* -- Remove synth->sfont_info list's reference to SoundFont */
|
|
|
|
fluid_synth_sfont_unref (synth, sfont_info->sfont);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unref a SoundFont and destroy if no more references */
|
|
|
|
static void
|
|
|
|
fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont)
|
|
|
|
{
|
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
int refcount = 0;
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_hash */
|
|
|
|
|
|
|
|
sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont);
|
|
|
|
|
|
|
|
if (sfont_info)
|
|
|
|
{
|
|
|
|
sfont_info->refcount--; /* -- Remove the sfont_info list's reference */
|
|
|
|
refcount = sfont_info->refcount;
|
|
|
|
|
|
|
|
if (refcount == 0) /* Remove SoundFont from hash if no more references */
|
|
|
|
fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock sfont_hash */
|
|
|
|
|
|
|
|
fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */
|
|
|
|
|
|
|
|
if (refcount == 0) /* No more references? - Attempt delete */
|
|
|
|
{
|
|
|
|
if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */
|
|
|
|
{
|
|
|
|
FLUID_FREE (sfont_info);
|
|
|
|
FLUID_LOG (FLUID_DBG, "Unloaded SoundFont");
|
|
|
|
} /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */
|
|
|
|
else new_fluid_timer (100, fluid_synth_sfunload_callback, sfont_info, TRUE, TRUE, FALSE);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
}
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Callback to continually attempt to unload a SoundFont,
|
|
|
|
* only if a SoundFont loader blocked the unload operation */
|
|
|
|
static int
|
|
|
|
fluid_synth_sfunload_callback(void* data, unsigned int msec)
|
|
|
|
{
|
|
|
|
fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data;
|
|
|
|
|
|
|
|
if (delete_fluid_sfont (sfont_info->sfont) == 0)
|
|
|
|
{
|
|
|
|
FLUID_FREE (sfont_info);
|
|
|
|
FLUID_LOG (FLUID_DBG, "Unloaded SoundFont");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else return TRUE;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack.
|
|
|
|
* @param synth SoundFont instance
|
|
|
|
* @param id ID of SoundFont to reload
|
|
|
|
* @return SoundFont ID on success, FLUID_FAILED on error
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
char filename[1024];
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info, *old_sfont_info;
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_sfont_t* sfont;
|
|
|
|
fluid_sfloader_t* loader;
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_list_t *list;
|
|
|
|
int index;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */
|
|
|
|
|
|
|
|
/* Search for SoundFont and get its index */
|
|
|
|
for (list = synth->sfont_info, index = 0; list; list = fluid_list_next (list), index++) {
|
|
|
|
old_sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
|
|
|
if (fluid_sfont_get_id (old_sfont_info->sfont) == id) break;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (!list) {
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
|
|
|
|
return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* keep a copy of the SoundFont's filename */
|
2009-09-22 07:04:07 +00:00
|
|
|
FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont));
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK)
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_FAILED;
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
/* MT Note: SoundFont loader list will not change */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
for (list = synth->loaders; list; list = fluid_list_next(list)) {
|
|
|
|
loader = (fluid_sfloader_t*) fluid_list_get(list);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
sfont = fluid_sfloader_load(loader, filename);
|
|
|
|
|
|
|
|
if (sfont != NULL) {
|
|
|
|
sfont->id = id;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
sfont_info = new_fluid_sfont_info (synth, sfont);
|
|
|
|
|
|
|
|
if (!sfont_info)
|
|
|
|
{
|
|
|
|
delete_fluid_sfont (sfont_info->sfont); /* FIXME - Shouldn't fail right? - JG */
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */
|
|
|
|
synth->sfont_info = fluid_list_insert_at(synth->sfont_info, index, sfont_info); /* insert the sfont at the same index */
|
|
|
|
fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
/* reset the presets for all channels */
|
|
|
|
fluid_synth_update_presets(synth);
|
|
|
|
|
|
|
|
return sfont->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
* @return New assigned SoundFont ID or FLUID_FAILED on error
|
2004-05-07 09:53:39 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
|
2004-05-07 09:53:39 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
unsigned int sfont_id;
|
2004-05-07 09:53:39 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (sfont != NULL, FLUID_FAILED);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
sfont_info = new_fluid_sfont_info (synth, sfont);
|
|
|
|
if (!sfont_info) return (FLUID_FAILED);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont_id and sfont_info list */
|
|
|
|
sfont->id = sfont_id = ++synth->sfont_id;
|
|
|
|
synth->sfont_info = fluid_list_prepend (synth->sfont_info, sfont_info); /* prepend to list */
|
|
|
|
fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2004-05-07 09:53:39 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* reset the presets for all channels */
|
|
|
|
fluid_synth_program_reset (synth);
|
2004-05-07 09:53:39 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return sfont_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a SoundFont from the SoundFont stack without deleting it.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param sfont SoundFont to remove
|
|
|
|
*
|
|
|
|
* SoundFont is not freed and is left as the responsibility of the caller.
|
|
|
|
*
|
|
|
|
* NOTE: The SoundFont should only be freed after there are no presets
|
|
|
|
* referencing it. This can only be ensured by the SoundFont loader and
|
|
|
|
* therefore this function should not normally be used.
|
2004-05-07 09:53:39 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
void
|
|
|
|
fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
|
2004-05-07 09:53:39 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
|
|
|
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
fluid_return_if_fail (sfont != NULL);
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* remove the SoundFont from the list */
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_info list */
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t*) fluid_list_get(list);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (sfont_info->sfont == sfont)
|
|
|
|
{
|
|
|
|
synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
|
|
|
|
|
|
|
|
/* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */
|
|
|
|
fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-05-07 09:53:39 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2004-05-07 09:53:39 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* reset the presets for all channels */
|
|
|
|
fluid_synth_program_reset (synth);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count number of loaded SoundFont files.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Count of loaded SoundFont files.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_sfcount(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
int count;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */
|
|
|
|
count = fluid_list_size (synth->sfont_info);
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return count;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
2003-03-11 16:56:45 +00:00
|
|
|
*
|
2009-09-22 07:04:07 +00:00
|
|
|
* NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
|
|
|
|
* the duration of use of the returned pointer.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t *
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t *sfont = NULL;
|
|
|
|
fluid_list_t *list;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */
|
|
|
|
list = fluid_list_nth (synth->sfont_info, num);
|
|
|
|
if (list) sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return sfont;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get SoundFont by ID.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param id SoundFont ID
|
|
|
|
* @return SoundFont instance or NULL if invalid ID
|
2003-03-11 16:56:45 +00:00
|
|
|
*
|
2009-09-22 07:04:07 +00:00
|
|
|
* NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
|
|
|
|
* the duration of use of the returned pointer.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t *
|
|
|
|
fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t* sfont = NULL;
|
|
|
|
fluid_list_t* list;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */
|
|
|
|
|
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
|
|
|
|
if (fluid_sfont_get_id (sfont) == id)
|
|
|
|
break;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return list ? sfont : NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get SoundFont by name.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of SoundFont
|
|
|
|
* @return SoundFont instance or NULL if invalid name
|
2004-02-28 13:20:07 +00:00
|
|
|
*
|
2009-09-22 07:04:07 +00:00
|
|
|
* NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
|
|
|
|
* the duration of use of the returned pointer.
|
2004-02-28 13:20:07 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t *
|
|
|
|
fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name)
|
2004-02-28 13:20:07 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_t* sfont = NULL;
|
|
|
|
fluid_list_t* list;
|
2004-02-28 13:20:07 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (name != NULL, NULL);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock */
|
|
|
|
|
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
|
|
|
|
if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0)
|
|
|
|
break;
|
2004-02-28 13:20:07 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
return list ? sfont : NULL;
|
2004-02-28 13:20:07 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get active preset on a MIDI channel.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @return Preset or NULL if no preset active on channel
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from within synthesis thread, which includes
|
|
|
|
* SoundFont loader preset noteon methods.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_preset_t *
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return fluid_channel_get_preset(synth->channel[chan]);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get list of voices.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param buf Array to store voices to (NULL terminated if not filled completely)
|
|
|
|
* @param bufsize Count of indexes in buf
|
|
|
|
* @param id Voice ID to search for or < 0 to return list of all playing voices
|
|
|
|
*
|
|
|
|
* NOTE: Should only be called from within synthesis thread, which includes
|
|
|
|
* SoundFont loader preset noteon methods. Voices are only guaranteed to remain
|
|
|
|
* unchanged until next synthesis process iteration.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
void
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize,
|
|
|
|
int id)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int count = 0;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
fluid_return_if_fail (buf != NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < synth->polyphony && count < bufsize; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_voice_t* voice = synth->voice[i];
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (_PLAYING(voice) && (id < 0 || (int)voice->id == id))
|
2003-03-11 16:56:45 +00:00
|
|
|
buf[count++] = voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count < bufsize) buf[count] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable reverb effect.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param on TRUE to enable reverb, FALSE to disable
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fluid_synth_set_reverb_on(fluid_synth_t* synth, int on)
|
|
|
|
{
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
|
|
|
|
fluid_atomic_int_set (&synth->with_reverb, on != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Activate a reverb preset.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param num Reverb preset number
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*
|
|
|
|
* NOTE: Currently private to libfluidsynth.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while (revmodel_preset[i].name != NULL) {
|
|
|
|
if (i == num) {
|
|
|
|
fluid_synth_set_reverb (synth, revmodel_preset[i].roomsize,
|
|
|
|
revmodel_preset[i].damp, revmodel_preset[i].width,
|
|
|
|
revmodel_preset[i].level);
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
i++;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set reverb parameters.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @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)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping,
|
|
|
|
double width, double level)
|
|
|
|
{
|
|
|
|
fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ALL,
|
|
|
|
roomsize, damping, width, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize,
|
|
|
|
double damping, double width, double level)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (!(set & FLUID_REVMODEL_SET_ALL))
|
|
|
|
set = FLUID_REVMODEL_SET_ALL;
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
{
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_REVERB;
|
|
|
|
event->reverb.set = set;
|
|
|
|
event->reverb.roomsize = roomsize;
|
|
|
|
event->reverb.damping = damping;
|
|
|
|
event->reverb.width = width;
|
|
|
|
event->reverb.level = level;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
else return fluid_synth_set_reverb_LOCAL (synth, set, roomsize, damping, width, level);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread reverb set function */
|
|
|
|
static int
|
|
|
|
fluid_synth_set_reverb_LOCAL(fluid_synth_t* synth, int set, double roomsize,
|
|
|
|
double damping, double width, double level)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
fluid_revmodel_set (synth->reverb, set, roomsize, damping, width, level);
|
|
|
|
|
|
|
|
/* Send return reverb event to sync synth's copy of reverb parameters */
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (synth->return_queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synth return event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_REVERB;
|
|
|
|
event->reverb.set = set;
|
|
|
|
event->reverb.roomsize = roomsize;
|
|
|
|
event->reverb.damping = damping;
|
|
|
|
event->reverb.width = width;
|
|
|
|
event->reverb.level = level;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (synth->return_queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get reverb room size.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Reverb room size (0.0-1.2)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_reverb_roomsize(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */
|
|
|
|
value = synth->reverb_roomsize;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get reverb damping.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Reverb damping value (0.0-1.0)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_reverb_damp(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */
|
|
|
|
value = synth->reverb_damping;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get reverb level.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Reverb level value (0.0-1.0)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_reverb_level(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */
|
|
|
|
value = synth->reverb_level;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get reverb width.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Reverb width value (0.0-100.0)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_reverb_width(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */
|
|
|
|
value = synth->reverb_width;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable chorus effect.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param on TRUE to enable chorus, FALSE to disable
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fluid_synth_set_chorus_on(fluid_synth_t* synth, int on)
|
|
|
|
{
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
|
|
|
|
fluid_atomic_int_set (&synth->with_chorus, on != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set chorus parameters.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param nr Chorus voice count (0-99, CPU time consumption proportional to
|
|
|
|
* this value)
|
|
|
|
* @param level Chorus level (0.0-1.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 96KHz)
|
|
|
|
* @param type Chorus waveform type (#fluid_chorus_mod)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
|
|
|
|
double speed, double depth_ms, int type)
|
|
|
|
{
|
|
|
|
fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed,
|
|
|
|
depth_ms, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-1.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 96KHz)
|
|
|
|
* @param type Chorus waveform type (#fluid_chorus_mod)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level,
|
|
|
|
double speed, double depth_ms, int type)
|
|
|
|
{
|
|
|
|
fluid_event_queue_t *queue;
|
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (!(set & FLUID_CHORUS_SET_ALL))
|
|
|
|
set = FLUID_CHORUS_SET_ALL;
|
|
|
|
|
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
{
|
|
|
|
queue = fluid_synth_get_event_queue (synth);
|
|
|
|
if (!queue) return FLUID_FAILED;
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synthesis event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_CHORUS;
|
|
|
|
event->chorus.set = set;
|
|
|
|
event->chorus.nr = nr;
|
|
|
|
event->chorus.type = type;
|
|
|
|
event->chorus.level = level;
|
|
|
|
event->chorus.speed = speed;
|
|
|
|
event->chorus.depth = depth_ms;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (queue);
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
else return fluid_synth_set_chorus_LOCAL (synth, set, nr, level, speed, depth_ms, type);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread version of set chorus function */
|
|
|
|
static int
|
|
|
|
fluid_synth_set_chorus_LOCAL(fluid_synth_t* synth, int set, int nr, float level,
|
|
|
|
float speed, float depth_ms, int type)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_event_queue_elem_t *event;
|
|
|
|
|
|
|
|
fluid_chorus_set (synth->chorus, set, nr, level, speed, depth_ms, type);
|
|
|
|
|
|
|
|
/* Send return chorus event to sync synth's copy of chorus parameters */
|
|
|
|
|
|
|
|
event = fluid_event_queue_get_inptr (synth->return_queue);
|
|
|
|
if (!event)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "Synth return event queue full");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->type = FLUID_EVENT_QUEUE_ELEM_CHORUS;
|
|
|
|
event->chorus.set = set;
|
|
|
|
event->chorus.nr = nr;
|
|
|
|
event->chorus.type = type;
|
|
|
|
event->chorus.level = level;
|
|
|
|
event->chorus.speed = speed;
|
|
|
|
event->chorus.depth = depth_ms;
|
|
|
|
|
|
|
|
fluid_event_queue_next_inptr (synth->return_queue);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get chorus voice number (delay line count) value.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Chorus voice count (0-99)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_get_chorus_nr(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
int value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */
|
|
|
|
value = synth->chorus_nr;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get chorus level.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Chorus level value (0.0-10.0)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_chorus_level(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */
|
|
|
|
value = synth->chorus_level;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get chorus speed in Hz.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Chorus speed in Hz (0.29-5.0)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */
|
|
|
|
value = synth->chorus_speed;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get chorus depth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Chorus depth
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
double value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */
|
|
|
|
value = synth->chorus_depth;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get chorus waveform type.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Chorus waveform type (#fluid_chorus_mod)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_get_chorus_type(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
int value;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, 0.0);
|
|
|
|
|
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */
|
|
|
|
value = synth->chorus_type;
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- Unlock */
|
|
|
|
|
|
|
|
return value;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/*
|
2003-03-11 16:56:45 +00: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...
|
|
|
|
*/
|
2009-06-07 05:16:47 +00:00
|
|
|
static void
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan,
|
|
|
|
int key)
|
2009-06-07 05:16:47 +00:00
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
int i;
|
|
|
|
fluid_voice_t* voice;
|
2004-03-29 10:05:18 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2003-03-11 16:56:45 +00:00
|
|
|
voice = synth->voice[i];
|
2007-03-04 16:45:05 +00:00
|
|
|
if (_PLAYING(voice)
|
|
|
|
&& (voice->chan == chan)
|
|
|
|
&& (voice->key == key)
|
2003-03-11 16:56:45 +00:00
|
|
|
&& (fluid_voice_get_id(voice) != synth->noteid)) {
|
|
|
|
fluid_voice_noteoff(voice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method)
|
|
|
|
{
|
2003-03-11 16:56:45 +00:00
|
|
|
int i;
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
|
|
|
|
if (synth->channel[0] == NULL) {
|
|
|
|
FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!");
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
for (i = 0; i < synth->midi_channels; i++) {
|
2009-09-22 07:04:07 +00:00
|
|
|
if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_channel_set_interp_method(synth->channel[i], interp_method);
|
2009-09-22 07:04:07 +00:00
|
|
|
}
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
};
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get the total count of MIDI channels.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Count of MIDI channels
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_count_midi_channels(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->midi_channels;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get the total count of audio channels.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Count of audio channels
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_count_audio_channels(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->audio_channels;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get the total number of allocated audio channels. Usually identical to the
|
|
|
|
* number of audio channels.
|
|
|
|
*
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Count of allocated audio channels
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_count_audio_groups(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->audio_groups;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get the total number of allocated effects channels.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Count of allocated effects channels
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_count_effects_channels(fluid_synth_t* synth)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->effects_channels;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get the synth CPU load value.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @return Estimated CPU load value in percent (0-100)
|
|
|
|
*/
|
|
|
|
double
|
|
|
|
fluid_synth_get_cpu_load(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->cpu_load;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Get tuning for a given bank:program */
|
|
|
|
static fluid_tuning_t *
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL);
|
|
|
|
fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if ((synth->tuning == NULL) ||
|
|
|
|
(synth->tuning[bank] == NULL) ||
|
|
|
|
(synth->tuning[bank][prog] == NULL)) {
|
|
|
|
FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog);
|
2007-03-04 16:45:05 +00:00
|
|
|
return NULL;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->tuning[bank][prog];
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Create tuning for a given bank:program */
|
2007-03-04 16:45:05 +00:00
|
|
|
static fluid_tuning_t*
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL);
|
|
|
|
fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
if (synth->tuning == NULL) {
|
|
|
|
synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128);
|
|
|
|
if (synth->tuning == NULL) {
|
|
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (synth->tuning[bank] == NULL) {
|
|
|
|
synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128);
|
|
|
|
if (synth->tuning[bank] == NULL) {
|
|
|
|
FLUID_LOG(FLUID_PANIC, "Out of memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (synth->tuning[bank][prog] == NULL) {
|
|
|
|
synth->tuning[bank][prog] = new_fluid_tuning(name, bank, prog);
|
|
|
|
if (synth->tuning[bank][prog] == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
if ((fluid_tuning_get_name(synth->tuning[bank][prog]) == NULL)
|
2003-03-11 16:56:45 +00:00
|
|
|
|| (FLUID_STRCMP(fluid_tuning_get_name(synth->tuning[bank][prog]), name) != 0)) {
|
|
|
|
fluid_tuning_set_name(synth->tuning[bank][prog], name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return synth->tuning[bank][prog];
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Set the tuning of the entire MIDI note scale.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param bank MIDI bank number for this tuning (0-16383)
|
|
|
|
* @param prog MIDI program number for this tuning (0-127)
|
|
|
|
* @param name Label name for this tuning
|
|
|
|
* @param pitch Array of pitch values (length of 128, each value is number of
|
|
|
|
* cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc).
|
|
|
|
* Pass NULL to create a well-tempered (normal) scale.
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog,
|
|
|
|
char* name, double* pitch)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name);
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (tuning == NULL) return FLUID_FAILED;
|
|
|
|
if (pitch) fluid_tuning_set_all(tuning, pitch);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Apply an octave tuning to every octave in the MIDI note scale.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param bank MIDI bank number for this tuning (0-16383)
|
|
|
|
* @param prog MIDI program number for this tuning (0-127)
|
|
|
|
* @param name Label name for this tuning
|
|
|
|
* @param pitch Array of pitch values (length of 12 for each note of an octave
|
|
|
|
* starting at note C, values are number of offset cents to add to the normal
|
|
|
|
* tuning amount)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog,
|
|
|
|
char* name, double* pitch)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name);
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (tuning == NULL) return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_tuning_set_octave(tuning, pitch);
|
2009-09-22 07:04:07 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Set tuning values for one or more MIDI notes for an existing tuning.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param bank MIDI bank number for this tuning (0-16383)
|
|
|
|
* @param prog MIDI program number for this tuning (0-127)
|
|
|
|
* @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 Not used currently, may be used in the future to apply tuning
|
|
|
|
* change in realtime.
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog,
|
|
|
|
int len, int *key, double* pitch, int apply)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog);
|
|
|
|
int i;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (tuning == NULL) return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (i = 0; i < len; i++)
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_tuning_set_pitch(tuning, key[i], pitch[i]);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Activate an existing tuning scale on a MIDI channel.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param bank MIDI bank number of tuning
|
|
|
|
* @param prog MIDI program number of tuning
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog);
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (tuning == NULL) return FLUID_FAILED;
|
|
|
|
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]);
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Clear tuning scale on a MIDI channel (set it to the default well-tempered scale).
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_reset_tuning(fluid_synth_t* synth, int chan)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
fluid_channel_set_tuning(synth->channel[chan], NULL);
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Start tuning iteration.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fluid_synth_tuning_iteration_start(fluid_synth_t* synth)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
synth->cur_tuning = NULL;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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
|
|
|
|
fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
int b = 0, p = 0;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
|
|
|
fluid_return_val_if_fail (bank != NULL, 0);
|
|
|
|
fluid_return_val_if_fail (prog != NULL, 0);
|
|
|
|
|
|
|
|
if (synth->tuning == NULL) return 0;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
if (synth->cur_tuning != NULL) {
|
|
|
|
/* get the next program number */
|
|
|
|
b = fluid_tuning_get_bank(synth->cur_tuning);
|
|
|
|
p = 1 + fluid_tuning_get_prog(synth->cur_tuning);
|
|
|
|
if (p >= 128) {
|
|
|
|
p = 0;
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (b < 128) {
|
|
|
|
if (synth->tuning[b] != NULL) {
|
|
|
|
while (p < 128) {
|
|
|
|
if (synth->tuning[b][p] != NULL) {
|
|
|
|
synth->cur_tuning = synth->tuning[b][p];
|
|
|
|
*bank = b;
|
|
|
|
*prog = p;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = 0;
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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 on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog,
|
|
|
|
char* name, int len, double* pitch)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog);
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (tuning == NULL) return FLUID_FAILED;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
if (name) {
|
|
|
|
snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning));
|
|
|
|
name[len - 1] = 0; /* make sure the string is null terminated */
|
|
|
|
}
|
2009-09-22 07:04:07 +00:00
|
|
|
|
|
|
|
if (pitch)
|
2003-03-11 16:56:45 +00:00
|
|
|
FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double));
|
|
|
|
|
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, NULL);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return synth->settings;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to set a string setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param str Value to assign to the setting
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return fluid_settings_setstr(synth->settings, name, str);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to duplicate a string setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param str Location to store a pointer to the newly allocated string value
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*
|
|
|
|
* The returned string is owned by the caller and should be freed with free()
|
|
|
|
* when finished with it.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_dupstr(fluid_synth_t* synth, char* name, char** str)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (str != NULL, FLUID_FAILED);
|
|
|
|
|
|
|
|
return fluid_settings_dupstr(synth->settings, name, str);
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to set a floating point setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param val Value to assign to the setting
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_setnum(fluid_synth_t* synth, char* name, double val)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return fluid_settings_setnum(synth->settings, name, val);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to get a floating point setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param val Location to store the current value of the setting
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return fluid_settings_getnum(synth->settings, name, val);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to set an integer setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param val Value to assign to the setting
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_setint(fluid_synth_t* synth, char* name, int val)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return fluid_settings_setint(synth->settings, name, val);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Convenience function to get an integer setting of a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param name Name of setting parameter
|
|
|
|
* @param val Location to store the current value of the setting
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_getint(fluid_synth_t* synth, char* name, int* val)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
return fluid_settings_getint(synth->settings, name, val);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Set a SoundFont generator (effect) value on a MIDI channel in real-time.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param param SoundFont generator ID (#fluid_gen_type)
|
|
|
|
* @param value Offset generator value to assign to the MIDI channel
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*
|
|
|
|
* Parameter numbers and ranges are described in the SoundFont 2.01
|
|
|
|
* specification PDF, paragraph 8.1.3, page 48. See #fluid_gen_type.
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (param >= 0 && param < GEN_LAST, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return (fluid_synth_queue_gen_event (synth, chan, param, value, FALSE));
|
|
|
|
else fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Synthesis thread local set gen function */
|
|
|
|
static void
|
|
|
|
fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value,
|
|
|
|
int absolute)
|
|
|
|
{
|
|
|
|
fluid_voice_t* voice;
|
|
|
|
int i;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_set_gen (synth->channel[chan], param, value, absolute);
|
2004-03-03 11:14:25 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2004-03-03 11:14:25 +00:00
|
|
|
voice = synth->voice[i];
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (voice->chan == chan)
|
|
|
|
fluid_voice_set_param (voice, param, value, absolute);
|
|
|
|
}
|
2004-03-03 11:14:25 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Set a SoundFont generator (effect) value on a MIDI channel in real-time.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param chan MIDI channel number (0 to MIDI channel count - 1)
|
|
|
|
* @param param SoundFont generator ID (#fluid_gen_type)
|
|
|
|
* @param value Offset or absolute generator value to assign to the MIDI channel
|
|
|
|
* @param absolute 0 to assign a relative value, non-zero to assign an absolute value
|
|
|
|
* @param normalize 0 if value is specified in the native units of the generator,
|
|
|
|
* non-zero to take the value as a 0.0-1.0 range and apply it to the valid
|
|
|
|
* generator effect range (scaled and shifted as necessary).
|
|
|
|
* @return FLUID_OK on success, FLUID_FAILED otherwise
|
|
|
|
*
|
|
|
|
* 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.
|
2004-03-03 11:14:25 +00:00
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
|
|
|
fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param,
|
2004-03-03 11:14:25 +00:00
|
|
|
float value, int absolute, int normalized)
|
|
|
|
{
|
|
|
|
float v;
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (param >= 0 && param < GEN_LAST, FLUID_FAILED);
|
2004-03-03 11:14:25 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
v = normalized ? fluid_gen_scale(param, value) : value;
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return (fluid_synth_queue_gen_event (synth, chan, param, v, absolute));
|
|
|
|
else fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
2007-03-04 16:45:05 +00:00
|
|
|
return FLUID_OK;
|
2003-03-11 16:56:45 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00: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)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
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 (param >= 0 && param < GEN_LAST, FLUID_FAILED);
|
2003-03-11 16:56:45 +00:00
|
|
|
|
|
|
|
return fluid_channel_get_gen(synth->channel[chan], param);
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Assign a MIDI router to a synth.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param router MIDI router to assign to the synth
|
|
|
|
*
|
|
|
|
* NOTE: This should only be done once and prior to using the synth.
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
void
|
|
|
|
fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router)
|
|
|
|
{
|
|
|
|
fluid_return_if_fail (synth != NULL);
|
|
|
|
|
|
|
|
synth->midi_router = router;
|
2003-03-11 16:56:45 +00:00
|
|
|
};
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2003-03-11 16:56:45 +00:00
|
|
|
*/
|
2009-09-22 07:04:07 +00:00
|
|
|
int
|
|
|
|
fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event)
|
2003-03-11 16:56:45 +00:00
|
|
|
{
|
|
|
|
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:
|
2007-03-04 16:45:05 +00:00
|
|
|
return fluid_synth_noteon(synth, chan,
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_midi_event_get_key(event),
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_midi_event_get_velocity(event));
|
|
|
|
|
|
|
|
case NOTE_OFF:
|
|
|
|
return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event));
|
|
|
|
|
|
|
|
case CONTROL_CHANGE:
|
2007-03-04 16:45:05 +00:00
|
|
|
return fluid_synth_cc(synth, chan,
|
|
|
|
fluid_midi_event_get_control(event),
|
2003-03-11 16:56:45 +00:00
|
|
|
fluid_midi_event_get_value(event));
|
|
|
|
|
|
|
|
case PROGRAM_CHANGE:
|
|
|
|
return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event));
|
|
|
|
|
2009-01-22 16:47:21 +00:00
|
|
|
case CHANNEL_PRESSURE:
|
|
|
|
return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event));
|
|
|
|
|
2003-03-11 16:56:45 +00:00
|
|
|
case PITCH_BEND:
|
|
|
|
return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));
|
|
|
|
|
|
|
|
case MIDI_SYSTEM_RESET:
|
|
|
|
return fluid_synth_system_reset(synth);
|
|
|
|
}
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* 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 midi_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 midi_chan, int key, int vel)
|
2004-05-05 20:27:22 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_channel_t* channel;
|
2004-05-05 20:27:22 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (preset != NULL, FLUID_FAILED);
|
|
|
|
fluid_return_val_if_fail (midi_chan >= 0 && midi_chan < synth->midi_channels, 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_return_val_if_fail (fluid_synth_is_synth_thread (synth), FLUID_FAILED);
|
2004-05-05 20:27:22 +00:00
|
|
|
|
|
|
|
synth->storeid = id;
|
2009-09-22 07:04:07 +00:00
|
|
|
return fluid_preset_noteon (preset, synth, midi_chan, key, vel);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
2004-05-05 20:27:22 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_synth_should_queue (synth))
|
|
|
|
return fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, id);
|
2004-05-05 20:27:22 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_synth_stop_LOCAL (synth, id);
|
|
|
|
return FLUID_OK;
|
2004-05-05 20:27:22 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/* Local synthesis thread variant of fluid_synth_stop */
|
|
|
|
static void
|
|
|
|
fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id)
|
2004-05-05 20:27:22 +00:00
|
|
|
{
|
|
|
|
fluid_voice_t* voice;
|
2009-09-22 07:04:07 +00:00
|
|
|
int i;
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2005-06-08 03:51:13 +00:00
|
|
|
for (i = 0; i < synth->polyphony; i++) {
|
2004-05-05 20:27:22 +00:00
|
|
|
voice = synth->voice[i];
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (_ON(voice) && (fluid_voice_get_id (voice) == id))
|
2004-05-05 20:27:22 +00:00
|
|
|
fluid_voice_noteoff(voice);
|
|
|
|
}
|
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Offset the bank numbers of a loaded SoundFont.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param sfont_id ID of a loaded SoundFont
|
|
|
|
* @param offset Bank offset value to apply to all instruments
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset)
|
2004-08-18 15:11:47 +00:00
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */
|
2007-03-04 16:45:05 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
|
|
|
|
{
|
|
|
|
sfont_info->bankofs = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
if (!list)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
|
|
|
|
return FLUID_FAILED;
|
|
|
|
}
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
return FLUID_OK;
|
2004-08-18 15:11:47 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
/**
|
|
|
|
* Get bank offset of a loaded SoundFont.
|
|
|
|
* @param synth FluidSynth instance
|
|
|
|
* @param sfont_id ID of a loaded SoundFont
|
|
|
|
* @return SoundFont bank offset value
|
|
|
|
*/
|
2007-03-04 16:45:05 +00:00
|
|
|
int
|
2004-08-18 15:11:47 +00:00
|
|
|
fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id)
|
|
|
|
{
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_sfont_info_t *sfont_info;
|
|
|
|
fluid_list_t *list;
|
|
|
|
int offset;
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_return_val_if_fail (synth != NULL, 0);
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */
|
2004-08-18 15:11:47 +00:00
|
|
|
|
2009-09-22 07:04:07 +00:00
|
|
|
for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
|
|
|
|
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
|
|
|
|
|
|
|
if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
|
|
|
|
{
|
|
|
|
offset = sfont_info->bankofs;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_mutex_unlock (synth->mutex); /* -- unlock */
|
|
|
|
|
|
|
|
if (!list)
|
|
|
|
{
|
|
|
|
FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
2004-08-18 15:11:47 +00:00
|
|
|
}
|