From 43a9923dffbae4d460aec670392c4447b72ddd07 Mon Sep 17 00:00:00 2001 From: Josh Green Date: Tue, 29 Sep 2009 21:40:28 +0000 Subject: [PATCH] Added public API functions: fluid_synth_sysex, fluid_synth_activate_key_tuning, fluid_synth_activate_octave_tuning, fluid_synth_tune_notes, fluid_synth_activate_tuning and fluid_synth_deactivate_tuning. Added audio.realtime, audio.realtime-prio, midi.realtime and midi.realtime-prio (only ALSA updated to use them at this point). Fixed bug in new_fluid_channel() where tuning field was not initialized. fluid_settings.h had wrong field order in prototypes for fluid_settings_register_num and fluid_settings_register_int. Added multi core support: "synth.cpu-cores" setting, fluid_synth_core_thread_func() and many core_ variables to fluid_synth_t. Switched fluid_mutex_t back to a normal non-recursive mutex and added fluid_rec_mutex_t. Added fluid_cond_t thread synchronization stuff. Added fluid_cond_mutex_t which is a dynamically allocated regular mutex for use with fluid_cond_t. fluid_settings_t and fluid_synth_t are now using fluid_rec_mutex_t (same as before, just name change). Added platform specific fluid_thread_self_set_prio() functions to fluid_sys.c for activating high priority for the calling thread. Modified new_fluid_thread() to take a prio and prio_level parameters. Added missing fluid_atomic_pointer_set(). fluid_voice_write() changed to only take a voice audio buffer to render to, mixing is done separately with new fluid_voice_mix(). fluid_voice_write() now returns the count of samples rendered. fluid_voice_effects() split into fluid_voice_filter() and fluid_voice_mix(). Added dsp_buf_count field to fluid_voice_t to keep track of last count of samples rendered to dsp_buf. fluid_voice_get_channel() converted to a macro. Added FLUID_DEFAULT_AUDIO_RT_PRIO and FLUID_DEFAULT_MIDI_RT_PRIO in fluidsynth_priv.h, set to 90 and 80 respectively which get used for audio.realtime-prio and midi.realtime-prio (was 90 and 90 before). --- fluidsynth/include/fluidsynth/synth.h | 29 +- fluidsynth/src/fluid_adriver.c | 6 + fluidsynth/src/fluid_alsa.c | 54 ++-- fluidsynth/src/fluid_chan.c | 1 + fluidsynth/src/fluid_cmd.c | 6 +- fluidsynth/src/fluid_hash.h | 2 +- fluidsynth/src/fluid_mdriver.c | 6 + fluidsynth/src/fluid_settings.c | 102 +++---- fluidsynth/src/fluid_settings.h | 11 +- fluidsynth/src/fluid_synth.c | 422 ++++++++++++++++++++------ fluidsynth/src/fluid_synth.h | 21 +- fluidsynth/src/fluid_sys.c | 96 +++++- fluidsynth/src/fluid_sys.h | 89 +++--- fluidsynth/src/fluid_voice.c | 197 ++++++------ fluidsynth/src/fluid_voice.h | 14 +- fluidsynth/src/fluidsynth_priv.h | 2 + 16 files changed, 727 insertions(+), 331 deletions(-) diff --git a/fluidsynth/include/fluidsynth/synth.h b/fluidsynth/include/fluidsynth/synth.h index a7d9f072..65c48b4d 100644 --- a/fluidsynth/include/fluidsynth/synth.h +++ b/fluidsynth/include/fluidsynth/synth.h @@ -56,6 +56,8 @@ FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, i FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); +FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, char *data, int len, + char *response, int *response_len, int *handled, int dryrun); FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); @@ -187,22 +189,33 @@ FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int par /* Tuning */ FLUIDSYNTH_API -int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - char* name, double* pitch); +int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch); +FLUIDSYNTH_API +int fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch, int apply); FLUIDSYNTH_API -int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - char* name, double* pitch); +int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch); +FLUIDSYNTH_API +int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch, int apply); FLUIDSYNTH_API -int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog, - int len, int *keys, double* pitch, int apply); +int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, + int len, int *keys, double* pitch, int apply); FLUIDSYNTH_API -int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog); +int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog); +FLUIDSYNTH_API +int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, + int apply); FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); +FLUIDSYNTH_API +int fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply); FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch); + char* name, int len, double* pitch); /* Misc */ diff --git a/fluidsynth/src/fluid_adriver.c b/fluidsynth/src/fluid_adriver.c index 37f65404..b9beca1f 100644 --- a/fluidsynth/src/fluid_adriver.c +++ b/fluidsynth/src/fluid_adriver.c @@ -221,6 +221,12 @@ void fluid_audio_driver_settings(fluid_settings_t* settings) fluid_settings_register_int(settings, "audio.periods", 16, 2, 64, 0, NULL, NULL); #endif + fluid_settings_register_str (settings, "audio.realtime", "yes", 0, NULL, NULL); + fluid_settings_add_option (settings, "audio.realtime", "yes"); + fluid_settings_add_option (settings, "audio.realtime", "no"); + fluid_settings_register_int (settings, "audio.realtime-prio", + FLUID_DEFAULT_AUDIO_RT_PRIO, 1, 99, 0, NULL, NULL); + /* Set the default driver */ #if JACK_SUPPORT fluid_settings_register_str(settings, "audio.driver", "jack", 0, NULL, NULL); diff --git a/fluidsynth/src/fluid_alsa.c b/fluidsynth/src/fluid_alsa.c index f7b581fc..77aeed74 100644 --- a/fluidsynth/src/fluid_alsa.c +++ b/fluidsynth/src/fluid_alsa.c @@ -49,11 +49,6 @@ #define BUFFER_LENGTH 512 -/* SCHED_FIFO priorities for ALSA threads (see pthread_attr_setschedparam) */ -#define ALSA_PCM_SCHED_PRIORITY 90 -#define ALSA_RAWMIDI_SCHED_PRIORITY 90 -#define ALSA_SEQ_SCHED_PRIORITY 90 - /** fluid_alsa_audio_driver_t * * This structure should not be accessed directly. Use audio port @@ -174,8 +169,9 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, int periods, period_size; char* device = NULL; pthread_attr_t attr; - int sched = SCHED_FIFO; + int sched; struct sched_param priority; + int realtime_prio = 0; int i, err, dir = 0; snd_pcm_hw_params_t* hwparams; snd_pcm_sw_params_t* swparams = NULL; @@ -192,7 +188,12 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); - fluid_settings_dupstr(settings, "audio.alsa.device", &device); /* ++ dup device name */ + fluid_settings_dupstr(settings, "audio.alsa.device", &device); /* ++ dup device name */ + fluid_settings_getint (settings, "audio.realtime-prio", &realtime_prio); + + if (fluid_settings_str_equal (settings, "audio.realtime", "yes")) + sched = SCHED_FIFO; + else sched = SCHED_OTHER; dev->data = data; dev->callback = func; @@ -297,22 +298,6 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, FLUID_LOG(FLUID_ERR, "Failed to set start threshold."); } - /* FIXME - Any of these calls important? One of them was causing massive - ALSA CPU consumption! */ - -// if (snd_pcm_sw_params_set_stop_threshold(dev->pcm, swparams, ~0u) != 0) { -// FLUID_LOG(FLUID_ERR, "Cannot turn off stop threshold."); -// } - -// if (snd_pcm_sw_params_set_silence_threshold(dev->pcm, swparams, 0) != 0) { -// FLUID_LOG(FLUID_ERR, "Cannot set 0 silence threshold."); -// } - -// if (snd_pcm_sw_params_set_silence_size(dev->pcm, swparams, 0) != 0) { -// FLUID_LOG(FLUID_ERR, "Cannot set 0 silence size."); -// } - -// if (snd_pcm_sw_params_set_avail_min(dev->pcm, swparams, period_size / 2) != 0) { if (snd_pcm_sw_params_set_avail_min(dev->pcm, swparams, period_size) != 0) { FLUID_LOG(FLUID_ERR, "Software setup for minimum available frames failed."); } @@ -348,7 +333,7 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, } /* SCHED_FIFO will not be active without setting the priority */ - priority.sched_priority = (sched == SCHED_FIFO) ? ALSA_PCM_SCHED_PRIORITY : 0; + priority.sched_priority = (sched == SCHED_FIFO) ? realtime_prio : 0; pthread_attr_setschedparam(&attr, &priority); err = pthread_create(&dev->thread, &attr, fluid_alsa_formats[i].run, (void*) dev); @@ -636,8 +621,9 @@ new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, int i, err; fluid_alsa_rawmidi_driver_t* dev; pthread_attr_t attr; - int sched = SCHED_FIFO; + int sched; struct sched_param priority; + int realtime_prio = 0; int count; struct pollfd *pfd = NULL; char* device = NULL; @@ -666,6 +652,12 @@ new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, goto error_recovery; } + fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio); + + if (fluid_settings_str_equal (settings, "midi.realtime", "yes")) + sched = SCHED_FIFO; + else sched = SCHED_OTHER; + /* get the device name. if none is specified, use the default device. */ fluid_settings_dupstr(settings, "midi.alsa.device", &device); /* ++ alloc device name */ @@ -734,7 +726,7 @@ new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, } /* SCHED_FIFO will not be active without setting the priority */ - priority.sched_priority = (sched == SCHED_FIFO) ? ALSA_RAWMIDI_SCHED_PRIORITY : 0; + priority.sched_priority = (sched == SCHED_FIFO) ? realtime_prio : 0; pthread_attr_setschedparam (&attr, &priority); err = pthread_create(&dev->thread, &attr, fluid_alsa_midi_run, (void*) dev); @@ -901,8 +893,9 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, int i, err; fluid_alsa_seq_driver_t* dev; pthread_attr_t attr; - int sched = SCHED_FIFO; + int sched; struct sched_param priority; + int realtime_prio = 0; int count; struct pollfd *pfd = NULL; char *device = NULL; @@ -929,6 +922,11 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, dev->driver.data = data; dev->driver.handler = handler; + fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio); + + if (fluid_settings_str_equal (settings, "midi.realtime", "yes")) + sched = SCHED_FIFO; + else sched = SCHED_OTHER; /* get the device name. if none is specified, use the default device. */ if (fluid_settings_dupstr(settings, "midi.alsa_seq.device", &device) == 0) /* ++ alloc device name */ @@ -1056,7 +1054,7 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, } /* SCHED_FIFO will not be active without setting the priority */ - priority.sched_priority = (sched == SCHED_FIFO) ? ALSA_SEQ_SCHED_PRIORITY : 0; + priority.sched_priority = (sched == SCHED_FIFO) ? realtime_prio : 0; pthread_attr_setschedparam (&attr, &priority); err = pthread_create(&dev->thread, &attr, fluid_alsa_seq_run, (void*) dev); diff --git a/fluidsynth/src/fluid_chan.c b/fluidsynth/src/fluid_chan.c index 2107e2a2..067d9f59 100644 --- a/fluidsynth/src/fluid_chan.c +++ b/fluidsynth/src/fluid_chan.c @@ -55,6 +55,7 @@ new_fluid_channel(fluid_synth_t* synth, int num) chan->synth = synth; chan->channum = num; chan->preset = NULL; + chan->tuning = NULL; fluid_channel_init(chan); fluid_channel_init_ctrl(chan, 0); diff --git a/fluidsynth/src/fluid_cmd.c b/fluidsynth/src/fluid_cmd.c index 97d66e2f..3017aace 100644 --- a/fluidsynth/src/fluid_cmd.c +++ b/fluidsynth/src/fluid_cmd.c @@ -223,7 +223,8 @@ fluid_shell_t* new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* fluid_shell_init(shell, settings, handler, in, out); if (thread) { - shell->thread = new_fluid_thread((fluid_thread_func_t) fluid_shell_run, shell, 1); + shell->thread = new_fluid_thread((fluid_thread_func_t) fluid_shell_run, shell, + FLUID_THREAD_PRIO_NORMAL, 0, TRUE); if (shell->thread == NULL) { delete_fluid_shell(shell); return NULL; @@ -1813,7 +1814,8 @@ new_fluid_client(fluid_server_t* server, fluid_settings_t* settings, client->settings = settings; client->handler = handler; - client->thread = new_fluid_thread((fluid_thread_func_t) fluid_client_run, client, 0); + client->thread = new_fluid_thread((fluid_thread_func_t) fluid_client_run, client, + FLUID_THREAD_PRIO_NORMAL, 0, FALSE); if (client->thread == NULL) { fluid_socket_close(sock); diff --git a/fluidsynth/src/fluid_hash.h b/fluidsynth/src/fluid_hash.h index 467d3d26..bf2b93a1 100644 --- a/fluidsynth/src/fluid_hash.h +++ b/fluidsynth/src/fluid_hash.h @@ -68,7 +68,7 @@ struct _fluid_hashtable_t volatile int ref_count; fluid_destroy_notify_t key_destroy_func; fluid_destroy_notify_t value_destroy_func; - fluid_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) + fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) }; struct _fluid_hashtable_iter_t diff --git a/fluidsynth/src/fluid_mdriver.c b/fluidsynth/src/fluid_mdriver.c index b1457802..e5f37f5a 100644 --- a/fluidsynth/src/fluid_mdriver.c +++ b/fluidsynth/src/fluid_mdriver.c @@ -145,6 +145,12 @@ void fluid_midi_driver_settings(fluid_settings_t* settings) { int i; + fluid_settings_register_str (settings, "midi.realtime", "yes", 0, NULL, NULL); + fluid_settings_add_option (settings, "midi.realtime", "yes"); + fluid_settings_add_option (settings, "midi.realtime", "no"); + fluid_settings_register_int (settings, "midi.realtime-prio", + FLUID_DEFAULT_MIDI_RT_PRIO, 1, 99, 0, NULL, NULL); + /* Set the default driver */ #if ALSA_SUPPORT fluid_settings_register_str(settings, "midi.driver", "alsa_seq", 0, NULL, NULL); diff --git a/fluidsynth/src/fluid_settings.c b/fluidsynth/src/fluid_settings.c index f2125d3d..5b2f6f0e 100644 --- a/fluidsynth/src/fluid_settings.c +++ b/fluidsynth/src/fluid_settings.c @@ -238,7 +238,7 @@ new_fluid_settings(void) fluid_settings_value_destroy_func); if (settings == NULL) return NULL; - fluid_mutex_init (settings->mutex); + fluid_rec_mutex_init (settings->mutex); fluid_settings_init(settings); return settings; } @@ -252,7 +252,7 @@ delete_fluid_settings(fluid_settings_t* settings) { fluid_return_if_fail (settings != NULL); - fluid_mutex_destroy (settings->mutex); + fluid_rec_mutex_destroy (settings->mutex); delete_fluid_hashtable(settings); } @@ -342,7 +342,7 @@ fluid_settings_get(fluid_settings_t* settings, char** name, int len, fluid_setting_node_t **value) { fluid_hashtable_t* table = settings; - fluid_setting_node_t *node; + fluid_setting_node_t *node = NULL; int n; for (n = 0; n < len; n++) { @@ -445,7 +445,7 @@ fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, i ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, tokens, ntokens, &node)) { setting = new_fluid_str_setting(def, def, hints, fun, data); @@ -466,7 +466,7 @@ fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, i } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -489,7 +489,7 @@ fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, tokens, ntokens, &node)) { /* insert a new setting */ @@ -515,7 +515,7 @@ fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -538,7 +538,7 @@ fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, tokens, ntokens, &node)) { /* insert a new setting */ @@ -564,7 +564,7 @@ fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -590,9 +590,9 @@ fluid_settings_get_type(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); type = fluid_settings_get (settings, tokens, ntokens, &node) ? node->type : FLUID_NO_TYPE; - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return (type); } @@ -618,7 +618,7 @@ fluid_settings_get_hints(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node)) { if (node->type == FLUID_NUM_TYPE) { @@ -630,7 +630,7 @@ fluid_settings_get_hints(fluid_settings_t* settings, char* name) } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return hints; } @@ -656,7 +656,7 @@ fluid_settings_is_realtime(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node)) { if (node->type == FLUID_NUM_TYPE) { @@ -668,7 +668,7 @@ fluid_settings_is_realtime(fluid_settings_t* settings, char* name) } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return isrealtime; } @@ -696,7 +696,7 @@ fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) ntokens = fluid_settings_tokenize (name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get (settings, tokens, ntokens, &node)) { if (node->type == FLUID_STR_TYPE) { @@ -717,7 +717,7 @@ fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) if (retval != 1) delete_fluid_str_setting (setting); } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -753,7 +753,7 @@ fluid_settings_copystr(fluid_settings_t* settings, char* name, char* str, int le ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -768,7 +768,7 @@ fluid_settings_copystr(fluid_settings_t* settings, char* name, char* str, int le retval = 1; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -799,7 +799,7 @@ fluid_settings_dupstr(fluid_settings_t* settings, char* name, char** str) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -814,7 +814,7 @@ fluid_settings_dupstr(fluid_settings_t* settings, char* name, char** str) if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -850,7 +850,7 @@ fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -860,7 +860,7 @@ fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) } else *str = NULL; - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -888,7 +888,7 @@ fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -896,7 +896,7 @@ fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -923,7 +923,7 @@ fluid_settings_getstr_default(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -931,7 +931,7 @@ fluid_settings_getstr_default(fluid_settings_t* settings, char* name) retval = setting->def; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -958,7 +958,7 @@ fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -968,7 +968,7 @@ fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) retval = 1; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -995,7 +995,7 @@ fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -1015,7 +1015,7 @@ fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1043,7 +1043,7 @@ fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node)) { if (node->type == FLUID_NUM_TYPE) { @@ -1067,7 +1067,7 @@ fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) if (retval != 1) delete_fluid_num_setting (setting); } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1095,7 +1095,7 @@ fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_NUM_TYPE)) { @@ -1104,7 +1104,7 @@ fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) retval = 1; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1132,7 +1132,7 @@ fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_NUM_TYPE)) { @@ -1141,7 +1141,7 @@ fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, *max = setting->max; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); } /** @@ -1165,7 +1165,7 @@ fluid_settings_getnum_default(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_NUM_TYPE)) { @@ -1173,7 +1173,7 @@ fluid_settings_getnum_default(fluid_settings_t* settings, char* name) retval = setting->def; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1201,7 +1201,7 @@ fluid_settings_setint(fluid_settings_t* settings, char* name, int val) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node)) { if (node->type == FLUID_INT_TYPE) { @@ -1225,7 +1225,7 @@ fluid_settings_setint(fluid_settings_t* settings, char* name, int val) if (retval != 1) delete_fluid_int_setting (setting); } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1253,7 +1253,7 @@ fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_INT_TYPE)) { @@ -1262,7 +1262,7 @@ fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) retval = 1; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1289,7 +1289,7 @@ fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, in ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_INT_TYPE)) { @@ -1298,7 +1298,7 @@ fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, in *max = setting->max; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); } /** @@ -1322,7 +1322,7 @@ fluid_settings_getint_default(fluid_settings_t* settings, char* name) ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_INT_TYPE)) { @@ -1330,7 +1330,7 @@ fluid_settings_getint_default(fluid_settings_t* settings, char* name) retval = setting->def; } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); return retval; } @@ -1359,7 +1359,7 @@ fluid_settings_foreach_option (fluid_settings_t* settings, char* name, void* dat ntokens = fluid_settings_tokenize(name, buf, tokens); - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, tokens, ntokens, &node) && (node->type == FLUID_STR_TYPE)) { @@ -1374,7 +1374,7 @@ fluid_settings_foreach_option (fluid_settings_t* settings, char* name, void* dat } } - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); } /* Structure passed to fluid_settings_foreach_iter recursive function */ @@ -1440,7 +1440,7 @@ fluid_settings_foreach(fluid_settings_t* settings, void* data, fluid_settings_fo bag.data = data; bag.path[0] = 0; - fluid_mutex_lock (settings->mutex); + fluid_rec_mutex_lock (settings->mutex); fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag); - fluid_mutex_unlock (settings->mutex); + fluid_rec_mutex_unlock (settings->mutex); } diff --git a/fluidsynth/src/fluid_settings.h b/fluidsynth/src/fluid_settings.h index b7944d38..00734a26 100644 --- a/fluidsynth/src/fluid_settings.h +++ b/fluidsynth/src/fluid_settings.h @@ -42,14 +42,15 @@ int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* de /** returns 0 if the value has been registered correctly, non-zero otherwise */ -int fluid_settings_register_num(fluid_settings_t* settings, char* name, double min, double max, - double def, int hints, fluid_num_update_t fun, void* data); - +int fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, + double min, double max, int hints, + fluid_num_update_t fun, void* data); /** returns 0 if the value has been registered correctly, non-zero otherwise */ -int fluid_settings_register_int(fluid_settings_t* settings, char* name, int min, int max, - int def, int hints, fluid_int_update_t fun, void* data); +int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, + int min, int max, int hints, + fluid_int_update_t fun, void* data); #endif /* _FLUID_SETTINGS_H */ diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 7bb66f8e..c679d0ae 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -93,6 +93,7 @@ static int fluid_synth_set_polyphony_LOCAL(fluid_synth_t* synth, int polyphony); 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); +static void fluid_synth_core_thread_func (void* data); 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); @@ -222,6 +223,7 @@ void fluid_synth_settings(fluid_settings_t* settings) 0, NULL, NULL); fluid_settings_register_int(settings, "synth.device-id", 0, 126, 0, 0, NULL, NULL); + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0, NULL, NULL); } /* @@ -517,7 +519,7 @@ new_fluid_synth(fluid_settings_t *settings) } FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); - fluid_mutex_init(synth->mutex); + fluid_rec_mutex_init(synth->mutex); fluid_private_init(synth->thread_queues); synth->return_queue = fluid_event_queue_new (FLUID_MAX_RETURN_EVENTS); @@ -542,6 +544,7 @@ new_fluid_synth(fluid_settings_t *settings) fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); fluid_settings_getnum(settings, "synth.gain", &synth->gain); fluid_settings_getint(settings, "synth.device-id", &synth->device_id); + fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); /* register the callbacks */ fluid_settings_register_num(settings, "synth.gain", @@ -729,6 +732,57 @@ new_fluid_synth(fluid_settings_t *settings) goto error_recovery; } + /* Initialize multi-core variables if multiple cores enabled */ + if (synth->cores > 1) + { + int prio, prio_level; + + synth->core_mutex = new_fluid_cond_mutex (); + synth->core_cond = new_fluid_cond (); + synth->core_wait_last_cond = new_fluid_cond (); + + synth->core_threads = FLUID_ARRAY (fluid_thread_t *, synth->cores - 1); + synth->core_voice_processed = FLUID_ARRAY (fluid_voice_t *, synth->polyphony); + synth->core_bufs = FLUID_MALLOC (synth->polyphony * FLUID_BUFSIZE * sizeof (fluid_real_t)); + + if (!synth->core_mutex || !synth->core_cond || !synth->core_wait_last_cond + || !synth->core_threads || !synth->core_voice_processed + || !synth->core_bufs) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + synth->cores_active = TRUE; + synth->core_work = FALSE; + synth->core_inprogress = 0; + synth->core_waiting_for_last = FALSE; + + for (i = 0; i < synth->polyphony; i++) + synth->core_voice_processed[i] = NULL; + + prio = fluid_settings_str_equal (synth->settings, "audio.realtime", "yes"); + + if (prio) + { + prio = FLUID_THREAD_PRIO_HIGH; + fluid_settings_getint (synth->settings, "audio.realtime-prio", &prio_level); + } + else + { + prio = FLUID_THREAD_PRIO_NORMAL; + prio_level = 0; + } + + for (i = 0; i < synth->cores - 1; i++) + { + synth->core_threads[i] = new_fluid_thread (fluid_synth_core_thread_func, + synth, prio, prio_level, FALSE); + if (!synth->core_threads[i]) + FLUID_LOG(FLUID_ERR, "Failed to create a synthesis core thread"); + } + } + /* FIXME */ synth->start = fluid_curtime(); @@ -755,26 +809,26 @@ fluid_synth_return_event_process_callback (void* data, unsigned int msec) switch (event->type) { case FLUID_EVENT_QUEUE_ELEM_GAIN: /* Sync gain variable */ - fluid_mutex_lock (synth->mutex); /* ++ Lock gain variable */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock gain variable */ synth->gain = event->dval; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_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 */ + fluid_rec_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 */ + fluid_rec_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 */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ break; case FLUID_EVENT_QUEUE_ELEM_FREE_PRESET: /* Preset free event */ preset = (fluid_preset_t *)(event->pval); @@ -823,6 +877,26 @@ delete_fluid_synth(fluid_synth_t* synth) fluid_event_queue_free(synth->return_queue); } + /* Free multi-core resources (if multi-core enabled) */ + if (synth->cores > 1) + { + /* Signal slave core threads to exit and wait for them to finish */ + fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock */ + synth->cores_active = FALSE; + fluid_cond_broadcast (synth->core_cond); + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock */ + + for (i = 0; i < synth->cores - 1; i++) + if (synth->core_threads[i]) + fluid_thread_join (synth->core_threads[i]); + + delete_fluid_cond_mutex (synth->core_mutex); + delete_fluid_cond (synth->core_cond); + delete_fluid_cond (synth->core_wait_last_cond); + FLUID_FREE (synth->core_voice_processed); + FLUID_FREE (synth->core_bufs); + } + /* turn off all voices, needed to unload SoundFont data */ if (synth->voice != NULL) { for (i = 0; i < synth->nvoice; i++) { @@ -959,7 +1033,7 @@ delete_fluid_synth(fluid_synth_t* synth) delete_fluid_list (synth->queue_pool); fluid_private_free (synth->thread_queues); - fluid_mutex_destroy(synth->mutex); + fluid_rec_mutex_destroy(synth->mutex); FLUID_FREE(synth); @@ -992,7 +1066,7 @@ fluid_synth_get_event_queue (fluid_synth_t* synth) if (!queue) /* This thread has no queue yet? */ { - fluid_mutex_lock (synth->mutex); /* ++ lock queue_pool */ + fluid_rec_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) @@ -1007,7 +1081,7 @@ fluid_synth_get_event_queue (fluid_synth_t* synth) delete1_fluid_list (p); } - fluid_mutex_unlock (synth->mutex); /* -- unlock queue_pool */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock queue_pool */ if (!queue) /* Create event queue, if one wasn't re-claimed */ { @@ -1180,9 +1254,9 @@ fluid_synth_thread_queue_destroy_notify (void *data) /* 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 */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock queue_pool */ } /** @@ -2101,7 +2175,7 @@ fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, fluid_sfont_info_t *sfont_info; fluid_list_t *list; - fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); @@ -2115,7 +2189,7 @@ fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return preset; } @@ -2134,7 +2208,7 @@ fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname fluid_sfont_info_t *sfont_info; fluid_list_t *list; - fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); @@ -2148,7 +2222,7 @@ fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return preset; } @@ -2167,7 +2241,7 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, fluid_list_t *list; int ofs; - fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); @@ -2181,7 +2255,7 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return preset; } @@ -2483,9 +2557,9 @@ fluid_synth_get_gain(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); + fluid_rec_mutex_lock (synth->mutex); gain = synth->gain; - fluid_mutex_unlock (synth->mutex); + fluid_rec_mutex_unlock (synth->mutex); return (gain); } @@ -2511,7 +2585,7 @@ 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); + fluid_return_val_if_fail (polyphony >= 16 && polyphony <= synth->nvoice, FLUID_FAILED); if (fluid_synth_should_queue (synth)) return fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_POLYPHONY, @@ -2923,7 +2997,8 @@ fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, static int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) { - int i, auchan; + fluid_real_t local_voice_buf[FLUID_BUFSIZE]; + int i, auchan, start_index, voice_index; fluid_voice_t* voice; fluid_real_t* left_buf; fluid_real_t* right_buf; @@ -2931,6 +3006,8 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) fluid_real_t* chorus_buf; int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); double prof_ref = fluid_profile_ref(); + double prof_ref_voice; + int count; /* Assign ID of synthesis thread, if not already set */ if (synth->synth_thread_id == FLUID_THREAD_ID_NULL) @@ -2970,12 +3047,99 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref); - /* call all playing synthesis processes */ - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; + if (synth->cores > 1) + { + /* Look for first active voice to process */ + for (voice_index = 0; voice_index < synth->polyphony; voice_index++) + { + voice = synth->voice[voice_index]; + if (_PLAYING (voice)) break; + } - if (_PLAYING(voice)) { - double prof_ref_voice = fluid_profile_ref(); + /* Was there a voice to process? */ + if (voice_index < synth->polyphony) + { + fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */ + + synth->core_voice_index = voice_index + 1; /* Set the next core_voice_index */ + + /* Tell the other core threads that there is work to do */ + synth->core_work = TRUE; + synth->core_waiting_for_last = FALSE; + fluid_cond_broadcast (synth->core_cond); + + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */ + + while (TRUE) + { +got_voice: /* We got a voice to process */ + count = fluid_voice_write (voice, &synth->core_bufs[voice_index * FLUID_BUFSIZE]); + + if (count > 0) synth->core_voice_processed[voice_index] = voice; + + /* Look for next active voice to process (in a lock free manner) */ + do + { + voice_index = fluid_atomic_int_get (&synth->core_voice_index); + + for (start_index = voice_index; voice_index < synth->polyphony; voice_index++) + { + voice = synth->voice[voice_index]; + + if (_PLAYING (voice)) + { + if (fluid_atomic_int_compare_and_exchange (&synth->core_voice_index, + start_index, voice_index + 1)) + goto got_voice; + + break; /* compare and exchange failed (another thread grabbed the voice first) */ + } + } + } + while (voice_index < synth->polyphony); + + /* No more voices to process */ + fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */ + synth->core_work = FALSE; + + /* If there are still other core threads in progress, wait for last one */ + if (synth->core_inprogress > 0) + { + synth->core_waiting_for_last = TRUE; + + while (synth->core_inprogress > 0) + fluid_cond_wait (synth->core_wait_last_cond, synth->core_mutex); + } + + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */ + break; /* We're done */ + } /* while (TRUE) - Process voices loop */ + + /* Mix all voices */ + for (i = 0; i < synth->polyphony; i++) + { + voice = synth->core_voice_processed[i]; + if (!voice) continue; + + synth->core_voice_processed[i] = NULL; + + 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_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf); + } /* while (TRUE) - Mix processed voices loop */ + } /* if (i < synth->polyphony) - Are there any voices to process? */ + } + else /* synth->cores < 1 - Not multi-core enabled */ + { + /* call all playing synthesis processes */ + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (!_PLAYING(voice)) continue; + + prof_ref_voice = fluid_profile_ref(); /* The output associated with a MIDI channel is wrapped around * using the number of audio groups as modulo divider. This is @@ -2994,7 +3158,8 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) 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_voice_write (voice, local_voice_buf); + fluid_voice_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice); } @@ -3066,6 +3231,84 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) return 0; } +/* Core thread function (processes voices in parallel to primary synthesis thread) */ +static void +fluid_synth_core_thread_func (void* data) +{ + fluid_synth_t *synth = data; + fluid_voice_t *voice; + int i, count, start_index; + + /* We do this, rather than adding an "if (first_run)" statement to the while loop */ + fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */ + synth->core_inprogress++; + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */ + + /* Loop until delete_fluid_synth() kills off core threads */ + while (synth->cores_active) + { + fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */ + + synth->core_inprogress--; + + /* Wakeup primary synthesis thread if it is waiting for last and we are it */ + if (synth->core_waiting_for_last && synth->core_inprogress == 0) + fluid_cond_signal (synth->core_wait_last_cond); + + /* Wait until there is core work */ + while (!synth->core_work && synth->cores_active) + fluid_cond_wait (synth->core_cond, synth->core_mutex); + + if (!synth->cores_active) + { + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */ + break; + } + + synth->core_inprogress++; + + fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */ + + /* Voice processing loop (lock free) */ + while (TRUE) + { + /* Look for next active voice to process (in a lock free manner) */ + do + { + i = fluid_atomic_int_get (&synth->core_voice_index); + + for (start_index = i; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if (_PLAYING (voice)) + { + if (fluid_atomic_int_compare_and_exchange (&synth->core_voice_index, + start_index, i + 1)) + goto got_voice; + + break; /* compare and exchange failed (another thread grabbed the voice first) */ + } + } + } + while (i < synth->polyphony); + + /* No more voices to process */ + fluid_atomic_int_set (&synth->core_voice_index, synth->polyphony); + fluid_atomic_int_set (&synth->core_work, FALSE); + break; + +got_voice: + + /* Synthesize the voice */ + count = fluid_voice_write (voice, &synth->core_bufs[i * FLUID_BUFSIZE]); + + /* Assign the processed voice to the same voicebuf index (if there was any audio) */ + if (count > 0) synth->core_voice_processed[i] = voice; + } /* while (TRUE) - Lock free voice processing loop */ + } /* while (synth->cores_active) */ +} + /* Process events in an event queue */ static FLUID_INLINE void fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth, @@ -3401,9 +3644,9 @@ fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) sfont_already_loaded = synth->sfont_info != NULL; fluid_return_if_fail (!sfont_already_loaded); - fluid_mutex_lock (synth->mutex); + fluid_rec_mutex_lock (synth->mutex); synth->loaders = fluid_list_prepend(synth->loaders, loader); - fluid_mutex_unlock (synth->mutex); + fluid_rec_mutex_unlock (synth->mutex); } /** @@ -3445,11 +3688,11 @@ fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets return FLUID_FAILED; } - fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_id and sfont list */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ /* reset the presets for all channels if requested */ if (reset_presets) fluid_synth_program_reset(synth); @@ -3500,7 +3743,7 @@ fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); /* remove the SoundFont from the list */ - fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */ + fluid_rec_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); @@ -3512,7 +3755,7 @@ fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ if (!list) { FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); @@ -3536,7 +3779,7 @@ 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 */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock sfont_hash */ sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont); @@ -3549,7 +3792,7 @@ fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont) fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); } - fluid_mutex_unlock (synth->mutex); /* -- Unlock sfont_hash */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock sfont_hash */ fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */ @@ -3598,7 +3841,7 @@ fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */ + fluid_rec_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++) { @@ -3607,7 +3850,7 @@ fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) } if (!list) { - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); return FLUID_FAILED; } @@ -3615,7 +3858,7 @@ fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) /* keep a copy of the SoundFont's filename */ FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont)); - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK) return FLUID_FAILED; @@ -3638,10 +3881,10 @@ fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) return FLUID_FAILED; } - fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ /* reset the presets for all channels */ fluid_synth_update_presets(synth); @@ -3672,11 +3915,11 @@ fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) sfont_info = new_fluid_sfont_info (synth, sfont); if (!sfont_info) return (FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont_id and sfont_info list */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ /* reset the presets for all channels */ fluid_synth_program_reset (synth); @@ -3705,7 +3948,7 @@ fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) fluid_return_if_fail (sfont != NULL); /* remove the SoundFont from the list */ - fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_info list */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock sfont_info list */ for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); @@ -3720,7 +3963,7 @@ fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ /* reset the presets for all channels */ fluid_synth_program_reset (synth); @@ -3738,9 +3981,9 @@ fluid_synth_sfcount(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ count = fluid_list_size (synth->sfont_info); - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return count; } @@ -3762,10 +4005,10 @@ fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) fluid_return_val_if_fail (synth != NULL, NULL); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */ + fluid_rec_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 */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return sfont; } @@ -3787,7 +4030,7 @@ fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) fluid_return_val_if_fail (synth != NULL, NULL); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + fluid_rec_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; @@ -3795,7 +4038,7 @@ fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) break; } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return list ? sfont : NULL; } @@ -3818,7 +4061,7 @@ fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) fluid_return_val_if_fail (synth != NULL, NULL); fluid_return_val_if_fail (name != NULL, NULL); - fluid_mutex_lock (synth->mutex); /* ++ lock */ + fluid_rec_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; @@ -3826,7 +4069,7 @@ fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) break; } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ return list ? sfont : NULL; } @@ -4015,9 +4258,9 @@ fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock reverb value */ value = synth->reverb_roomsize; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4034,9 +4277,9 @@ fluid_synth_get_reverb_damp(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock reverb value */ value = synth->reverb_damping; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4053,9 +4296,9 @@ fluid_synth_get_reverb_level(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock reverb value */ value = synth->reverb_level; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4072,9 +4315,9 @@ fluid_synth_get_reverb_width(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock reverb value */ value = synth->reverb_width; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4197,9 +4440,9 @@ fluid_synth_get_chorus_nr(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock chorus value */ value = synth->chorus_nr; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4216,9 +4459,9 @@ fluid_synth_get_chorus_level(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock chorus value */ value = synth->chorus_level; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4235,9 +4478,9 @@ fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock chorus value */ value = synth->chorus_speed; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4254,9 +4497,9 @@ fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock chorus value */ value = synth->chorus_depth; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4273,9 +4516,9 @@ fluid_synth_get_chorus_type(fluid_synth_t* synth) fluid_return_val_if_fail (synth != NULL, 0.0); - fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock chorus value */ value = synth->chorus_type; - fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock */ return value; } @@ -4599,7 +4842,7 @@ fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock tunings */ tuning = new_fluid_tuning (name, bank, prog); @@ -4611,7 +4854,7 @@ fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, } else retval = FLUID_FAILED; - fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock tunings */ return retval; } @@ -4665,7 +4908,7 @@ fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, fluid_return_val_if_fail (name != NULL, FLUID_FAILED); fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock tunings */ tuning = new_fluid_tuning (name, bank, prog); @@ -4677,7 +4920,7 @@ fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, } else retval = FLUID_FAILED; - fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock tunings */ return retval; } @@ -4714,7 +4957,7 @@ fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, fluid_return_val_if_fail (key != NULL, FLUID_FAILED); fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock tunings */ old_tuning = fluid_synth_get_tuning (synth, bank, prog); @@ -4732,7 +4975,7 @@ fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, } else retval = FLUID_FAILED; - fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock tunings */ return retval; } @@ -4786,7 +5029,7 @@ fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ Lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ Lock tunings */ tuning = fluid_synth_get_tuning (synth, bank, prog); @@ -4800,7 +5043,7 @@ fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, if (tuning) fluid_tuning_ref (tuning); /* ++ ref for outside of lock */ - fluid_mutex_unlock (synth->mutex); /* -- Unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock tunings */ if (!tuning) return (FLUID_FAILED); @@ -4957,11 +5200,11 @@ fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) b = (p >> 8) & 0xFF; p &= 0xFF; - fluid_mutex_lock (synth->mutex); /* ++ lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock tunings */ if (!synth->tuning) { - fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock tunings */ return 0; } @@ -4981,12 +5224,12 @@ fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) else fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER ((b + 1) << 8), NULL); - fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock tunings */ return 1; } } - fluid_mutex_unlock (synth->mutex); /* -- unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock tunings */ return 0; } @@ -5007,7 +5250,7 @@ fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, { fluid_tuning_t* tuning; - fluid_mutex_lock (synth->mutex); /* ++ lock tunings */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock tunings */ tuning = fluid_synth_get_tuning (synth, bank, prog); @@ -5023,7 +5266,7 @@ fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double)); } - fluid_mutex_unlock (synth->mutex); /* unlock tunings */ + fluid_rec_mutex_unlock (synth->mutex); /* unlock tunings */ return tuning ? FLUID_OK : FLUID_FAILED; } @@ -5228,6 +5471,7 @@ fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, * @param param SoundFont generator ID (#fluid_gen_type) * @return Current generator value assigned to MIDI channel */ +/* FIXME - Not currently SMP multi-thread safe (need atomic set/get of gen) */ float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) { @@ -5379,7 +5623,7 @@ fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); @@ -5391,7 +5635,7 @@ fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ if (!list) { @@ -5417,7 +5661,7 @@ fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) fluid_return_val_if_fail (synth != NULL, 0); - fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + fluid_rec_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); @@ -5429,7 +5673,7 @@ fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) } } - fluid_mutex_unlock (synth->mutex); /* -- unlock */ + fluid_rec_mutex_unlock (synth->mutex); /* -- unlock */ if (!list) { diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index 67c885c6..d191521f 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -110,6 +110,8 @@ typedef struct _fluid_sfont_info_t { * left_buf[], right_buf[] (Contents change) * fx_left_buf[], fx_right_buf[] (Contents change) * LADSPA_FxUnit (Contents change) + * cores + * core_threads[] * * Single thread use only (modify only prior to synthesis): * loaders<> @@ -152,7 +154,7 @@ struct _fluid_synth_t fluid_private_t thread_queues; /**< Thread private data for event queues for each non-synthesis thread queuing events */ fluid_event_queue_t *queues[FLUID_MAX_EVENT_QUEUES]; /**< Thread event queues (NULL for unused elements) */ - fluid_mutex_t mutex; /**< Lock for multi-thread sensitive variables (not used by synthesis process) */ + fluid_rec_mutex_t mutex; /**< Lock for multi-thread sensitive variables (not used by synthesis process) */ fluid_list_t *queue_pool; /**< List of event queues whose threads have been destroyed and which can be re-used */ fluid_event_queue_t *return_queue; /**< Event queue for events from synthesis thread to non-synthesis threads (memory frees, etc) */ fluid_timer_t *return_queue_timer; /**< Timer thread to process return event queue */ @@ -219,6 +221,23 @@ struct _fluid_synth_t fluid_midi_router_t* midi_router; /**< The midi router. Could be done nicer. */ fluid_sample_timer_t* sample_timers; /**< List of timers triggered after a block has been processed */ + int cores; /**< Number of CPU cores (1 by default) */ + fluid_thread_t **core_threads; /**< Array of core threads (cores - 1 in length) */ + unsigned char cores_active; /**< TRUE if core slave threads should remain active, FALSE to terminate them */ + + /* Multi-core variables (protected by core_mutex) */ + fluid_cond_mutex_t *core_mutex; /**< Mutex to protect all core_ variables and use with core_cond and core_wait_last_cond */ + fluid_cond_t *core_cond; /**< Thread condition for signaling core slave threads */ + int core_work; /**< Boolean: TRUE if there is work, FALSE otherwise */ + + /* Used in a lockless atomic fashion */ + int core_voice_index; /**< Next voice index to process */ + fluid_voice_t **core_voice_processed; /**< Array for processed voices */ + fluid_real_t *core_bufs; /**< Block containing audio buffers for each voice (FLUID_BUFSIZE in length each) */ + int core_inprogress; /**< Count of secondary core threads in progress */ + int core_waiting_for_last; /**< Boolean: Set to TRUE if primary synthesis thread is waiting for last slave thread to finish */ + fluid_cond_t *core_wait_last_cond; /**< Thread condition for signaling primary synthesis thread when last slave thread finishes */ + #ifdef LADSPA fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */ #endif diff --git a/fluidsynth/src/fluid_sys.c b/fluidsynth/src/fluid_sys.c index 71818343..a376f58f 100644 --- a/fluidsynth/src/fluid_sys.c +++ b/fluidsynth/src/fluid_sys.c @@ -364,6 +364,13 @@ fluid_utime (void) /* */ /*=============================================================*/ +void +fluid_thread_self_set_prio (fluid_thread_prio_t prio, int prio_level) +{ + if (prio == FLUID_THREAD_PRIO_HIGH) + SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST); +} + /*************************************************************** * * Timer @@ -488,6 +495,13 @@ fluid_timer_join(fluid_timer_t* timer) /* */ /*=============================================================*/ +void +fluid_thread_self_set_prio (fluid_thread_prio_t prio, int prio_level) +{ + if (prio == FLUID_THREAD_PRIO_HIGH) + DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); +} + /*************************************************************** * * Timer @@ -610,6 +624,19 @@ fluid_timer_join(fluid_timer_t* timer) /* */ /*=============================================================*/ +void +fluid_thread_self_set_prio (fluid_thread_prio_t prio, int prio_level) +{ + struct sched_param priority; + + if (prio == FLUID_THREAD_PRIO_HIGH) + { + priority.sched_priority = prio_level; + + if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) != 0) + FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); + } +} /*************************************************************** * @@ -871,16 +898,61 @@ void fluid_profiling_print(void) * */ +typedef struct +{ + fluid_thread_func_t func; + void *data; + int prio_level; +} fluid_thread_info_t; +static gpointer +fluid_thread_high_prio (gpointer data) +{ + fluid_thread_info_t *info = data; + + fluid_thread_self_set_prio (FLUID_THREAD_PRIO_HIGH, info->prio_level); + + info->func (info->data); + FLUID_FREE (info); + + return NULL; +} + +/** + * Create a new thread. + * @param func Function to execute in new thread context + * @param data User defined data to pass to func + * @param prio Priority class + * @param prio_level Priority level (used only for high priority on Posix currently, 1-99) + * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished. + * @return New thread pointer or NULL on error + */ fluid_thread_t * -new_fluid_thread (fluid_thread_func_t func, void *data, int detach) +new_fluid_thread (fluid_thread_func_t func, void *data, + fluid_thread_prio_t prio, int prio_level, int detach) { GThread *thread; + fluid_thread_info_t *info; GError *err = NULL; g_return_val_if_fail (func != NULL, NULL); - thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err); + if (prio == FLUID_THREAD_PRIO_HIGH) + { + info = FLUID_NEW (fluid_thread_info_t); + + if (!info) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + info->func = func; + info->data = data; + info->prio_level = prio_level; + thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err); + } + else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err); if (!thread) { @@ -892,12 +964,23 @@ new_fluid_thread (fluid_thread_func_t func, void *data, int detach) return thread; } -int delete_fluid_thread(fluid_thread_t* thread) +/** + * Frees data associated with a thread (does not actually stop thread). + * @param thread Thread to free + */ +void +delete_fluid_thread(fluid_thread_t* thread) { - return FLUID_OK; + /* Threads free themselves when they quit, nothing to do */ } -int fluid_thread_join(fluid_thread_t* thread) +/** + * Join a thread (wait for it to terminate). + * @param thread Thread to join + * @return FLUID_OK + */ +int +fluid_thread_join(fluid_thread_t* thread) { g_thread_join (thread); @@ -1136,7 +1219,8 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void* data) server_socket->data = data; server_socket->cont = 1; - server_socket->thread = new_fluid_thread(fluid_server_socket_run, server_socket, 0); + server_socket->thread = new_fluid_thread(fluid_server_socket_run, server_socket, + FLUID_THREAD_PRIO_NORMAL, 0, FALSE); if (server_socket->thread == NULL) { FLUID_FREE(server_socket); fluid_socket_close(sock); diff --git a/fluidsynth/src/fluid_sys.h b/fluidsynth/src/fluid_sys.h index 77f0eb65..3ee9c88e 100644 --- a/fluidsynth/src/fluid_sys.h +++ b/fluidsynth/src/fluid_sys.h @@ -122,18 +122,38 @@ int delete_fluid_timer(fluid_timer_t* timer); int fluid_timer_join(fluid_timer_t* timer); int fluid_timer_stop(fluid_timer_t* timer); -/** +/* Muteces */ - Muteces +/* Regular mutex */ +typedef GStaticMutex fluid_mutex_t; +#define fluid_mutex_init(_m) g_static_mutex_init(&(_m)) +#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m)) +#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) -*/ +/* Recursive lock capable mutex */ +typedef GStaticRecMutex fluid_rec_mutex_t; +#define fluid_rec_mutex_init(_m) g_static_rec_mutex_init(&(_m)) +#define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) +#define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) +#define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) -/* Recursive locks allowed */ -typedef GStaticRecMutex fluid_mutex_t; -#define fluid_mutex_init(_m) g_static_rec_mutex_init(&(_m)) -#define fluid_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) -#define fluid_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) -#define fluid_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef GMutex fluid_cond_mutex_t; +#define new_fluid_cond_mutex g_mutex_new +#define delete_fluid_cond_mutex g_mutex_free +#define fluid_cond_mutex_lock g_mutex_lock +#define fluid_cond_mutex_unlock g_mutex_unlock + + +/* Thread condition signaling */ + +typedef GCond fluid_cond_t; +#define new_fluid_cond g_cond_new +#define delete_fluid_cond g_cond_free +#define fluid_cond_signal g_cond_signal +#define fluid_cond_broadcast g_cond_broadcast +#define fluid_cond_wait g_cond_wait /* Atomic operations */ @@ -148,7 +168,8 @@ typedef GStaticRecMutex fluid_mutex_t; #define fluid_atomic_int_exchange_and_add(_pi, _add) \ g_atomic_int_exchange_and_add(_pi, _add) -#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) +#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) +#define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val) #define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ g_atomic_pointer_compare_and_exchange(_pp, _old, _new) @@ -180,37 +201,40 @@ typedef GStaticPrivate fluid_private_t; #define fluid_private_free(_priv) g_static_private_free(&(_priv)) -/** - Threads - - */ +/* Threads */ typedef GThread fluid_thread_t; typedef void (*fluid_thread_func_t)(void* data); -/** When detached, 'join' does not work and the thread destroys itself - when finished. */ -fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach); -int delete_fluid_thread(fluid_thread_t* thread); -int fluid_thread_join(fluid_thread_t* thread); +/** + * Thread priorities. + */ +typedef enum +{ + FLUID_THREAD_PRIO_NORMAL, /**< Normal thread priority */ + FLUID_THREAD_PRIO_HIGH /**< High priority thread */ +} fluid_thread_prio_t; #define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ #define fluid_thread_id_t GThread * /* Data type for a thread ID */ #define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ -/** - Sockets and I/O +fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void *data, + fluid_thread_prio_t prio, int prio_level, int detach); +void delete_fluid_thread(fluid_thread_t* thread); +void fluid_thread_self_set_prio (fluid_thread_prio_t prio, int prio_level); +int fluid_thread_join(fluid_thread_t* thread); - */ +/* Sockets and I/O */ fluid_istream_t fluid_get_stdin (void); fluid_ostream_t fluid_get_stdout (void); int fluid_istream_readline(fluid_istream_t in, char* prompt, char* buf, int len); int fluid_ostream_printf (fluid_ostream_t out, char* format, ...); -/** The function should return 0 if no error occured, non-zero - otherwise. If the function return non-zero, the socket will be - closed by the server. */ +/* The function should return 0 if no error occured, non-zero + otherwise. If the function return non-zero, the socket will be + closed by the server. */ typedef int (*fluid_server_func_t)(void* data, fluid_socket_t client_socket, char* addr); fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data); @@ -222,17 +246,14 @@ fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock); -/** +/* Profiling */ - Profiling + +/** + * Profile numbers. List all the pieces of code you want to profile + * here. Be sure to add an entry in the fluid_profile_data table in + * fluid_sys.c */ - - -/** - Profile numbers. List all the pieces of code you want to profile - here. Be sure to add an entry in the fluid_profile_data table in - fluid_sys.c -*/ enum { FLUID_PROF_WRITE_S16, FLUID_PROF_ONE_BLOCK, diff --git a/fluidsynth/src/fluid_voice.c b/fluidsynth/src/fluid_voice.c index ff60eaeb..464e0ecc 100644 --- a/fluidsynth/src/fluid_voice.c +++ b/fluidsynth/src/fluid_voice.c @@ -48,11 +48,7 @@ static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice); static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, int gen_key2base, int is_decay); -static inline void fluid_voice_effects (fluid_voice_t *voice, int count, - fluid_real_t* dsp_left_buf, - fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, - fluid_real_t* dsp_chorus_buf); +static inline void fluid_voice_filter (fluid_voice_t *voice); static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); static void fluid_voice_check_sample_sanity(fluid_voice_t* voice); @@ -236,44 +232,38 @@ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) } -/* - * fluid_voice_write +/** + * Synthesize a voice to a buffer. * - * This is where it all happens. This function is called by the - * synthesizer to generate the sound samples. The synthesizer passes - * four audio buffers: left, right, reverb out, and chorus out. + * @param voice Voice to synthesize + * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) + * @return Count of samples written to dsp_buf (can be 0) * - * The biggest part of this function sets the correct values for all - * the dsp parameters (all the control data boil down to only a few - * dsp parameters). The dsp routine is #included in several places (fluid_dsp_core.c). + * Panning, reverb and chorus are processed separately. The dsp interpolation + * routine is in (fluid_dsp_float.c). */ int -fluid_voice_write(fluid_voice_t* voice, - fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf) +fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf) { unsigned int i; fluid_real_t incr; fluid_real_t fres; fluid_real_t target_amp; /* target amplitude */ - int count; - int dsp_interp_method = voice->interp_method; - - fluid_real_t dsp_buf[FLUID_BUFSIZE]; fluid_env_data_t* env_data; fluid_real_t x; + int count = 0; - - /* make sure we're playing and that we have sample data */ - if (!_PLAYING(voice)) return FLUID_OK; + /* Other routines (such as fluid_voice_effects) use the last dsp_buf assigned */ + voice->dsp_buf = dsp_buf; + voice->dsp_buf_count = 0; /******************* sample **********************/ if (voice->sample == NULL) { fluid_voice_off(voice); - return FLUID_OK; + return 0; } fluid_check_fpe ("voice_write startup"); @@ -319,7 +309,7 @@ fluid_voice_write(fluid_voice_t* voice, { fluid_profile (FLUID_PROF_VOICE_RELEASE, voice->ref); fluid_voice_off (voice); - return FLUID_OK; + return 0; } fluid_check_fpe ("voice_write vol env"); @@ -593,8 +583,6 @@ fluid_voice_write(fluid_voice_t* voice, * Depending on the position in the loop and the loop size, this * may require several runs. */ - voice->dsp_buf = dsp_buf; - switch (voice->interp_method) { case FLUID_INTERP_NONE: @@ -614,9 +602,10 @@ fluid_voice_write(fluid_voice_t* voice, fluid_check_fpe ("voice_write interpolation"); - if (count > 0) - fluid_voice_effects (voice, count, dsp_left_buf, dsp_right_buf, - dsp_reverb_buf, dsp_chorus_buf); + voice->dsp_buf_count = count; + + /* Apply filter */ + if (count > 0) fluid_voice_filter (voice); /* turn off voice if short count (sample ended and not looping) */ if (count < FLUID_BUFSIZE) @@ -628,27 +617,19 @@ fluid_voice_write(fluid_voice_t* voice, post_process: voice->ticks += FLUID_BUFSIZE; fluid_check_fpe ("voice_write postprocess"); - return FLUID_OK; + return count; } -/* Purpose: - * - * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) - * - mixes the processed sample to left and right output using the pan setting - * - sends the processed sample to chorus and reverb - * +/** + * Applies a lowpass filter with variable cutoff frequency and quality factor. + * @param voice Voice to apply filter to + * @param count Count of samples in voice->dsp_buf + */ +/* * Variable description: - * - dsp_data: Pointer to the original waveform data - * - dsp_left_buf: The generated signal goes here, left channel - * - dsp_right_buf: right channel - * - dsp_reverb_buf: Send to reverb unit - * - dsp_chorus_buf: Send to chorus unit - * - dsp_a1: Coefficient for the filter - * - dsp_a2: same - * - dsp_b0: same - * - dsp_b1: same - * - dsp_b2: same + * - dsp_buf: Pointer to the synthesized audio data + * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients * - voice holds the voice structure * * A couple of variables are used internally, their results are discarded: @@ -660,12 +641,9 @@ fluid_voice_write(fluid_voice_t* voice, * - dsp_centernode: delay line for the IIR filter * - dsp_hist1: same * - dsp_hist2: same - * */ static inline void -fluid_voice_effects (fluid_voice_t *voice, int count, - fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf, - fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf) +fluid_voice_filter (fluid_voice_t *voice) { /* IIR filter sample history */ fluid_real_t dsp_hist1 = voice->hist1; @@ -685,6 +663,7 @@ fluid_voice_effects (fluid_voice_t *voice, int count, fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_centernode; + int count = voice->dsp_buf_count; int dsp_i; float v; @@ -729,50 +708,6 @@ fluid_voice_effects (fluid_voice_t *voice, int count, } } - /* pan (Copy the signal to the left and right output buffer) The voice - * panning generator has a range of -500 .. 500. If it is centered, - * it's close to 0. voice->amp_left and voice->amp_right are then the - * same, and we can save one multiplication per voice and sample. - */ - if ((-0.5 < voice->pan) && (voice->pan < 0.5)) - { - /* The voice is centered. Use voice->amp_left twice. */ - for (dsp_i = 0; dsp_i < count; dsp_i++) - { - v = voice->amp_left * dsp_buf[dsp_i]; - dsp_left_buf[dsp_i] += v; - dsp_right_buf[dsp_i] += v; - } - } - else /* The voice is not centered. Stereo samples have one side zero. */ - { - if (voice->amp_left != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i]; - } - - if (voice->amp_right != 0.0) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i]; - } - } - - /* reverb send. Buffer may be NULL. */ - if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i]; - } - - /* chorus send. Buffer may be NULL. */ - if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0)) - { - for (dsp_i = 0; dsp_i < count; dsp_i++) - dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i]; - } - voice->hist1 = dsp_hist1; voice->hist2 = dsp_hist2; voice->a1 = dsp_a1; @@ -781,16 +716,76 @@ fluid_voice_effects (fluid_voice_t *voice, int count, voice->b1 = dsp_b1; voice->filter_coeff_incr_count = dsp_filter_coeff_incr_count; - fluid_check_fpe ("voice_effects"); + fluid_check_fpe ("voice_filter"); } -/* - * fluid_voice_get_channel +/** + * Mix voice data to left/right (panning), reverb and chorus buffers. + * @param voice Voice to mix + * @param left_buf Left audio buffer + * @param right_buf Right audio buffer + * @param reverb_buf Reverb buffer + * @param chorus_buf Chorus buffer + * + * NOTE: Uses voice->dsp_buf and voice->dsp_buf_count which were assigned + * by fluid_voice_write(). This is therefore meant to be called only after + * that function. */ -fluid_channel_t* -fluid_voice_get_channel(fluid_voice_t* voice) +void +fluid_voice_mix (fluid_voice_t *voice, + fluid_real_t* left_buf, fluid_real_t* right_buf, + fluid_real_t* reverb_buf, fluid_real_t* chorus_buf) { - return voice->channel; + fluid_real_t *dsp_buf = voice->dsp_buf; + int count = voice->dsp_buf_count; + int dsp_i; + float v; + + /* pan (Copy the signal to the left and right output buffer) The voice + * panning generator has a range of -500 .. 500. If it is centered, + * it's close to 0. voice->amp_left and voice->amp_right are then the + * same, and we can save one multiplication per voice and sample. + */ + if ((-0.5 < voice->pan) && (voice->pan < 0.5)) + { + /* The voice is centered. Use voice->amp_left twice. */ + for (dsp_i = 0; dsp_i < count; dsp_i++) + { + v = voice->amp_left * dsp_buf[dsp_i]; + left_buf[dsp_i] += v; + right_buf[dsp_i] += v; + } + } + else /* The voice is not centered. Stereo samples have one side zero. */ + { + if (voice->amp_left != 0.0) + { + for (dsp_i = 0; dsp_i < count; dsp_i++) + left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i]; + } + + if (voice->amp_right != 0.0) + { + for (dsp_i = 0; dsp_i < count; dsp_i++) + right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i]; + } + } + + /* reverb send. Buffer may be NULL. */ + if ((reverb_buf != NULL) && (voice->amp_reverb != 0.0)) + { + for (dsp_i = 0; dsp_i < count; dsp_i++) + reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i]; + } + + /* chorus send. Buffer may be NULL. */ + if ((chorus_buf != NULL) && (voice->amp_chorus != 0)) + { + for (dsp_i = 0; dsp_i < count; dsp_i++) + chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i]; + } + + fluid_check_fpe ("voice_mix"); } /* diff --git a/fluidsynth/src/fluid_voice.h b/fluidsynth/src/fluid_voice.h index e838abae..8f97d3b0 100644 --- a/fluidsynth/src/fluid_voice.h +++ b/fluidsynth/src/fluid_voice.h @@ -116,6 +116,7 @@ struct _fluid_voice_t fluid_real_t phase_incr; /* the phase increment for the next 64 samples */ fluid_real_t amp_incr; /* amplitude increment value */ fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ + int dsp_buf_count; /* Number of audio samples in dsp_buf */ /* End temporary variables */ @@ -219,9 +220,7 @@ int delete_fluid_voice(fluid_voice_t* voice); void fluid_voice_start(fluid_voice_t* voice); void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice); -int fluid_voice_write(fluid_voice_t* voice, - fluid_real_t* left, fluid_real_t* right, - fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); +int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf); int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, fluid_channel_t* channel, int key, int vel, @@ -246,9 +245,14 @@ void fluid_voice_update_param(fluid_voice_t* voice, int gen); int fluid_voice_noteoff(fluid_voice_t* voice); int fluid_voice_off(fluid_voice_t* voice); -fluid_channel_t* fluid_voice_get_channel(fluid_voice_t* voice); +void fluid_voice_mix (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 fluid_voice_kill_excl(fluid_voice_t* voice); +#define fluid_voice_get_channel(voice) ((voice)->channel) + + #define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); } #define fluid_voice_get_chan(_voice) (_voice)->chan @@ -265,7 +269,7 @@ int fluid_voice_kill_excl(fluid_voice_t* voice); #define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) -/* FIXME - Josh Green - This doesn't seem to be used anywhere */ +/* FIXME - This doesn't seem to be used anywhere - JG */ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); #define _GEN(_voice, _n) \ diff --git a/fluidsynth/src/fluidsynth_priv.h b/fluidsynth/src/fluidsynth_priv.h index 436e710b..5a0c59d7 100644 --- a/fluidsynth/src/fluidsynth_priv.h +++ b/fluidsynth/src/fluidsynth_priv.h @@ -202,6 +202,8 @@ typedef struct _fluid_sample_timer_t fluid_sample_timer_t; #define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ #define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ #define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ +#define FLUID_DEFAULT_AUDIO_RT_PRIO 90 /**< Default setting for audio.realtime-prio */ +#define FLUID_DEFAULT_MIDI_RT_PRIO 80 /**< Default setting for midi.realtime-prio */ #ifndef PI #define PI 3.141592654