diff --git a/CMakeLists.txt b/CMakeLists.txt index b8b4b58e..4fd4a995 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ set ( PACKAGE "fluidsynth" ) # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 2 ) set ( FLUIDSYNTH_VERSION_MINOR 0 ) -set ( FLUIDSYNTH_VERSION_MICRO 4 ) +set ( FLUIDSYNTH_VERSION_MICRO 5 ) set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" ) set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) @@ -44,7 +44,7 @@ set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) # This is not exactly the same algorithm as the libtool one, but the results are the same. set ( LIB_VERSION_CURRENT 2 ) set ( LIB_VERSION_AGE 1 ) -set ( LIB_VERSION_REVISION 1 ) +set ( LIB_VERSION_REVISION 2 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) diff --git a/doc/Doxyfile b/doc/Doxyfile index b32e5f15..6e09cb84 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth -PROJECT_NUMBER = 2.0.4 +PROJECT_NUMBER = 2.0.5 OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index db838fe4..c24969b8 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -8,8 +8,8 @@ \author David Henningsson \author Tom Moebert \author Copyright © 2003-2019 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert -\version Revision 2.0.4 -\date 2019-02-09 +\version Revision 2.0.5 +\date 2019-04-13 All the source code examples in this document are in the public domain; you can use them as you please. This document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ . The FluidSynth library is distributed under the GNU Lesser General Public License. A copy of the GNU Lesser General Public License is contained in the FluidSynth package; if not, visit http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -42,7 +42,7 @@ All the source code examples in this document are in the public domain; you can \section Disclaimer -This documentation, in its current version, is incomplete. As always, the source code is the final reference. +This documentation may be partly incomplete. As always, the source code is the final reference. SoundFont(R) is a registered trademark of E-mu Systems, Inc. @@ -162,7 +162,7 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions Before you can use the synthesizer, you have to create a settings object. The settings objects is used by many components of the FluidSynth library. It gives a unified API to set the parameters of the audio drivers, the midi drivers, the synthesizer, and so forth. A number of default settings are defined by the current implementation. -All settings have a name that follows the "dotted-name" notation. For example, "synth.polyphony" refers to the number of voices (polyphony) preallocated by the synthesizer. The settings also have a type. There are currently three types: strings, numbers (double floats), and integers. You can change the values of a setting using the fluid_settings_setstr(), fluid_settings_setnum(), and fluid_settings_setint() functions. For example: +All settings have a name that follows the "dotted-name" notation. For example, "synth.polyphony" refers to the number of voices (polyphony) allocated by the synthesizer. The settings also have a type. There are currently three types: strings, numbers (double floats), and integers. You can change the values of a setting using the fluid_settings_setstr(), fluid_settings_setnum(), and fluid_settings_setint() functions. For example: \code #include diff --git a/sf2/VintageDreamsWaves-v2.sf3 b/sf2/VintageDreamsWaves-v2.sf3 deleted file mode 100644 index d0a699b5..00000000 Binary files a/sf2/VintageDreamsWaves-v2.sf3 and /dev/null differ diff --git a/src/drivers/fluid_jack.c b/src/drivers/fluid_jack.c index d86daa26..60503e2f 100644 --- a/src/drivers/fluid_jack.c +++ b/src/drivers/fluid_jack.c @@ -115,11 +115,13 @@ fluid_jack_audio_driver_settings(fluid_settings_t *settings) * Connect all midi input ports to all terminal midi output ports */ void -fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *midi_driver) { +fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *midi_driver) +{ int i, j; - const char ** midi_source_ports; + const char **midi_source_ports; midi_source_ports = jack_get_ports(client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal); + if(midi_source_ports != NULL) { for(j = 0; midi_source_ports[j] != NULL; j++) @@ -130,6 +132,7 @@ fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *mid jack_connect(client, midi_source_ports[j], jack_port_name(midi_driver->midi_port[i])); } } + jack_free(midi_source_ports); } @@ -199,7 +202,7 @@ new_fluid_jack_client(fluid_settings_t *settings, int isaudio, void *driver) if(!client_ref) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_LOG(FLUID_PANIC, "Out of memory"); goto error_recovery; } @@ -338,7 +341,7 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien if((dev->midi_port = FLUID_ARRAY(jack_port_t *, ports)) == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } @@ -375,7 +378,7 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien if(dev->output_ports == NULL) { - FLUID_LOG(FLUID_PANIC, "Jack server not running?"); + FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } @@ -388,6 +391,11 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien dev->output_ports[1] = jack_port_register(client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if(dev->output_ports[0] == NULL || dev->output_ports[1] == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port"); + goto error_recovery; + } } else { @@ -406,7 +414,7 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien if(dev->output_bufs == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); - return FLUID_FAILED; + goto error_recovery; } FLUID_MEMSET(dev->output_ports, 0, 2 * dev->num_output_ports * sizeof(jack_port_t *)); @@ -414,12 +422,22 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien for(i = 0; i < dev->num_output_ports; i++) { sprintf(name, "l_%02d", i); - dev->output_ports[2 * i] - = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + if((dev->output_ports[2 * i] + = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port '%s'", name); + goto error_recovery; + } sprintf(name, "r_%02d", i); - dev->output_ports[2 * i + 1] - = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + if((dev->output_ports[2 * i + 1] + = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create Jack audio port '%s'", name); + goto error_recovery; + } } fluid_settings_getint(settings, "synth.effects-channels", &dev->num_fx_ports); @@ -431,7 +449,7 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien if(dev->fx_ports == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); - return FLUID_FAILED; + goto error_recovery; } dev->fx_bufs = FLUID_ARRAY(float *, 2 * dev->num_fx_ports); @@ -439,7 +457,7 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien if(dev->fx_bufs == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); - return FLUID_FAILED; + goto error_recovery; } FLUID_MEMSET(dev->fx_ports, 0, 2 * dev->num_fx_ports * sizeof(jack_port_t *)); @@ -447,12 +465,22 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien for(i = 0; i < dev->num_fx_ports; i++) { sprintf(name, "fx_l_%02d", i); - dev->fx_ports[2 * i] - = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + if((dev->fx_ports[2 * i] + = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create Jack fx audio port '%s'", name); + goto error_recovery; + } sprintf(name, "fx_r_%02d", i); - dev->fx_ports[2 * i + 1] - = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + if((dev->fx_ports[2 * i + 1] + = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create Jack fx audio port '%s'", name); + goto error_recovery; + } } } @@ -477,6 +505,18 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien } return FLUID_OK; + +error_recovery: + + FLUID_FREE(dev->output_ports); + dev->output_ports = NULL; + FLUID_FREE(dev->fx_ports); + dev->fx_ports = NULL; + FLUID_FREE(dev->output_bufs); + dev->output_bufs = NULL; + FLUID_FREE(dev->fx_bufs); + dev->fx_bufs = NULL; + return FLUID_FAILED; } static void @@ -539,7 +579,7 @@ new_fluid_jack_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func if(dev == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } @@ -688,6 +728,7 @@ fluid_jack_driver_process(jack_nframes_t nframes, void *arg) } audio_driver = fluid_atomic_pointer_get(&client->audio_driver); + if(audio_driver == NULL) { // shutting down @@ -765,6 +806,7 @@ void fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg) { fluid_jack_client_t *client_ref = (fluid_jack_client_t *)arg; + if(client_ref->midi_driver != NULL) { client_ref->midi_driver->autoconnect_is_outdated = client_ref->midi_driver->autoconnect_inputs && is_registering != 0; @@ -793,7 +835,7 @@ new_fluid_jack_midi_driver(fluid_settings_t *settings, if(dev == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } @@ -807,9 +849,8 @@ new_fluid_jack_midi_driver(fluid_settings_t *settings, if(dev->parser == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE(dev); - return NULL; + FLUID_LOG(FLUID_PANIC, "Out of memory"); + goto error_recovery; } fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconnect_inputs); @@ -819,11 +860,15 @@ new_fluid_jack_midi_driver(fluid_settings_t *settings, if(!dev->client_ref) { - FLUID_FREE(dev); - return NULL; + FLUID_LOG(FLUID_PANIC, "Out of memory"); + goto error_recovery; } return (fluid_midi_driver_t *)dev; + +error_recovery: + delete_fluid_jack_midi_driver((fluid_midi_driver_t *)dev); + return NULL; } void diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index ae8a9fb2..5f8a2baa 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -246,7 +246,18 @@ int delete_fluid_defsfont(fluid_defsfont_t *defsfont) for(list = defsfont->sample; list; list = fluid_list_next(list)) { - delete_fluid_sample((fluid_sample_t *) fluid_list_get(list)); + sample = (fluid_sample_t *) fluid_list_get(list); + + /* If the sample data pointer is different to the sampledata chunk of + * the soundfont, then the sample has been loaded individually (SF3) + * and needs to be unloaded explicitly. This is safe even if using + * dynamic sample loading, as the sample_unload mechanism sets + * sample->data to NULL after unload. */ + if ((sample->data != NULL) && (sample->data != defsfont->sampledata)) + { + fluid_samplecache_unload(sample->data); + } + delete_fluid_sample(sample); } if(defsfont->sample) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 1c7a1f27..b72d5139 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1235,7 +1235,7 @@ fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key) { /* channel is poly and legato CC is Off) */ /* removes the note from the monophonic list */ - if(key == fluid_channel_last_note(channel)) + if(channel->n_notes && key == fluid_channel_last_note(channel)) { fluid_channel_clear_monolist(channel); } @@ -3638,13 +3638,20 @@ alias with buffers of \c fx. NULL buffers are permitted and will cause to skip m int fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[]) +{ + return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks); +} + +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)) { fluid_real_t *left_in, *fx_left_in; fluid_real_t *right_in, *fx_right_in; int nfxchan, nfxunits, naudchan; double time = fluid_utime(); - int i, f, num, count; + int i, f, num, count, buffered_blocks; float cpu_load; @@ -3668,9 +3675,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], count = 0; num = synth->cur; - if(synth->cur < FLUID_BUFSIZE) + buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + if(synth->cur < buffered_blocks * FLUID_BUFSIZE) { - int available = FLUID_BUFSIZE - synth->cur; + int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur; num = (available > len) ? len : available; if(nout != 0) @@ -3712,7 +3720,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], while(count < len) { int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; - int blockcount = fluid_synth_render_blocks(synth, blocksleft); + int blockcount = block_render_func(synth, blocksleft); num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; diff --git a/src/synth/fluid_synth.h b/src/synth/fluid_synth.h index 46c92ccf..f061aeaf 100644 --- a/src/synth/fluid_synth.h +++ b/src/synth/fluid_synth.h @@ -209,6 +209,13 @@ void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer void fluid_synth_process_event_queue(fluid_synth_t *synth); +int fluid_synth_set_gen2(fluid_synth_t *synth, int chan, + int param, float value, + int absolute, int normalized); + +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); /* * misc */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fb086598..0b735452 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,12 +10,13 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C $ --output-on ADD_FLUID_TEST(test_sample_cache) ADD_FLUID_TEST(test_sfont_loading) ADD_FLUID_TEST(test_sample_rate_change) -ADD_FLUID_TEST(test_preset_sample_loading) +# ADD_FLUID_TEST(test_preset_sample_loading) ADD_FLUID_TEST(test_pointer_alignment) ADD_FLUID_TEST(test_seqbind_unregister) ADD_FLUID_TEST(test_synth_chorus_reverb) ADD_FLUID_TEST(test_snprintf) +ADD_FLUID_TEST(test_synth_process) -if ( LIBSNDFILE_HASVORBIS ) - ADD_FLUID_TEST(test_sf3_sfont_loading) -endif ( LIBSNDFILE_HASVORBIS ) +# if ( LIBSNDFILE_HASVORBIS ) +# ADD_FLUID_TEST(test_sf3_sfont_loading) +# endif ( LIBSNDFILE_HASVORBIS ) diff --git a/test/test.h b/test/test.h index 74c4bc59..2df1ebe6 100644 --- a/test/test.h +++ b/test/test.h @@ -4,7 +4,7 @@ #include #include -#define TEST_ASSERT(COND) do { if (!(COND)) { fprintf(stderr, __FILE__ ":%d assertion (%s) failed\n", __LINE__, #COND); exit(-1); } } while (0) +#define TEST_ASSERT(COND) do { if (!(COND)) { fprintf(stderr, __FILE__ ":%d assertion (%s) failed\n", __LINE__, #COND); abort(); } } while (0) /* macro to test whether a fluidsynth function succeeded or not */ #define TEST_SUCCESS(FLUID_FUNCT) TEST_ASSERT((FLUID_FUNCT) != FLUID_FAILED) diff --git a/test/test_synth_process.c b/test/test_synth_process.c new file mode 100644 index 00000000..d273bdf2 --- /dev/null +++ b/test/test_synth_process.c @@ -0,0 +1,86 @@ + +#include "test.h" +#include "fluidsynth.h" +#include "fluidsynth_priv.h" +#include "fluid_synth.h" +#include + +static int smpl; +// static const int CHANNELS=16; +static const int SAMPLES=1024; + +int render_one_mock(fluid_synth_t *synth, int blocks) +{ + fluid_real_t *left_in, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + + int i, j; + + int nfxchan = fluid_synth_count_effects_channels(synth), + nfxunits = fluid_synth_count_effects_groups(synth), + naudchan = fluid_synth_count_audio_channels(synth); + + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + for(i = 0; i < naudchan; i++) + { + for(j = 0; j < blocks * FLUID_BUFSIZE; j++) + { + int idx = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + + right_in[idx] = left_in[idx] = (float)smpl++; + } + } + + return blocks; +} + +int render_and_check(fluid_synth_t* synth, int number_of_samples, int offset) +{ + int i; + float left[SAMPLES], right[SAMPLES]; + float *dry[1 * 2]; + dry[0] = left; + dry[1] = right; + memset(left, 0, sizeof(left)); + memset(right, 0, sizeof(right)); + + TEST_SUCCESS(fluid_synth_process_LOCAL(synth, number_of_samples, 0, NULL, 2, dry, render_one_mock)); + + for(i=0; i