ALSA sequencer driver opens several ports if midi channels bigger than 16.

Filter on/off optimization is deactivated.
This commit is contained in:
Peter Hanappe 2004-05-05 20:27:22 +00:00
parent 6bf2150ad7
commit e05b491a13
11 changed files with 326 additions and 176 deletions

View file

@ -1,3 +1,21 @@
2004-05-05 Peter Hanappe <peter@hanappe.com>
* src/fluid_alsa.c (new_fluid_alsa_seq_driver): The alsa driver
now opens several ports if the synthesizer is configured for more
than 16 MIDI channels.
* src/fluid_voice.c (fluid_voice_write): I removed the filter
on/off optimization. The filter is always on and serves as an
anti-aliasing filter.
2004-05-04 Peter Hanappe <peter@hanappe.com>
* src/fluid_synth.c (new_fluid_synth): The number of MIDI channels
now has to be a multiple of 16. The synth checks that this is the
case and changes the settings accordingly. I removed the sanity
checks for the min/max value of the number of MIDI channels since
this is already done by the settings object.
2004-03-30 Josh Green <jgreen@users.sourceforge.net>
* src/fluid_voice.c (fluid_voice_write): Altered filter turn-off

View file

@ -10,25 +10,27 @@ would simply not exist. Many thanks!
In alphabetic order:
Paul Barton-Davis
Samuel Bianchini <biank@online.fr>
Raoul Bonisch <jkl345@gmx.net>
Jake Commander <jakec@ukfirst.co.uk>
Francois Dechelle <Francois.Dechelle@ircam.fr>
Tim Goetze <tim@quitte.de>
Anthony Green <green@redhat.com>
Josh Green <jgreen@users.sourceforge.net>
Bob Ham <bob@ham.org>
Peter Hanappe <peter@hanappe.com>
Jezar <jezar@dreampoint.co.uk>
Fernando Pablo Lopez-Lezcano <nando@ccrma.Stanford.EDU>
Johnathan Lee <jlee@music.columbia.edu>
Stephane Letz <letz@grame.fr>
Samuel Bianchini <biank at online dot fr>
Raoul Bonisch <jkl345 at gmx dot net>
Jake Commander <jakec at ukfirst dot co dot uk>
Francois Dechelle <Francois dot Dechelle at ircam dot fr>
Tim Goetze <tim at quitte dot de>
Anthony Green <green at redhat dot com>
Josh Green <jgreen at users dot sourceforge dot net>
Bob Ham <bob at ham dot org>
Peter Hanappe <peter at hanappe dot com>
Jezar <jezar at dreampoint dot co dot uk>
Fernando Pablo Lopez-Lezcano <nando at ccrma dot Stanford dot EDU>
Johnathan Lee <jlee at music dot columbia dot edu>
Stephane Letz <letz at grame dot fr>
Juergen Mueller
Markus Nentwig <nentwig@users.sourceforge.net>
Markus Nentwig <nentwig at users dot sourceforge dot net>
David Olofson <david at olofson dot net>
Dave Phillips
Daniel Pressnitzer <pressnit@ircam.fr>
Norbert Schnell <Norbert.Schnell@ircam.fr>
Daniel Pressnitzer <pressnit at ircam dot fr>
Norbert Schnell <Norbert dot Schnell at ircam dot fr>
Joshua Scholar
Antoine Schmitt <as@gratin.org>
Werner Schweer <ws@seh.de>
Martin Uddén <nanook@lysator.liu.se>
Antoine Schmitt <as at gratin dot org>
Werner Schweer <ws at seh dot de>
Stephan Tassart <Stephan dot Tassart at st dot com>
Martin Uddén <nanook at lysator dot liu dot se>

View file

@ -1,14 +1,12 @@
Bugs and incomplete code
------------------------
Bugs, errors, and incomplete code
---------------------------------
- Add support for "loop till release" instruments (currently broken)
- Filter on/off optimization causes clicks
- Get TCP server working for windows
- The synth consumes too much CPU when no voices are playing.
- Multi-channel audio output
- Filter on/off optimization causes clicks
- Phase sync'ed samples should start simultaneously.
- Add fluid_synth_remove_sfont()
Validation
----------
@ -16,23 +14,12 @@ Validation
- Validation tests: create soundfont with basic wave forms [sine,
square, triangle]; make test midi file; compare with SBLive output;
"regression" test
- Validate reverb
- Validate chorus
- compare performance with timidity
JG:
> Its often hard to get the right Reverb/Chorus/Gain settings as
> well, so often I end up turning off Reverb and Chorus which can help.
JG:
> Well actually, it almost seems like every instrument is kind of flat. It
> just doesn't sound real nice.
- Analyse performance
Documentation
-------------
- Multi-channel audio output
- Write documention on tuning
- fluid_synth_program_select2() with name of soundfont instead of font_id
- fluid_synth_set_gen2()
@ -72,7 +59,9 @@ Requests
- DirectSound 3D and EAX
- Pause and resume the synthesizer/audio thread (run synthesizer as a daemon)
- set loop on/off on a sample (1 - with name sample, 2 - name sf and name preset) Use set_gen?
- set loop on/off on a sample (1 - with name sample, 2 - name sf and name preset)
Use set_gen?
set_gen: GEN_SAMPLEMODE (54):
Loop during release: 1,
@ -86,8 +75,15 @@ Requests
Fluid 1.1:
--------------------------------------------
Top of the list
- Use FIFOs to send events to the audio thread
- Redo sfloader api using "interface" api
- Clean multi-channel audio implementation
- 3D audio output
- Sample streaming, load/unload sample on demand
SFLoader API:
- "interface" api
- redo sfloader api using "interface" api
Sample streaming

View file

@ -149,6 +149,28 @@ FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth);
FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth);
/*
*
* Low level access
*
*/
/** Create and start voices using a preset. The id passed as
* argument will be used as the voice group id. */
FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id,
fluid_preset_t* preset, int audio_chan,
int midi_chan, int key, int vel);
/** Stop the voices in the voice group defined by id. */
FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id);
/** Change the value of a generator of the voices in the voice group
* defined by id. */
/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */
/* int gen, float value, */
/* int absolute, int normalized); */
/*
*
* SoundFont management

View file

@ -138,11 +138,11 @@ static void* fluid_alsa_midi_run(void* d);
typedef struct {
fluid_midi_driver_t driver;
snd_seq_t *seq_handle;
int seq_port;
struct pollfd *pfd;
int npfd;
pthread_t thread;
int status;
int port_count;
} fluid_alsa_seq_driver_t;
fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings,
@ -151,7 +151,6 @@ fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings,
int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p);
static void* fluid_alsa_seq_run(void* d);
/**************************************************************
*
* Alsa audio driver
@ -825,6 +824,37 @@ void fluid_alsa_seq_driver_settings(fluid_settings_t* settings)
fluid_settings_register_str(settings, "midi.alsa_seq.id", "pid", 0, NULL, NULL);
}
static char* fluid_alsa_seq_full_id(char* id, char* buf, int len)
{
if (id != NULL) {
if (FLUID_STRCMP(id, "pid") == 0) {
snprintf(buf, len, "FLUID Synth (%d)", getpid());
} else {
snprintf(buf, len, "FLUID Synth (%s)", id);
}
} else {
snprintf(buf, len, "FLUID Synth");
}
return buf;
}
static char* fluid_alsa_seq_full_name(char* id, int port, char* buf, int len)
{
if (id != NULL) {
if (FLUID_STRCMP(id, "pid") == 0) {
snprintf(buf, len, "Synth input port (%d:%d)", getpid(), port);
} else {
snprintf(buf, len, "Synth input port (%s:%d)", id, port);
}
} else {
snprintf(buf, len, "Synth input port");
}
return buf;
}
/*
* new_fluid_alsa_seq_driver
*/
@ -843,6 +873,9 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
char* id;
char full_id[64];
char full_name[64];
char id_pid[16];
snd_seq_port_info_t *port_info = NULL;
int midi_channels;
/* not much use doing anything */
if (handler == NULL) {
@ -857,33 +890,29 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
return NULL;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_seq_driver_t));
dev->seq_port = -1;
dev->driver.data = data;
dev->driver.handler = handler;
/* get the device name. if none is specified, use the default device. */
fluid_settings_getstr(settings, "midi.alsa_seq.device", &device);
if (device == NULL) {
device = "default";
}
fluid_settings_getstr(settings, "midi.alsa_seq.id", &id);
if (id == NULL) {
sprintf(id_pid, "%d", getpid());
id = id_pid;
}
/* open the sequencer INPUT only, non-blocking */
if ((err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT,
SND_SEQ_NONBLOCK)) < 0) {
err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
if (err < 0) {
FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer");
goto error_recovery;
}
/* tell the ladcca server our client id */
#ifdef HAVE_LADCCA
{
int enable_ladcca = 0;
fluid_settings_getint (settings, "ladcca.enable", &enable_ladcca);
if (enable_ladcca)
cca_alsa_client_id (fluid_cca_client, snd_seq_client_id (dev->seq_handle));
}
#endif /* HAVE_LADCCA */
/* get # of MIDI file descriptors */
count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN);
if (count > 0) { /* make sure there are some */
@ -903,34 +932,49 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
}
}
FLUID_FREE(pfd);
fluid_settings_getstr(settings, "midi.alsa_seq.id", &id);
if (id != NULL) {
if (FLUID_STRCMP(id, "pid") == 0) {
snprintf(full_id, 64, "FLUID Synth (%d)", getpid());
snprintf(full_name, 64, "Synth input port (%d)", getpid());
} else {
snprintf(full_id, 64, "FLUID Synth (%s)", id);
snprintf(full_name, 64, "Synth input port (%s)", id);
}
} else {
snprintf(full_id, 64, "FLUID Synth");
snprintf(full_name, 64, "Synth input port");
}
/* set the client name */
snd_seq_set_client_name (dev->seq_handle, full_id);
snd_seq_set_client_name(dev->seq_handle, fluid_alsa_seq_full_id(id, full_id, 64));
if ((dev->seq_port = snd_seq_create_simple_port (dev->seq_handle,
full_name,
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
{
/* create the ports */
snd_seq_port_info_alloca(&port_info);
FLUID_MEMSET(port_info, 0, snd_seq_port_info_sizeof());
fluid_settings_getint(settings, "synth.midi-channels", &midi_channels);
dev->port_count = midi_channels / 16;
snd_seq_port_info_set_capability(port_info,
SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE);
snd_seq_port_info_set_type(port_info,
SND_SEQ_PORT_TYPE_APPLICATION);
snd_seq_port_info_set_midi_channels(port_info, 16);
snd_seq_port_info_set_port_specified(port_info, 1);
for (i = 0; i < dev->port_count; i++) {
snd_seq_port_info_set_name(port_info, fluid_alsa_seq_full_name(id, i, full_name, 64));
snd_seq_port_info_set_port(port_info, i);
err = snd_seq_create_port(dev->seq_handle, port_info);
if (err < 0) {
FLUID_LOG(FLUID_ERR, "Error creating ALSA sequencer port");
goto error_recovery;
}
}
/* tell the ladcca server our client id */
#ifdef HAVE_LADCCA
{
int enable_ladcca = 0;
fluid_settings_getint (settings, "ladcca.enable", &enable_ladcca);
if (enable_ladcca)
cca_alsa_client_id (fluid_cca_client, snd_seq_client_id (dev->seq_handle));
}
#endif /* HAVE_LADCCA */
dev->status = FLUID_MIDI_READY;
@ -1003,9 +1047,6 @@ delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p)
return FLUID_FAILED;
}
}
if (dev->seq_port >= 0) {
snd_seq_delete_simple_port (dev->seq_handle, dev->seq_port);
}
if (dev->seq_handle) {
snd_seq_close(dev->seq_handle);
}
@ -1023,6 +1064,7 @@ fluid_alsa_seq_run(void* d)
snd_seq_event_t *seq_ev;
fluid_midi_event_t evt;
fluid_alsa_seq_driver_t* dev = (fluid_alsa_seq_driver_t*) d;
int channel;
/* make sure the other threads can cancel this thread any time */
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
@ -1051,43 +1093,43 @@ fluid_alsa_seq_run(void* d)
{
case SND_SEQ_EVENT_NOTEON:
evt.type = NOTE_ON;
evt.channel = seq_ev->data.note.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
evt.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity;
break;
case SND_SEQ_EVENT_NOTEOFF:
evt.type = NOTE_OFF;
evt.channel = seq_ev->data.note.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
evt.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity;
break;
case SND_SEQ_EVENT_KEYPRESS:
evt.type = KEY_PRESSURE;
evt.channel = seq_ev->data.note.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel;
evt.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity;
break;
case SND_SEQ_EVENT_CONTROLLER:
evt.type = CONTROL_CHANGE;
evt.channel = seq_ev->data.control.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
evt.param1 = seq_ev->data.control.param;
evt.param2 = seq_ev->data.control.value;
break;
case SND_SEQ_EVENT_PITCHBEND:
evt.type = PITCH_BEND;
evt.channel = seq_ev->data.control.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
/* ALSA pitch bend is -8192 - 8191, we adjust it here */
evt.param1 = seq_ev->data.control.value + 8192;
break;
case SND_SEQ_EVENT_PGMCHANGE:
evt.type = PROGRAM_CHANGE;
evt.channel = seq_ev->data.control.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
evt.param1 = seq_ev->data.control.value;
break;
case SND_SEQ_EVENT_CHANPRESS:
evt.type = CHANNEL_PRESSURE;
evt.channel = seq_ev->data.control.channel;
evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel;
evt.param1 = seq_ev->data.control.value;
break;
default:

View file

@ -318,8 +318,8 @@ fluid_channel_get_num(fluid_channel_t* chan)
*/
void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method)
{
chan->interp_method=new_method;
};
chan->interp_method = new_method;
}
/* Purpose:
* Returns the index of the interpolation method used on this channel,
@ -328,7 +328,7 @@ void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method)
int fluid_channel_get_interp_method(fluid_channel_t* chan)
{
return chan->interp_method;
};
}
unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan)
{

View file

@ -113,6 +113,10 @@ fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voic
{
fluid_real_t v1 = 0.0, v2 = 1.0;
fluid_real_t range1 = 127.0, range2 = 127.0;
if (chan == NULL) {
return 0.0f;
}
/* 'special treatment' for default controller
*

View file

@ -352,7 +352,7 @@ new_fluid_synth(fluid_settings_t *settings)
}
FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));
/* fluid_mutex_init(synth->busy); */
fluid_mutex_init(synth->busy);
synth->settings = settings;
@ -375,14 +375,13 @@ new_fluid_synth(fluid_settings_t *settings)
(fluid_num_update_t) fluid_synth_update_gain, synth);
/* do some basic sanity checking on the settings */
if (synth->midi_channels < 16) {
FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is smaller than 16. "
"Changing this setting to 16.");
synth->midi_channels = 16;
} else if (synth->midi_channels > 1024) {
FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is too big (%d). "
"Limiting this setting to 1024.", synth->midi_channels);
synth->midi_channels = 1024;
if (synth->midi_channels % 16 != 0) {
int n = synth->midi_channels / 16;
synth->midi_channels = (n + 1) * 16;
fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels);
FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. "
"I'll increase the number of channels to the next multiple.");
}
if (synth->audio_channels < 1) {
@ -695,7 +694,8 @@ delete_fluid_synth(fluid_synth_t* synth)
FLUID_FREE(synth->LADSPA_FxUnit);
#endif
/* fluid_mutex_destroy(synth->busy); */
fluid_mutex_destroy(synth->busy);
FLUID_FREE(synth);
return FLUID_OK;
@ -720,24 +720,12 @@ int
fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
{
fluid_channel_t* channel;
int r;
/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
/* fluid_mutex_unlock(synth->busy); */
int r = FLUID_FAILED;
/* check the ranges of the arguments */
if ((chan < 0) || (chan >= synth->midi_channels)) {
FLUID_LOG(FLUID_WARN, "Channel out of range");
return FLUID_FAILED;
}
if ((key < 0) || (key >= 128)) {
FLUID_LOG(FLUID_WARN, "Key out of range");
return FLUID_FAILED;
}
if ((vel < 0) || (vel >= 128)) {
FLUID_LOG(FLUID_WARN, "Velocity out of range");
return FLUID_FAILED;
return FLUID_FAILED;
}
/* notes with velocity zero go to noteoff */
@ -745,8 +733,6 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
return fluid_synth_noteoff(synth, chan, key);
}
synth->noteid++;
channel = synth->channel[chan];
/* make sure this channel has a preset */
@ -758,12 +744,10 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
(fluid_curtime() - synth->start) / 1000.0f,
0.0f, 0, "channel has no preset");
}
return FLUID_FAILED;
return FLUID_FAILED;
}
r = fluid_preset_noteon(channel->preset, synth, chan, key, vel);
return r;
return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel);
}
/*
@ -1852,10 +1836,10 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
* of the algorithm previously in fluid_synth_alloc_voice.
*/
fluid_voice_t*
fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
fluid_synth_free_voice_by_kill(fluid_synth_t* synth)
{
int i;
fluid_real_t best_prio=999999.;
fluid_real_t best_prio = 999999.;
fluid_real_t this_voice_prio;
fluid_voice_t* voice;
int best_voice_index=-1;
@ -1868,12 +1852,13 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
voice = synth->voice[i];
/* safeguard against an available voice. */
if (_AVAILABLE(voice))
if (_AVAILABLE(voice)) {
return voice;
}
/* Determine, how 'important' a voice is.
* Start with an arbitrary number */
this_voice_prio=10000.;
this_voice_prio = 10000.;
/* Is this voice on the drum channel?
* Then it is very important.
@ -1882,13 +1867,15 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
* of the time in release phase.
*/
if (voice->chan == 9){
this_voice_prio+=4000;
this_voice_prio += 4000;
} else if (_RELEASED(voice)){
/* The key for this voice has been released. Consider it much less important
* than a voice, which is still held.
*/
this_voice_prio-=2000.;
};
this_voice_prio -= 2000.;
}
if (_SUSTAINED(voice)){
/* The sustain pedal is held down on this channel.
* Consider it less important than non-sustained channels.
@ -1896,8 +1883,8 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
* is used to play 'more-voices-than-fingers', so it shouldn't hurt
* if we kill one voice.
*/
this_voice_prio-=1000;
};
this_voice_prio -= 1000;
}
/* 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
@ -1905,12 +1892,12 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
* So subtract the age of the voice from the priority - an older voice is just a little
* bit less important than a younger voice.
* This is a number between roughly 0 and 100.*/
this_voice_prio -= (synth->noteid-fluid_voice_get_id(voice));
this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
/* take a rough estimate of loudness into account. Louder voices are more important. */
if (voice->volenv_section != FLUID_VOICE_ENVATTACK){
this_voice_prio += voice->volenv_val*1000.;
};
this_voice_prio += voice->volenv_val * 1000.;
}
/* check if this voice has less priority than the previous candidate. */
if (this_voice_prio < best_prio)
@ -1918,11 +1905,13 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
best_prio = this_voice_prio;
}
if (best_voice_index < 0)
if (best_voice_index < 0) {
return NULL;
}
voice = synth->voice[best_voice_index];
fluid_voice_off (voice);
fluid_voice_off(voice);
return voice;
}
@ -1934,6 +1923,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
{
int i, k;
fluid_voice_t* voice = NULL;
fluid_channel_t* channel = NULL;
/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
/* fluid_mutex_unlock(synth->busy); */
@ -1952,7 +1942,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
/* No success yet? Then stop a running voice. */
if (voice == NULL) {
voice = fluid_synth_free_voice_by_kill (synth);
voice = fluid_synth_free_voice_by_kill(synth);
}
if (voice == NULL) {
@ -1969,15 +1959,19 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
}
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->noteid,
chan, key, vel, synth->storeid,
(float) synth->ticks / 44100.0f,
(fluid_curtime() - synth->start) / 1000.0f,
0.0f,
k);
}
if (fluid_voice_init(voice, sample, synth->channel[chan], key, vel,
synth->noteid, synth->ticks, synth->gain) != FLUID_OK) {
if (chan >= 0) {
channel = synth->channel[chan];
}
if (fluid_voice_init(voice, sample, channel, key, vel,
synth->storeid, synth->ticks, synth->gain) != FLUID_OK) {
FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
return NULL;
}
@ -2881,3 +2875,55 @@ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event)
return FLUID_FAILED;
}
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)
{
int r;
/* check the ranges of the arguments */
if ((midi_chan < 0) || (midi_chan >= synth->midi_channels)) {
FLUID_LOG(FLUID_WARN, "Channel out of range");
return FLUID_FAILED;
}
if ((key < 0) || (key >= 128)) {
FLUID_LOG(FLUID_WARN, "Key out of range");
return FLUID_FAILED;
}
if ((vel <= 0) || (vel >= 128)) {
FLUID_LOG(FLUID_WARN, "Velocity out of range");
return FLUID_FAILED;
}
fluid_mutex_lock(synth->busy); /* One at a time, please */
synth->storeid = id;
r = fluid_preset_noteon(preset, synth, midi_chan, key, vel);
fluid_mutex_unlock(synth->busy);
return r;
}
int fluid_synth_stop(fluid_synth_t* synth, unsigned int id)
{
int i;
fluid_voice_t* voice;
int status = FLUID_FAILED;
int count = 0;
for (i = 0; i < synth->nvoice; i++) {
voice = synth->voice[i];
if (_ON(voice) && (fluid_voice_get_id(voice) == id)) {
count++;
fluid_voice_noteoff(voice);
status = FLUID_OK;
}
}
return status;
}

View file

@ -59,10 +59,10 @@
* ENUM
*/
enum fluid_loop {
FLUID_UNLOOPED,
FLUID_LOOP_DURING_RELEASE,
FLUID_NOTUSED,
FLUID_LOOP
FLUID_UNLOOPED = 0,
FLUID_LOOP_DURING_RELEASE = 1,
FLUID_NOTUSED = 2,
FLUID_LOOP_UNTIL_RELEASE = 3
};
enum fluid_synth_status
@ -103,13 +103,14 @@ struct _fluid_synth_t
fluid_list_t* unloading; /** the soundfonts that need to be unloaded */
#endif
double gain; /** master gain */
double gain; /** master gain */
fluid_channel_t** channel; /** the channels */
int num_channels; /** the number of channels */
int nvoice; /** the length of the synthesis process array */
int num_channels; /** the number of channels */
int nvoice; /** the length of the synthesis process array */
fluid_voice_t** voice; /** the synthesis processes */
unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */
int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/
unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */
unsigned int storeid;
int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/
fluid_real_t** left_buf;
fluid_real_t** right_buf;
@ -132,7 +133,7 @@ struct _fluid_synth_t
fluid_tuning_t* cur_tuning; /** current tuning in the iteration */
fluid_midi_router_t* midi_router; /* The midi router. Could be done nicer. */
/*fluid_mutex_t busy;*/ /* Indicates, whether the audio thread is currently running.
fluid_mutex_t busy; /* Indicates, whether the audio thread is currently running.
* Note: This simple scheme does -not- provide 100 % protection against
* thread problems, for example from MIDI thread and shell thread
*/

View file

@ -200,8 +200,8 @@ delete_fluid_voice(fluid_voice_t* voice)
*/
int
fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
fluid_channel_t* channel, int key, int vel,
unsigned int id, unsigned int start_time, fluid_real_t gain)
fluid_channel_t* channel, int key, int vel, unsigned int id,
unsigned int start_time, fluid_real_t gain)
{
/* Note: The voice parameters will be initialized later, when the
* generators have been retrieved from the sound font. Here, only
@ -213,7 +213,6 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
voice->key = (unsigned char) key;
voice->vel = (unsigned char) vel;
voice->channel = channel;
voice->preset = fluid_channel_get_preset(channel);
voice->mod_count = 0;
voice->sample = sample;
voice->start_time = start_time;
@ -635,13 +634,33 @@ fluid_voice_write(fluid_voice_t* voice,
/* if filter has not yet started and filter cutoff and Q don't
exceed "audible" thresholds, then don't turn on the filter.
Once the filter is turned on, it remains on. */
if (voice->filter_startup && fres > FLUID_MAX_AUDIBLE_FILTER_FC
&& voice->q_lin < FLUID_MIN_AUDIBLE_FILTER_Q)
dsp_use_filter_flag = 0;
else if (fres < 5) fres = 5;
/* if (voice->filter_startup */
/* && (fres > FLUID_MAX_AUDIBLE_FILTER_FC) */
/* && (voice->q_lin < FLUID_MIN_AUDIBLE_FILTER_Q)) { */
/* dsp_use_filter_flag = 0; */
/* } else if (fres < 5) { */
/* fres = 5; */
/* } */
/* I removed the optimization of turning the filter off when the
* resonance frequence is above the maximum frequency. Instead, the
* filter frequence is set to a maximum of 0.45 times the sampling
* rate. For a 44100 kHz sampling rate, this amounts to 19845
* Hz. The reasing is that were problems with anti-aliasing when the
* synthesizer was run at lower sampling rates. Thanks to Stephan
* Tassart for pointing me to this bug. By turning the filter on and
* clipping the maximum filter frequency at 0.45*srate, the filter
* is used as an anti-aliasing filter. */
if (fres > 0.45f * voice->output_rate) {
fres = 0.45f * voice->output_rate;
} else if (fres < 5) {
fres = 5;
}
/* if filter enabled and there is a significant frequency change.. */
if (dsp_use_filter_flag && (abs(fres - voice->last_fres) > 0.01)) {
if (/*dsp_use_filter_flag &&*/ (abs(fres - voice->last_fres) > 0.01)) {
/* The filter coefficients have to be recalculated (filter
* parameters have changed). Recalculation for various reasons is
@ -744,7 +763,7 @@ fluid_voice_write(fluid_voice_t* voice,
* may require several runs. */
fluid_check_fpe("voice_write DSP processing");
if (((_SAMPLEMODE(voice) == FLUID_LOOP) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* At which index does the loop point occur in the output buffer?
@ -1149,18 +1168,21 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
break;
case GEN_FILTERFC:
/* The resonance frequency is converted from absolute cents to midicents
* .val and .mod are both used, this permits real-time modulation.
* The allowed range is tested in the 'fluid_ct2hz' function [PH,20021214]
/* The resonance frequency is converted from absolute cents to
* midicents .val and .mod are both used, this permits real-time
* modulation. The allowed range is tested in the 'fluid_ct2hz'
* function [PH,20021214]
*/
voice->fres = _GEN(voice, GEN_FILTERFC);
/* The synthesis loop will have to recalculate the filter coefficients. */
/* The synthesis loop will have to recalculate the filter
* coefficients. */
voice->last_fres = -1.0f;
break;
case GEN_FILTERQ:
/* The generator contains 'centibels' (1/10 dB) => divide by 10 to obtain dB */
/* The generator contains 'centibels' (1/10 dB) => divide by 10 to
* obtain dB */
q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
/* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
@ -1611,7 +1633,7 @@ fluid_voice_noteoff(fluid_voice_t* voice)
fluid_real_t env_value = - ((-200. * log (amp) / log (10.) - attn_and_lfo) / 960.0 - 1);
env_value = (env_value < 0.0 ? 0.0 : env_value);
env_value = (env_value > 1.0 ? 1.0 : env_value);
voice->volenv_val=env_value;
voice->volenv_val = env_value;
}
}
voice->volenv_section = FLUID_VOICE_ENVRELEASE;
@ -1793,8 +1815,8 @@ fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice
if ((mod->dest == GEN_ATTENUATION)
&& ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) {
fluid_real_t current_val=fluid_mod_get_value(mod, voice->channel, voice);
fluid_real_t v=fabs(mod->amount);
fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice);
fluid_real_t v = fabs(mod->amount);
if ((mod->src1 == FLUID_MOD_PITCHWHEEL)
|| (mod->flags1 & FLUID_MOD_BIPOLAR)
@ -1882,7 +1904,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
return;
}
if ((_SAMPLEMODE(voice) == FLUID_LOOP)
if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* Keep the loop start point within the sample data */
if (voice->loopstart < min_index_loop){
@ -1928,10 +1950,10 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
/* Run startup specific code (only once, when the voice is started) */
if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
if (max_index_loop-min_index_loop < FLUID_MIN_LOOP_SIZE){
if (_SAMPLEMODE(voice) == FLUID_LOOP
if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){
voice->gen[GEN_SAMPLEMODE].val=FLUID_UNLOOPED;
voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED;
}
}
@ -1942,8 +1964,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
/* Is this voice run in loop mode, or does it run straight to the
end of the waveform data? */
if (((_SAMPLEMODE(voice) == FLUID_LOOP)
&& (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* Yes, it will loop as soon as it reaches the loop point. In
* this case we must prevent, that the playback pointer (phase)
@ -1959,7 +1980,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
int index_in_sample = fluid_phase_index(voice->phase);
if (index_in_sample >= voice->loopend){
/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */
fluid_phase_set_int(voice->phase,voice->loopstart);
fluid_phase_set_int(voice->phase, voice->loopstart);
}
}
/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */

View file

@ -80,7 +80,6 @@ struct _fluid_voice_t
unsigned char key; /* the key, quick acces for noteoff */
unsigned char vel; /* the velocity */
fluid_channel_t* channel;
fluid_preset_t* preset;
fluid_gen_t gen[GEN_LAST];
fluid_mod_t mod[FLUID_NUM_MOD];
int mod_count;
@ -260,7 +259,6 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice);
#define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); }
#define fluid_voice_get_chan(_voice) (_voice)->chan
#define fluid_voice_get_preset(_voice) (_voice)->preset
#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED))