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> 2004-03-30 Josh Green <jgreen@users.sourceforge.net>
* src/fluid_voice.c (fluid_voice_write): Altered filter turn-off * 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: In alphabetic order:
Paul Barton-Davis Paul Barton-Davis
Samuel Bianchini <biank@online.fr> Samuel Bianchini <biank at online dot fr>
Raoul Bonisch <jkl345@gmx.net> Raoul Bonisch <jkl345 at gmx dot net>
Jake Commander <jakec@ukfirst.co.uk> Jake Commander <jakec at ukfirst dot co dot uk>
Francois Dechelle <Francois.Dechelle@ircam.fr> Francois Dechelle <Francois dot Dechelle at ircam dot fr>
Tim Goetze <tim@quitte.de> Tim Goetze <tim at quitte dot de>
Anthony Green <green@redhat.com> Anthony Green <green at redhat dot com>
Josh Green <jgreen@users.sourceforge.net> Josh Green <jgreen at users dot sourceforge dot net>
Bob Ham <bob@ham.org> Bob Ham <bob at ham dot org>
Peter Hanappe <peter@hanappe.com> Peter Hanappe <peter at hanappe dot com>
Jezar <jezar@dreampoint.co.uk> Jezar <jezar at dreampoint dot co dot uk>
Fernando Pablo Lopez-Lezcano <nando@ccrma.Stanford.EDU> Fernando Pablo Lopez-Lezcano <nando at ccrma dot Stanford dot EDU>
Johnathan Lee <jlee@music.columbia.edu> Johnathan Lee <jlee at music dot columbia dot edu>
Stephane Letz <letz@grame.fr> Stephane Letz <letz at grame dot fr>
Juergen Mueller 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 Dave Phillips
Daniel Pressnitzer <pressnit@ircam.fr> Daniel Pressnitzer <pressnit at ircam dot fr>
Norbert Schnell <Norbert.Schnell@ircam.fr> Norbert Schnell <Norbert dot Schnell at ircam dot fr>
Joshua Scholar Joshua Scholar
Antoine Schmitt <as@gratin.org> Antoine Schmitt <as at gratin dot org>
Werner Schweer <ws@seh.de> Werner Schweer <ws at seh dot de>
Martin Uddén <nanook@lysator.liu.se> 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 - Filter on/off optimization causes clicks
---------------------------------
- Add support for "loop till release" instruments (currently broken)
- Get TCP server working for windows - Get TCP server working for windows
- The synth consumes too much CPU when no voices are playing. - The synth consumes too much CPU when no voices are playing.
- Multi-channel audio output - Add fluid_synth_remove_sfont()
- Filter on/off optimization causes clicks
- Phase sync'ed samples should start simultaneously.
Validation Validation
---------- ----------
@ -16,23 +14,12 @@ Validation
- Validation tests: create soundfont with basic wave forms [sine, - Validation tests: create soundfont with basic wave forms [sine,
square, triangle]; make test midi file; compare with SBLive output; square, triangle]; make test midi file; compare with SBLive output;
"regression" test "regression" test
- Validate reverb
- Validate chorus - Validate chorus
- compare performance with timidity - Analyse performance
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.
Documentation Documentation
------------- -------------
- Multi-channel audio output
- Write documention on tuning - Write documention on tuning
- fluid_synth_program_select2() with name of soundfont instead of font_id - fluid_synth_program_select2() with name of soundfont instead of font_id
- fluid_synth_set_gen2() - fluid_synth_set_gen2()
@ -72,7 +59,9 @@ Requests
- DirectSound 3D and EAX - DirectSound 3D and EAX
- Pause and resume the synthesizer/audio thread (run synthesizer as a daemon) - 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): set_gen: GEN_SAMPLEMODE (54):
Loop during release: 1, Loop during release: 1,
@ -86,8 +75,15 @@ Requests
Fluid 1.1: 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: SFLoader API:
- "interface" api
- redo sfloader api using "interface" api - redo sfloader api using "interface" api
Sample streaming 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); 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 * SoundFont management

View file

@ -138,11 +138,11 @@ static void* fluid_alsa_midi_run(void* d);
typedef struct { typedef struct {
fluid_midi_driver_t driver; fluid_midi_driver_t driver;
snd_seq_t *seq_handle; snd_seq_t *seq_handle;
int seq_port;
struct pollfd *pfd; struct pollfd *pfd;
int npfd; int npfd;
pthread_t thread; pthread_t thread;
int status; int status;
int port_count;
} fluid_alsa_seq_driver_t; } fluid_alsa_seq_driver_t;
fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings, 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); int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p);
static void* fluid_alsa_seq_run(void* d); static void* fluid_alsa_seq_run(void* d);
/************************************************************** /**************************************************************
* *
* Alsa audio driver * 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); 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 * new_fluid_alsa_seq_driver
*/ */
@ -843,6 +873,9 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
char* id; char* id;
char full_id[64]; char full_id[64];
char full_name[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 */ /* not much use doing anything */
if (handler == NULL) { if (handler == NULL) {
@ -857,33 +890,29 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
return NULL; return NULL;
} }
FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_seq_driver_t)); FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_seq_driver_t));
dev->seq_port = -1;
dev->driver.data = data; dev->driver.data = data;
dev->driver.handler = handler; dev->driver.handler = handler;
/* get the device name. if none is specified, use the default device. */ /* get the device name. if none is specified, use the default device. */
fluid_settings_getstr(settings, "midi.alsa_seq.device", &device); fluid_settings_getstr(settings, "midi.alsa_seq.device", &device);
if (device == NULL) { if (device == NULL) {
device = "default"; 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 */ /* open the sequencer INPUT only, non-blocking */
if ((err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
SND_SEQ_NONBLOCK)) < 0) { if (err < 0) {
FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer"); FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer");
goto error_recovery; 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 */ /* get # of MIDI file descriptors */
count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN); count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN);
if (count > 0) { /* make sure there are some */ if (count > 0) { /* make sure there are some */
@ -904,32 +933,47 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
} }
FLUID_FREE(pfd); 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 */ /* 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, /* create the ports */
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, snd_seq_port_info_alloca(&port_info);
SND_SEQ_PORT_TYPE_APPLICATION)) < 0) 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"); FLUID_LOG(FLUID_ERR, "Error creating ALSA sequencer port");
goto error_recovery; 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; dev->status = FLUID_MIDI_READY;
@ -1003,9 +1047,6 @@ delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p)
return FLUID_FAILED; return FLUID_FAILED;
} }
} }
if (dev->seq_port >= 0) {
snd_seq_delete_simple_port (dev->seq_handle, dev->seq_port);
}
if (dev->seq_handle) { if (dev->seq_handle) {
snd_seq_close(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; snd_seq_event_t *seq_ev;
fluid_midi_event_t evt; fluid_midi_event_t evt;
fluid_alsa_seq_driver_t* dev = (fluid_alsa_seq_driver_t*) d; 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 */ /* make sure the other threads can cancel this thread any time */
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) { if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
@ -1051,43 +1093,43 @@ fluid_alsa_seq_run(void* d)
{ {
case SND_SEQ_EVENT_NOTEON: case SND_SEQ_EVENT_NOTEON:
evt.type = NOTE_ON; 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.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity; evt.param2 = seq_ev->data.note.velocity;
break; break;
case SND_SEQ_EVENT_NOTEOFF: case SND_SEQ_EVENT_NOTEOFF:
evt.type = NOTE_OFF; 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.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity; evt.param2 = seq_ev->data.note.velocity;
break; break;
case SND_SEQ_EVENT_KEYPRESS: case SND_SEQ_EVENT_KEYPRESS:
evt.type = KEY_PRESSURE; 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.param1 = seq_ev->data.note.note;
evt.param2 = seq_ev->data.note.velocity; evt.param2 = seq_ev->data.note.velocity;
break; break;
case SND_SEQ_EVENT_CONTROLLER: case SND_SEQ_EVENT_CONTROLLER:
evt.type = CONTROL_CHANGE; 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.param1 = seq_ev->data.control.param;
evt.param2 = seq_ev->data.control.value; evt.param2 = seq_ev->data.control.value;
break; break;
case SND_SEQ_EVENT_PITCHBEND: case SND_SEQ_EVENT_PITCHBEND:
evt.type = PITCH_BEND; 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 */ /* ALSA pitch bend is -8192 - 8191, we adjust it here */
evt.param1 = seq_ev->data.control.value + 8192; evt.param1 = seq_ev->data.control.value + 8192;
break; break;
case SND_SEQ_EVENT_PGMCHANGE: case SND_SEQ_EVENT_PGMCHANGE:
evt.type = PROGRAM_CHANGE; 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; evt.param1 = seq_ev->data.control.value;
break; break;
case SND_SEQ_EVENT_CHANPRESS: case SND_SEQ_EVENT_CHANPRESS:
evt.type = CHANNEL_PRESSURE; 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; evt.param1 = seq_ev->data.control.value;
break; break;
default: default:

View file

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

View file

@ -114,6 +114,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 v1 = 0.0, v2 = 1.0;
fluid_real_t range1 = 127.0, range2 = 127.0; fluid_real_t range1 = 127.0, range2 = 127.0;
if (chan == NULL) {
return 0.0f;
}
/* 'special treatment' for default controller /* 'special treatment' for default controller
* *
* Reference: SF2.01 section 8.4.2 * Reference: SF2.01 section 8.4.2

View file

@ -352,7 +352,7 @@ new_fluid_synth(fluid_settings_t *settings)
} }
FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));
/* fluid_mutex_init(synth->busy); */ fluid_mutex_init(synth->busy);
synth->settings = settings; synth->settings = settings;
@ -375,14 +375,13 @@ new_fluid_synth(fluid_settings_t *settings)
(fluid_num_update_t) fluid_synth_update_gain, synth); (fluid_num_update_t) fluid_synth_update_gain, synth);
/* do some basic sanity checking on the settings */ /* 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. " if (synth->midi_channels % 16 != 0) {
"Changing this setting to 16."); int n = synth->midi_channels / 16;
synth->midi_channels = 16; synth->midi_channels = (n + 1) * 16;
} else if (synth->midi_channels > 1024) { fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels);
FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is too big (%d). " FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. "
"Limiting this setting to 1024.", synth->midi_channels); "I'll increase the number of channels to the next multiple.");
synth->midi_channels = 1024;
} }
if (synth->audio_channels < 1) { if (synth->audio_channels < 1) {
@ -695,7 +694,8 @@ delete_fluid_synth(fluid_synth_t* synth)
FLUID_FREE(synth->LADSPA_FxUnit); FLUID_FREE(synth->LADSPA_FxUnit);
#endif #endif
/* fluid_mutex_destroy(synth->busy); */ fluid_mutex_destroy(synth->busy);
FLUID_FREE(synth); FLUID_FREE(synth);
return FLUID_OK; return FLUID_OK;
@ -720,9 +720,7 @@ int
fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
{ {
fluid_channel_t* channel; fluid_channel_t* channel;
int r; int r = FLUID_FAILED;
/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
/* fluid_mutex_unlock(synth->busy); */
/* check the ranges of the arguments */ /* check the ranges of the arguments */
if ((chan < 0) || (chan >= synth->midi_channels)) { if ((chan < 0) || (chan >= synth->midi_channels)) {
@ -730,23 +728,11 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
return FLUID_FAILED; 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;
}
/* notes with velocity zero go to noteoff */ /* notes with velocity zero go to noteoff */
if (vel == 0) { if (vel == 0) {
return fluid_synth_noteoff(synth, chan, key); return fluid_synth_noteoff(synth, chan, key);
} }
synth->noteid++;
channel = synth->channel[chan]; channel = synth->channel[chan];
/* make sure this channel has a preset */ /* make sure this channel has a preset */
@ -761,9 +747,7 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
return FLUID_FAILED; return FLUID_FAILED;
} }
r = fluid_preset_noteon(channel->preset, synth, chan, key, vel); return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel);
return r;
} }
/* /*
@ -1868,8 +1852,9 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
voice = synth->voice[i]; voice = synth->voice[i];
/* safeguard against an available voice. */ /* safeguard against an available voice. */
if (_AVAILABLE(voice)) if (_AVAILABLE(voice)) {
return voice; return voice;
}
/* Determine, how 'important' a voice is. /* Determine, how 'important' a voice is.
* Start with an arbitrary number */ * Start with an arbitrary number */
@ -1883,12 +1868,14 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
*/ */
if (voice->chan == 9){ if (voice->chan == 9){
this_voice_prio += 4000; this_voice_prio += 4000;
} else if (_RELEASED(voice)){ } else if (_RELEASED(voice)){
/* The key for this voice has been released. Consider it much less important /* The key for this voice has been released. Consider it much less important
* than a voice, which is still held. * than a voice, which is still held.
*/ */
this_voice_prio -= 2000.; this_voice_prio -= 2000.;
}; }
if (_SUSTAINED(voice)){ if (_SUSTAINED(voice)){
/* The sustain pedal is held down on this channel. /* The sustain pedal is held down on this channel.
* Consider it less important than non-sustained channels. * Consider it less important than non-sustained channels.
@ -1897,7 +1884,7 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
* if we kill one voice. * 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. /* 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 * Otherwise hitting a chord may result in killing notes belonging to that very same
@ -1910,7 +1897,7 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth)
/* take a rough estimate of loudness into account. Louder voices are more important. */ /* take a rough estimate of loudness into account. Louder voices are more important. */
if (voice->volenv_section != FLUID_VOICE_ENVATTACK){ 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. */ /* check if this voice has less priority than the previous candidate. */
if (this_voice_prio < best_prio) 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; best_prio = this_voice_prio;
} }
if (best_voice_index < 0) if (best_voice_index < 0) {
return NULL; return NULL;
}
voice = synth->voice[best_voice_index]; voice = synth->voice[best_voice_index];
fluid_voice_off(voice); fluid_voice_off(voice);
return voice; return voice;
} }
@ -1934,6 +1923,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
{ {
int i, k; int i, k;
fluid_voice_t* voice = NULL; fluid_voice_t* voice = NULL;
fluid_channel_t* channel = NULL;
/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ /* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
/* fluid_mutex_unlock(synth->busy); */ /* fluid_mutex_unlock(synth->busy); */
@ -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", 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, (float) synth->ticks / 44100.0f,
(fluid_curtime() - synth->start) / 1000.0f, (fluid_curtime() - synth->start) / 1000.0f,
0.0f, 0.0f,
k); k);
} }
if (fluid_voice_init(voice, sample, synth->channel[chan], key, vel, if (chan >= 0) {
synth->noteid, synth->ticks, synth->gain) != FLUID_OK) { 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"); FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
return NULL; return NULL;
} }
@ -2881,3 +2875,55 @@ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event)
return FLUID_FAILED; 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
*/ */
enum fluid_loop { enum fluid_loop {
FLUID_UNLOOPED, FLUID_UNLOOPED = 0,
FLUID_LOOP_DURING_RELEASE, FLUID_LOOP_DURING_RELEASE = 1,
FLUID_NOTUSED, FLUID_NOTUSED = 2,
FLUID_LOOP FLUID_LOOP_UNTIL_RELEASE = 3
}; };
enum fluid_synth_status enum fluid_synth_status
@ -109,6 +109,7 @@ struct _fluid_synth_t
int nvoice; /** the length of the synthesis process array */ int nvoice; /** the length of the synthesis process array */
fluid_voice_t** voice; /** the synthesis processes */ fluid_voice_t** voice; /** the synthesis processes */
unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */ 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)*/ int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/
fluid_real_t** left_buf; fluid_real_t** left_buf;
@ -132,7 +133,7 @@ struct _fluid_synth_t
fluid_tuning_t* cur_tuning; /** current tuning in the iteration */ fluid_tuning_t* cur_tuning; /** current tuning in the iteration */
fluid_midi_router_t* midi_router; /* The midi router. Could be done nicer. */ 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 * Note: This simple scheme does -not- provide 100 % protection against
* thread problems, for example from MIDI thread and shell thread * thread problems, for example from MIDI thread and shell thread
*/ */

View file

@ -200,8 +200,8 @@ delete_fluid_voice(fluid_voice_t* voice)
*/ */
int int
fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
fluid_channel_t* channel, int key, int vel, fluid_channel_t* channel, int key, int vel, unsigned int id,
unsigned int id, unsigned int start_time, fluid_real_t gain) unsigned int start_time, fluid_real_t gain)
{ {
/* Note: The voice parameters will be initialized later, when the /* Note: The voice parameters will be initialized later, when the
* generators have been retrieved from the sound font. Here, only * 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->key = (unsigned char) key;
voice->vel = (unsigned char) vel; voice->vel = (unsigned char) vel;
voice->channel = channel; voice->channel = channel;
voice->preset = fluid_channel_get_preset(channel);
voice->mod_count = 0; voice->mod_count = 0;
voice->sample = sample; voice->sample = sample;
voice->start_time = start_time; 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 /* if filter has not yet started and filter cutoff and Q don't
exceed "audible" thresholds, then don't turn on the filter. exceed "audible" thresholds, then don't turn on the filter.
Once the filter is turned on, it remains on. */ Once the filter is turned on, it remains on. */
if (voice->filter_startup && fres > FLUID_MAX_AUDIBLE_FILTER_FC /* if (voice->filter_startup */
&& voice->q_lin < FLUID_MIN_AUDIBLE_FILTER_Q) /* && (fres > FLUID_MAX_AUDIBLE_FILTER_FC) */
dsp_use_filter_flag = 0; /* && (voice->q_lin < FLUID_MIN_AUDIBLE_FILTER_Q)) { */
else if (fres < 5) fres = 5; /* 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 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 /* The filter coefficients have to be recalculated (filter
* parameters have changed). Recalculation for various reasons is * parameters have changed). Recalculation for various reasons is
@ -744,7 +763,7 @@ fluid_voice_write(fluid_voice_t* voice,
* may require several runs. */ * may require several runs. */
fluid_check_fpe("voice_write DSP processing"); 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)) { || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* At which index does the loop point occur in the output buffer? /* 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; break;
case GEN_FILTERFC: case GEN_FILTERFC:
/* The resonance frequency is converted from absolute cents to midicents /* The resonance frequency is converted from absolute cents to
* .val and .mod are both used, this permits real-time modulation. * midicents .val and .mod are both used, this permits real-time
* The allowed range is tested in the 'fluid_ct2hz' function [PH,20021214] * modulation. The allowed range is tested in the 'fluid_ct2hz'
* function [PH,20021214]
*/ */
voice->fres = _GEN(voice, GEN_FILTERFC); 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; voice->last_fres = -1.0f;
break; break;
case GEN_FILTERQ: 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; q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
/* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
@ -1882,7 +1904,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
return; return;
} }
if ((_SAMPLEMODE(voice) == FLUID_LOOP) if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* Keep the loop start point within the sample data */ /* Keep the loop start point within the sample data */
if (voice->loopstart < min_index_loop){ if (voice->loopstart < min_index_loop){
@ -1929,7 +1951,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
/* Run startup specific code (only once, when the voice is started) */ /* Run startup specific code (only once, when the voice is started) */
if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
if (_SAMPLEMODE(voice) == FLUID_LOOP if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_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 /* Is this voice run in loop mode, or does it run straight to the
end of the waveform data? */ end of the waveform data? */
if (((_SAMPLEMODE(voice) == FLUID_LOOP) if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
&& (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* Yes, it will loop as soon as it reaches the loop point. In /* Yes, it will loop as soon as it reaches the loop point. In
* this case we must prevent, that the playback pointer (phase) * this case we must prevent, that the playback pointer (phase)

View file

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