Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package fluidsynth for openSUSE:Factory checked in at 2021-01-18 11:26:31 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fluidsynth (Old) and /work/SRC/openSUSE:Factory/.fluidsynth.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "fluidsynth" Mon Jan 18 11:26:31 2021 rev:55 rq:862915 version:2.1.6 Changes: -------- --- /work/SRC/openSUSE:Factory/fluidsynth/fluidsynth.changes 2020-10-27 18:58:23.250718905 +0100 +++ /work/SRC/openSUSE:Factory/.fluidsynth.new.28504/fluidsynth.changes 2021-01-18 11:26:44.084468690 +0100 @@ -1,0 +2,9 @@ +Sat Jan 9 11:37:13 UTC 2021 - Tom Mbrt <tom.m...@googlemail.com> + +- Update to version 2.1.6 + * SoundFonts may never be unloaded correctly, if + * polyphony is ever exceeded, or + * voices are still playing while their SoundFont is being unloaded. + * fix a heap-based use-after-free + +------------------------------------------------------------------- Old: ---- fluidsynth-2.1.5.tar.gz New: ---- fluidsynth-2.1.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fluidsynth.spec ++++++ --- /var/tmp/diff_new_pack.PJEstT/_old 2021-01-18 11:26:44.720470123 +0100 +++ /var/tmp/diff_new_pack.PJEstT/_new 2021-01-18 11:26:44.724470132 +0100 @@ -1,7 +1,7 @@ # # spec file for package fluidsynth # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %define sover 2 Name: fluidsynth -Version: 2.1.5 +Version: 2.1.6 Release: 0 Summary: A Real-Time Software Synthesizer That Uses Soundfont(tm) License: LGPL-2.1-or-later ++++++ fluidsynth-2.1.5.tar.gz -> fluidsynth-2.1.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/.appveyor-vcpkg.yml new/fluidsynth-2.1.6/.appveyor-vcpkg.yml --- old/fluidsynth-2.1.5/.appveyor-vcpkg.yml 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/.appveyor-vcpkg.yml 2021-01-03 20:55:48.000000000 +0100 @@ -1,58 +1,81 @@ -image: - - Visual Studio 2019 -build: - parallel: true - verbosity: detailed - -configuration: - - Release - -environment: - # update the vcpkg cache even if build fails - APPVEYOR_SAVE_CACHE_ON_ERROR: true - - matrix: - - platform: ARM - - platform: x86 - - platform: x64 - -cache: - - c:\Tools\vcpkg\installed - -init: - - set TARGET_PLATFORM= - - if "%platform%"=="x86" ( set TARGET_PLATFORM=Win32) - - if "%platform%"=="x64" ( set TARGET_PLATFORM=x64) - - if "%platform%"=="ARM" ( set TARGET_PLATFORM=ARM) - - echo %TARGET_PLATFORM% - - echo %APPVEYOR_BUILD_WORKER_IMAGE% - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" ( set "generator=Visual Studio 16 2019" && set "toolset=v142" ) - - echo %generator% - - echo %toolset% - -install: -# make sure the latest version of git is installed - - choco upgrade git ninja -y - - ninja --version - - cmake --version -# manually update vcpkg - - cd "C:\Tools\vcpkg" - - git pull - - .\bootstrap-vcpkg.bat - - cd %appveyor_build_folder% - - vcpkg install glib:%platform%-windows || type C:\Tools\vcpkg\buildtrees\libffi\config-arm-windows-out.log - -build_script: - - mkdir build - - cd build - - cmake -Werror=dev -G "%generator%" -A %TARGET_PLATFORM% -T "%toolset%" -Denable-pkgconfig=0 -DCMAKE_TOOLCHAIN_FILE=c:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. - - cmake --build . --config Release # build libfluidsynth and fluidsynth exec - - if not "%platform%"=="ARM" ( cmake --build . --config Release --target check ) # build and exec unittests, unless when cross-compiling - -after_build: - - 7z a fluidsynth-vcpkg-%platform%.zip %APPVEYOR_BUILD_FOLDER%\build\src\Release\* - -artifacts: - - path: build/fluidsynth-vcpkg-%platform%.zip - name: FluidSynth +variables: + toolset: 'v142' + generator: 'Visual Studio 16 2019' + VCPKG_DIR: 'C:\vcpkg' + +jobs: +- job: vcpkg + workspace: + clean: all + strategy: + matrix: + ARM: + platform: 'ARM' + cmake_platform: 'ARM' + configuration: 'Release' + x86: + platform: 'x86' + cmake_platform: 'Win32' + configuration: 'Release' + x64: + platform: 'x64' + cmake_platform: 'x64' + configuration: 'Release' + pool: + vmImage: 'windows-2019' + steps: + - script: | + @ECHO ON + echo $(generator) + echo $(toolset) + choco upgrade ninja -y + ninja --version + cmake --version + REM manually update vcpkg + REM cd "$(VCPKG_DIR)" || exit -1 + REM git pull || exit -1 + REM .\bootstrap-vcpkg.bat || exit -1 + REM cd $(Build.SourcesDirectory) + where vcpkg.exe + vcpkg install --only-downloads glib:$(platform)-windows + displayName: 'Prerequisites' + - task: Cache@2 + displayName: "Cache vcpkg's packages" + inputs: + key: $(VCPKG_DIR)\downloads\glib* | "$(platform)" + path: "$(VCPKG_DIR)" + cacheHitVar: CACHE_RESTORED + - script: | + @ECHO ON + vcpkg install glib:$(platform)-windows + displayName: 'vcpkg build glib' + condition: ne(variables.CACHE_RESTORED, 'true') + - script: | + @ECHO ON + mkdir build + cd build + cmake -Werror=dev -G "$(generator)" -A "$(cmake_platform)" -T "$(toolset)" -Denable-pkgconfig=0 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1 + REM build libfluidsynth and fluidsynth exec + cmake --build . --config $(configuration) || exit -1 + displayName: 'Compile fluidsynth' + - script: | + @ECHO ON + REM build and exec unittests, unless when cross-compiling + if not "%platform%"=="ARM" ( cmake --build build --config $(configuration) --target check ) + displayName: 'Execute Unittests' + - script: | + @ECHO ON + cd build + cmake --build . --config $(configuration) --target install || exit -1 + REM del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll + REM del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll + REM del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll + REM del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib + REM del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc + REM rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q + displayName: 'Copy Artifacts' + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory) + artifactName: fluidsynth-vcpkg-$(platform) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/.azure-pipelines.yml new/fluidsynth-2.1.6/.azure-pipelines.yml --- old/fluidsynth-2.1.5/.azure-pipelines.yml 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/.azure-pipelines.yml 2021-01-03 20:55:48.000000000 +0100 @@ -49,6 +49,7 @@ mv -f * .. cd .. rmdir $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform)\ + DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON @@ -122,6 +123,7 @@ REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 + DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON @@ -146,12 +148,14 @@ gtk-bundle: $(gtk-bundle-x86) libsndfile-url: $(libsndfile-url-x86) mingw-url: $(mingw-url-x86) + artifact-prefix: "fluidsynth-mingw" x64: CMAKE_FLAGS: platform: x64 gtk-bundle: $(gtk-bundle-x64) libsndfile-url: $(libsndfile-url-x64) mingw-url: $(mingw-url-x64) + artifact-prefix: "fluidsynth-mingw" pool: vmImage: 'vs2017-win2016' steps: @@ -180,6 +184,7 @@ mv lib\libsndfile-1.def lib\sndfile.def || exit -1 cd mingw*\ && cp -rf * .. && cd .. && rm -rf mingw* || exit -1 cd $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform) && cp -rf * d:\deps\ && mv -f * .. && cd .. && rmdir $(Build.ArtifactStagingDirectory)\libinstpatch-$(platform)\ || exit -1 + DEL /F C:\Strawberry\perl\bin\pkg-config.bat displayName: 'Prerequisites' - script: | @ECHO ON @@ -189,8 +194,8 @@ set PATH=%PATH:C:\Program Files\Git\usr\bin;=% pkg-config --list-all mkdir build && cd build || exit -1 - cmake -Werror=dev -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) $(CMAKE_FLAGS) -Denable-readline=0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1 - mingw32-make.exe all || exit -1 + cmake -Werror=dev -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) $(CMAKE_FLAGS) -Denable-readline=0 -Denable-floats=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1 + mingw32-make.exe -j4 all || exit -1 displayName: 'Compile fluidsynth' - script: | @ECHO ON @@ -199,5 +204,22 @@ set PATH=%PATH:C:\Program Files\Git\bin;=% set PATH=%PATH:C:\Program Files\Git\usr\bin;=% cd build || exit -1 - mingw32-make.exe check || exit -1 + mingw32-make.exe -j4 check || exit -1 displayName: 'Execute Unittests' + continueOnError: 'true' + - script: | + @ECHO ON + cd build + mingw32-make.exe install || exit -1 + REM xcopy test $(Build.ArtifactStagingDirectory)\bin /s + del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll + del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll + del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll + del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib + del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc + rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q + displayName: 'Copy Artifacts' + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory) + artifactName: $(artifact-prefix)-$(platform) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/.travis.yml new/fluidsynth-2.1.6/.travis.yml --- old/fluidsynth-2.1.5/.travis.yml 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/.travis.yml 2021-01-03 20:55:48.000000000 +0100 @@ -1,15 +1,18 @@ language: c sudo: false os: linux -dist: bionic +dist: focal +git: + depth: false # disable shallow fetch, history is needed by SonarQube addons: apt: update: true sources: - ubuntu-toolchain-r-test - - llvm-toolchain-bionic-7 - - llvm-toolchain-bionic-8 - - llvm-toolchain-bionic-9 + - llvm-toolchain-focal-7 + - llvm-toolchain-focal-8 + - llvm-toolchain-focal-9 + - llvm-toolchain-focal-10 packages: - cmake-data - cmake @@ -36,33 +39,61 @@ matrix: include: + - addons: + sonarcloud: + organization: "fluidsynth" + env: + - BW="build-wrapper-linux-x86-64 --out-dir bw-output" + - SONARSC="sonar-scanner -Dsonar.cfamily.build-wrapper-output=build/bw-output" + - arch: arm64 env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && sudo apt-get install gcc-7" + - CC="gcc-7" + - CXX="g++-7" + - MATRIX_EVAL="sudo apt-get install gcc-7 g++-7" + + - dist: trusty + env: + - CMAKE_FLAGS="" - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && sudo apt-get install gcc-8" + - CC="gcc-8" + - CXX="g++-8" + - MATRIX_EVAL="sudo apt-get install gcc-8 g++-8" - CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold" - env: - - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && sudo apt-get install clang-7" + - CC="clang-7" + - CXX="clang++-7" + - MATRIX_EVAL="sudo apt-get install clang-7" - env: - - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8 && sudo rm -f /usr/local/clang-7.0.0/bin/clang-tidy && sudo ln -s /usr/bin/clang-tidy-8 /usr/bin/clang-tidy && sudo apt-get install clang-8 clang-tidy-8" + - CC="clang-8" + - CXX="clang++-8" + - MATRIX_EVAL="sudo rm -f /usr/local/clang-7.0.0/bin/clang-tidy && sudo ln -s /usr/bin/clang-tidy-8 /usr/bin/clang-tidy && sudo apt-get install clang-8 clang-tidy-8" - CMAKE_FLAGS="-Denable-profiling=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold" - env: - - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9 && sudo apt-get install clang-9" + - CC="clang-9" + - CXX="clang++-9" + - MATRIX_EVAL="sudo apt-get install clang-9" + + - env: + - CC="clang-10" + - CXX="clang++-10" + - MATRIX_EVAL="sudo apt-get install clang-10" - os: linux-ppc64le env: - CMAKE_FLAGS="" before_install: - - eval "${MATRIX_EVAL}" - which clang-tidy || true - ls -la `which clang-tidy` || true - echo $PATH + - echo $MATRIX_EVAL + - eval "${MATRIX_EVAL}" + - echo $SONARSC before_script: - mkdir $HOME/fluidsynth_install/ @@ -70,6 +101,9 @@ script: - cmake -Werror=dev -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install ${CMAKE_FLAGS} -Denable-portaudio=1 -Denable-ladspa=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. - - make -j4 + - ${BW} make -j4 + - ls -la - make check - - make install # install only on linux, as CMAKE_INSTALL_PREFIX is ignored for frameworks on macosx and I cant tell whether that's correct or a bug. + - make install + - cd .. + - ${SONARSC} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/CMakeLists.txt new/fluidsynth-2.1.6/CMakeLists.txt --- old/fluidsynth-2.1.5/CMakeLists.txt 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/CMakeLists.txt 2021-01-03 20:55:48.000000000 +0100 @@ -29,7 +29,7 @@ # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 2 ) set ( FLUIDSYNTH_VERSION_MINOR 1 ) -set ( FLUIDSYNTH_VERSION_MICRO 5 ) +set ( FLUIDSYNTH_VERSION_MICRO 6 ) set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" ) set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) @@ -44,7 +44,7 @@ # 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 3 ) -set ( LIB_VERSION_REVISION 5 ) +set ( LIB_VERSION_REVISION 6 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/doc/Doxyfile new/fluidsynth-2.1.6/doc/Doxyfile --- old/fluidsynth-2.1.5/doc/Doxyfile 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/doc/Doxyfile 2021-01-03 20:55:48.000000000 +0100 @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth -PROJECT_NUMBER = 2.1.5 +PROJECT_NUMBER = 2.1.6 OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/doc/fluidsynth-v20-devdoc.txt new/fluidsynth-2.1.6/doc/fluidsynth-v20-devdoc.txt --- old/fluidsynth-2.1.5/doc/fluidsynth-v20-devdoc.txt 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/doc/fluidsynth-v20-devdoc.txt 2021-01-03 20:55:48.000000000 +0100 @@ -7,9 +7,9 @@ \author Josh Green \author David Henningsson \author Tom Moebert -\author Copyright © 2003-2020 Peter Hanappe, Conrad Berh??rster, Antoine Schmitt, Pedro L??pez-Cabanillas, Josh Green, David Henningsson, Tom Moebert -\version Revision 2.1.5 -\date 2020-09-06 +\author Copyright © 2003-2021 Peter Hanappe, Conrad Berh??rster, Antoine Schmitt, Pedro L??pez-Cabanillas, Josh Green, David Henningsson, Tom Moebert +\version Revision 2.1.6 +\date 2021-01-02 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/doc/fluidsynth.1 new/fluidsynth-2.1.6/doc/fluidsynth.1 --- old/fluidsynth-2.1.5/doc/fluidsynth.1 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/doc/fluidsynth.1 2021-01-03 20:55:48.000000000 +0100 @@ -13,7 +13,7 @@ .\" along with this program; see the file LICENSE. If not, write to .\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. .\" -.TH FluidSynth 1 "Feb 16, 2020" +.TH FluidSynth 1 "Jan 1, 2021" .\" Please update the above date whenever this man page is modified. .\" .\" Some roff macros, for reference: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/sonar-project.properties new/fluidsynth-2.1.6/sonar-project.properties --- old/fluidsynth-2.1.5/sonar-project.properties 1970-01-01 01:00:00.000000000 +0100 +++ new/fluidsynth-2.1.6/sonar-project.properties 2021-01-03 20:55:48.000000000 +0100 @@ -0,0 +1,16 @@ +sonar.projectKey=FluidSynth_fluidsynth +sonar.organization=fluidsynth + +sonar.cfamily.threads=4 +sonar.cfamily.cache.enabled=false + + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=fluidsynth +#sonar.projectVersion=1.0 + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +#sonar.sources=. + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/fluidsynth.c new/fluidsynth-2.1.6/src/fluidsynth.c --- old/fluidsynth-2.1.5/src/fluidsynth.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/fluidsynth.c 2021-01-03 20:55:48.000000000 +0100 @@ -1113,7 +1113,7 @@ print_welcome() { printf("FluidSynth runtime version %s\n" - "Copyright (C) 2000-2020 Peter Hanappe and others.\n" + "Copyright (C) 2000-2021 Peter Hanappe and others.\n" "Distributed under the LGPL license.\n" "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n", fluid_version_str()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/midi/fluid_midi.c new/fluidsynth-2.1.6/src/midi/fluid_midi.c --- old/fluidsynth-2.1.5/src/midi/fluid_midi.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/midi/fluid_midi.c 2021-01-03 20:55:48.000000000 +0100 @@ -1717,6 +1717,9 @@ fluid_return_if_fail(player != NULL); + fluid_settings_callback_int(player->synth->settings, "player.reset-synth", + NULL, NULL); + fluid_player_stop(player); fluid_player_reset(player); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/synth/fluid_synth.c new/fluidsynth-2.1.6/src/synth/fluid_synth.c --- old/fluidsynth-2.1.5/src/synth/fluid_synth.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/synth/fluid_synth.c 2021-01-03 20:55:48.000000000 +0100 @@ -597,10 +597,15 @@ * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * - * @note The @p settings parameter is used directly and should freed after - * the synth has been deleted. Further note that you may modify FluidSettings of the + * @note The @p settings parameter is used directly, but the synth does not take ownership of it. + * Hence, the caller is responsible for freeing it, when no longer needed. + * Further note that you may modify FluidSettings of the * @p settings instance. However, only those FluidSettings marked as 'realtime' will * affect the synth immediately. + * + * @warning The @p settings object should only be used by a single synth at a time. I.e. creating + * multiple synth instances with a single @p settings object causes undefined behavior. Once the + * "single synth" has been deleted, you may use the @p settings object again for another synth. */ fluid_synth_t * new_fluid_synth(fluid_settings_t *settings) @@ -1006,6 +1011,50 @@ fluid_profiling_print(); + /* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after + * this synth has been deleted*/ + + fluid_settings_callback_num(synth->settings, "synth.gain", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.polyphony", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.device-id", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.percussion", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.sustained", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.released", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.age", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.volume", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.important", + NULL, NULL); + fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.room-size", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.damp", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.width", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.level", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.reverb.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.nr", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.level", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.depth", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.speed", + NULL, NULL); + /* turn off all voices, needed to unload SoundFont data */ if(synth->voice != NULL) { @@ -1018,6 +1067,12 @@ continue; } + /* WARNING: A this point we must ensure that the reference counter + of any soundfont sample owned by any rvoice belonging to the voice + are correctly decremented. This is the contrary part to + to fluid_voice_init() where the sample's reference counter is + incremented. + */ fluid_voice_unlock_rvoice(voice); fluid_voice_overflow_rvoice_finished(voice); @@ -1070,6 +1125,18 @@ delete_fluid_list(synth->loaders); + /* wait for and delete all the lazy sfont unloading timers */ + + for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list)) + { + fluid_timer_t* timer = fluid_list_get(list); + // explicitly join to wait for the unload really to happen + fluid_timer_join(timer); + // delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet + delete_fluid_timer(timer); + } + + delete_fluid_list(synth->fonts_to_be_unloaded); if(synth->channel != NULL) { @@ -4228,7 +4295,21 @@ } else if(synth->voice[j]->overflow_rvoice == fv) { + /* Unlock the overflow_rvoice of the voice. + Decrement the reference count of the sample owned by this + rvoice. + */ fluid_voice_overflow_rvoice_finished(synth->voice[j]); + + /* Decrement synth active voice count. Must not be incorporated + in fluid_voice_overflow_rvoice_finished() because + fluid_voice_overflow_rvoice_finished() is called also + at synth destruction and in this case the variable should be + accessed via voice->channel->synth->active_voice_count. + And for certain voices which are not playing, the field + voice->channel is NULL. + */ + synth->active_voice_count--; break; } } @@ -4732,11 +4813,25 @@ } /** - * Unload a SoundFont. + * Schedule a SoundFont for unloading. + * + * If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately. + * + * If any samples of the given SoundFont are still required by active voices, + * the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing. + * If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont + * will be unloaded in any case. + * Once this function returned, fluid_synth_sfcount() and similar functions will behave as if + * the SoundFont has already been unloaded, even though the lazy-unloading is still pending. + * + * @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a + * consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain + * conditions. Calling delete_fluid_synth() does not recover this situation either. + * * @param synth FluidSynth instance * @param id ID of SoundFont to unload * @param reset_presets TRUE to re-assign presets for all MIDI channels - * @return #FLUID_OK on success, #FLUID_FAILED on error + * @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise. */ int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) @@ -4797,7 +4892,8 @@ } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ else { - new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, TRUE, FALSE); + fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE); + synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/synth/fluid_synth.h new/fluidsynth-2.1.6/src/synth/fluid_synth.h --- old/fluidsynth-2.1.5/src/synth/fluid_synth.h 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/synth/fluid_synth.h 2021-01-03 20:55:48.000000000 +0100 @@ -127,6 +127,7 @@ fluid_list_t *loaders; /**< the SoundFont loaders */ fluid_list_t *sfont; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ + fluid_list_t *fonts_to_be_unloaded; /**< list of timers that try to unload a soundfont */ float gain; /**< master gain */ fluid_channel_t **channel; /**< the channels */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/synth/fluid_voice.c new/fluidsynth-2.1.6/src/synth/fluid_voice.c --- old/fluidsynth-2.1.5/src/synth/fluid_voice.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/synth/fluid_voice.c 2021-01-03 20:55:48.000000000 +0100 @@ -174,6 +174,7 @@ voice->can_access_rvoice = voice->can_access_overflow_rvoice; voice->overflow_rvoice = rtemp; voice->can_access_overflow_rvoice = ctemp; + voice->overflow_sample = voice->sample; } static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) @@ -242,6 +243,7 @@ voice->eventhandler = handler; voice->channel = NULL; voice->sample = NULL; + voice->overflow_sample = NULL; voice->output_rate = output_rate; /* Initialize both the rvoice and overflow_rvoice */ @@ -321,12 +323,13 @@ voice->has_noteoff = 0; UPDATE_RVOICE0(fluid_rvoice_reset); - /* Increment the reference count of the sample to prevent the - unloading of the soundfont while this voice is playing, - once for us and once for the rvoice. */ + /* + We increment the reference count of the sample to indicate that this + sample is about to be owned by the rvoice. This will prevent the + unloading of the soundfont while this rvoice is playing. + */ fluid_sample_incr_ref(sample); fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); - fluid_sample_incr_ref(sample); voice->sample = sample; i = fluid_channel_get_interp_method(channel); @@ -1406,12 +1409,20 @@ } /* - * Called by fluid_synth when the overflow rvoice can be reclaimed. + * Unlock the overflow rvoice of the voice. + * Decrement the reference count of the sample owned by this rvoice. + * + * Called by fluid_synth when the overflow rvoice has finished by itself. + * Must be called also explicitly at synth destruction to ensure that + * the soundfont be unloaded successfully. */ void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) { voice->can_access_overflow_rvoice = 1; - fluid_voice_sample_unref(&voice->overflow_rvoice->dsp.sample); + + /* Decrement the reference count of the sample to indicate + that this sample isn't owned by the rvoice anymore */ + fluid_voice_sample_unref(&voice->overflow_sample); } /* @@ -1439,17 +1450,14 @@ voice->chan = NO_CHANNEL; - if(voice->can_access_rvoice) - { - fluid_voice_sample_unref(&voice->rvoice->dsp.sample); - } + /* Decrement the reference count of the sample, to indicate + that this sample isn't owned by the rvoice anymore. + */ + fluid_voice_sample_unref(&voice->sample); voice->status = FLUID_VOICE_OFF; voice->has_noteoff = 1; - /* Decrement the reference count of the sample. */ - fluid_voice_sample_unref(&voice->sample); - /* Decrement voice count */ voice->channel->synth->active_voice_count--; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/synth/fluid_voice.h new/fluidsynth-2.1.6/src/synth/fluid_voice.h --- old/fluidsynth-2.1.5/src/synth/fluid_voice.h 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/synth/fluid_voice.h 2021-01-03 20:55:48.000000000 +0100 @@ -71,7 +71,8 @@ fluid_channel_t *channel; fluid_rvoice_eventhandler_t *eventhandler; fluid_zone_range_t *zone_range; /* instrument zone range*/ - fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *overflow_sample; /* Pointer to sample (dupe in overflow_rvoice) */ unsigned int start_time; int mod_count; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/utils/fluid_settings.c new/fluidsynth-2.1.6/src/utils/fluid_settings.c --- old/fluidsynth-2.1.5/src/utils/fluid_settings.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/utils/fluid_settings.c 2021-01-03 20:55:48.000000000 +0100 @@ -907,6 +907,10 @@ * @param settings a settings object * @param name a setting's name * @return TRUE if the setting is changeable in real-time, FALSE otherwise + * + * @note Before using this function, make sure the @p settings object has already been used to create + * a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on + * which settings you want to query). */ int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/utils/fluid_sys.c new/fluidsynth-2.1.6/src/utils/fluid_sys.c --- old/fluidsynth-2.1.5/src/utils/fluid_sys.c 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/utils/fluid_sys.c 2021-01-03 20:55:48.000000000 +0100 @@ -60,6 +60,10 @@ struct _fluid_timer_t { long msec; + + // Pointer to a function to be executed by the timer. + // This field is set to NULL once the timer is finished to indicate completion. + // This allows for timed waits, rather than waiting forever as fluid_timer_join() does. fluid_timer_callback_t callback; void *data; fluid_thread_t *thread; @@ -1096,6 +1100,7 @@ } FLUID_LOG(FLUID_DBG, "Timer thread finished"); + timer->callback = NULL; if(timer->auto_destroy) { @@ -1189,6 +1194,19 @@ return FLUID_OK; } +int +fluid_timer_is_running(const fluid_timer_t *timer) +{ + // for unit test usage only + return timer->callback != NULL; +} + +long fluid_timer_get_interval(const fluid_timer_t * timer) +{ + // for unit test usage only + return timer->msec; +} + /*************************************************************** * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/src/utils/fluid_sys.h new/fluidsynth-2.1.6/src/utils/fluid_sys.h --- old/fluidsynth-2.1.5/src/utils/fluid_sys.h 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/src/utils/fluid_sys.h 2021-01-03 20:55:48.000000000 +0100 @@ -232,6 +232,8 @@ void delete_fluid_timer(fluid_timer_t *timer); int fluid_timer_join(fluid_timer_t *timer); int fluid_timer_stop(fluid_timer_t *timer); +int fluid_timer_is_running(const fluid_timer_t *timer); +long fluid_timer_get_interval(const fluid_timer_t * timer); // Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) #define NEW_GLIB_THREAD_API GLIB_CHECK_VERSION(2,32,0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/test/CMakeLists.txt new/fluidsynth-2.1.6/test/CMakeLists.txt --- old/fluidsynth-2.1.5/test/CMakeLists.txt 2020-09-11 22:22:35.000000000 +0200 +++ new/fluidsynth-2.1.6/test/CMakeLists.txt 2021-01-03 20:55:48.000000000 +0100 @@ -12,6 +12,7 @@ ADD_FLUID_TEST(test_sample_rate_change) ADD_FLUID_TEST(test_preset_sample_loading) ADD_FLUID_TEST(test_bug_635) +ADD_FLUID_TEST(test_settings_unregister_callback) ADD_FLUID_TEST(test_pointer_alignment) ADD_FLUID_TEST(test_seqbind_unregister) ADD_FLUID_TEST(test_synth_chorus_reverb) @@ -19,9 +20,10 @@ ADD_FLUID_TEST(test_synth_process) ADD_FLUID_TEST(test_ct2hz) ADD_FLUID_TEST(test_sample_validate) -ADD_FLUID_TEST(test_seq_event_queue_sort) -ADD_FLUID_TEST(test_seq_scale) ADD_FLUID_TEST(test_jack_obtaining_synth) +ADD_FLUID_TEST(test_sfont_unloading) +ADD_FLUID_TEST(test_seq_scale) +ADD_FLUID_TEST(test_seq_event_queue_sort) # if ( LIBSNDFILE_HASVORBIS ) # ADD_FLUID_TEST(test_sf3_sfont_loading) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/test/test_settings_unregister_callback.c new/fluidsynth-2.1.6/test/test_settings_unregister_callback.c --- old/fluidsynth-2.1.5/test/test_settings_unregister_callback.c 1970-01-01 01:00:00.000000000 +0100 +++ new/fluidsynth-2.1.6/test/test_settings_unregister_callback.c 2021-01-03 20:55:48.000000000 +0100 @@ -0,0 +1,95 @@ + +#include "test.h" +#include "fluidsynth.h" +#include "fluidsynth_priv.h" +#include "fluid_synth.h" +#include <string.h> + +static fluid_list_t* realtime_int_settings = NULL; +static fluid_list_t* realtime_str_settings = NULL; +static fluid_list_t* realtime_num_settings = NULL; + +void iter_func (void *data, const char *name, int type) +{ + if(fluid_settings_is_realtime(data, name)) + { + switch(type) + { + case FLUID_INT_TYPE: + realtime_int_settings = fluid_list_prepend(realtime_int_settings, FLUID_STRDUP(name)); + break; + case FLUID_STR_TYPE: + realtime_str_settings = fluid_list_prepend(realtime_str_settings, FLUID_STRDUP(name)); + break; + case FLUID_NUM_TYPE: + realtime_num_settings = fluid_list_prepend(realtime_num_settings, FLUID_STRDUP(name)); + break; + case FLUID_SET_TYPE: + break; + default: + TEST_ASSERT(FALSE); + } + } +} + +// this test should make sure that sample rate changed are handled correctly +int main(void) +{ + fluid_list_t* list; + fluid_player_t* player; + fluid_synth_t *synth; + fluid_settings_t *settings = new_fluid_settings(); + TEST_ASSERT(settings != NULL); + + synth = new_fluid_synth(settings); + TEST_ASSERT(synth != NULL); + + player = new_fluid_player(synth); + TEST_ASSERT(player != NULL); + + // see which of the objects above has registered a realtime setting + fluid_settings_foreach(settings, settings, iter_func); + + // delete the objects + delete_fluid_player(player); + delete_fluid_synth(synth); + + // and now, start making changes to those realtime settings + // Anything below fluidsynth 2.1.5 will crash + + for(list = realtime_int_settings; list; list = fluid_list_next(list)) + { + int min, max; + char* name = fluid_list_get(list); + TEST_SUCCESS(fluid_settings_getint_range(settings, name, &min, &max)); + TEST_SUCCESS(fluid_settings_setint(settings, name, min)); + FLUID_FREE(name); + } + + delete_fluid_list(realtime_int_settings); + + for(list = realtime_num_settings; list; list = fluid_list_next(list)) + { + double min, max; + char* name = fluid_list_get(list); + TEST_SUCCESS(fluid_settings_getnum_range(settings, name, &min, &max)); + TEST_SUCCESS(fluid_settings_setnum(settings, name, min)); + FLUID_FREE(name); + } + + delete_fluid_list(realtime_num_settings); + + + for(list = realtime_str_settings; list; list = fluid_list_next(list)) + { + char* name = fluid_list_get(list); + TEST_SUCCESS(fluid_settings_setstr(settings, name, "ABCDEFG")); + FLUID_FREE(name); + } + + delete_fluid_list(realtime_str_settings); + + delete_fluid_settings(settings); + + return EXIT_SUCCESS; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fluidsynth-2.1.5/test/test_sfont_unloading.c new/fluidsynth-2.1.6/test/test_sfont_unloading.c --- old/fluidsynth-2.1.5/test/test_sfont_unloading.c 1970-01-01 01:00:00.000000000 +0100 +++ new/fluidsynth-2.1.6/test/test_sfont_unloading.c 2021-01-03 20:55:48.000000000 +0100 @@ -0,0 +1,229 @@ + +#include "test.h" +#include "fluidsynth.h" +#include "synth/fluid_synth.h" +#include "utils/fluid_sys.h" + +void wait_and_free(fluid_synth_t* synth, int id, const char* calling_func) +{ + fluid_list_t *list, *list_orig; + list_orig = list = synth->fonts_to_be_unloaded; + synth->fonts_to_be_unloaded = NULL; + delete_fluid_synth(synth); + + for(; list; list = fluid_list_next(list)) + { + fluid_timer_t* timer = fluid_list_get(list); + FLUID_LOG(FLUID_INFO, "%s(): Start waiting for soundfont %d to unload", calling_func, id); + if(fluid_timer_is_running(timer)) + { + /* timer still running, wait a bit*/ + fluid_msleep(50 * fluid_timer_get_interval(timer)); + TEST_ASSERT(!fluid_timer_is_running(timer)); + } + delete_fluid_timer(timer); + FLUID_LOG(FLUID_INFO, "%s(): End waiting for soundfont %d to unload", calling_func, id); + } + delete_fluid_list(list_orig); +} + +static void test_without_rendering(fluid_settings_t* settings) +{ + int id; + fluid_synth_t *synth = new_fluid_synth(settings); + TEST_ASSERT(synth != NULL); + + TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); + + // load a sfont to synth + TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); + // one sfont loaded + TEST_ASSERT(fluid_synth_sfcount(synth) == 1); + + TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); + + TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); + + TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); + + // there must be one font scheduled for lazy unloading + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + + wait_and_free(synth, id, __func__); +} + +// this should work fine after applying JJCs fix a4ac56502fec5f0c20a60187d965c94ba1dc81c2 +static void test_after_polyphony_exceeded(fluid_settings_t* settings) +{ + int id; + fluid_synth_t *synth = new_fluid_synth(settings); + TEST_ASSERT(synth != NULL); + + TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); + + // load a sfont to synth + TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); + // one sfont loaded + TEST_ASSERT(fluid_synth_sfcount(synth) == 1); + + TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); + FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C4, voice count=%d", + fluid_synth_get_active_voice_count(synth)); + + // need to render a bit to make synth->ticks_since_start advance, to make the previous voice "killable" + TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); + + // polyphony exceeded - killing the killable voice from above + TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); + + // need to render again, to make the synth thread assign rvoice->dsp.sample, so that sample_unref() later really unrefs + TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); + FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C#4, voice count=%d", + fluid_synth_get_active_voice_count(synth)); + + FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): unload sounfont"); + TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); + + TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); + + // need to render yet again, to make the synth thread release the rvoice so it can be reclaimed by + // fluid_synth_check_finished_voices() + // need to render may more samples this time, so the voice makes it pass the release phase... + TEST_SUCCESS(fluid_synth_process(synth, 204800, 0, NULL, 0, NULL)); + + // make any API call to execute fluid_synth_check_finished_voices() + FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note off C#4, voice count=%d", + fluid_synth_get_active_voice_count(synth)); + + // there must be one font scheduled for lazy unloading + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + + wait_and_free(synth, id, __func__); +} + +static void test_default_polyphony(fluid_settings_t* settings, int with_rendering) +{ + enum { BUFSIZE = 128 }; + fluid_voice_t* buf[BUFSIZE]; + + int id; + fluid_synth_t *synth = new_fluid_synth(settings); + TEST_ASSERT(synth != NULL); + + TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); + + // load a sfont to synth + TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); + // one sfont loaded + TEST_ASSERT(fluid_synth_sfcount(synth) == 1); + + TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); + + if(with_rendering) + { + TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); + } + + TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); + + fluid_synth_get_voicelist(synth, buf, BUFSIZE, -1); + + TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); + + if(with_rendering) + { + // make the synth thread assign rvoice->dsp.sample + TEST_SUCCESS(fluid_synth_process(synth, 2 * fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); + + TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); + } + + TEST_ASSERT(synth->fonts_to_be_unloaded == NULL); + + TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); + + // now, there must be one font scheduled for lazy unloading + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); + + // noteoff the second note and render something + TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); + if(with_rendering) + { + TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); + } + + // still 4 because key 61 is playing in release phase now + TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); + // must be still running + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); + + // noteoff the first note and render something + TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); + if(with_rendering) + { + TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); + } + + // still 4 because keys 60 + 61 are playing in release phase now + TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); + // must be still running + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); + + if(with_rendering) + { + // render enough, to make the synth thread release the rvoice so it can be reclaimed by + // fluid_synth_check_finished_voices() + TEST_SUCCESS(fluid_synth_process(synth, 2048000, 0, NULL, 0, NULL)); + + // this API call should reclaim the rvoices and call fluid_voice_stop() + TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 0); + } + + TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); + if(with_rendering) + { + // We want to see that the timer thread unloads the soundfont before we call delete_fluid_synth(). + // Wait to give the timer thread a chance to unload and finish. + fluid_msleep(10 * fluid_timer_get_interval(fluid_list_get(synth->fonts_to_be_unloaded))); + TEST_ASSERT(!fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); + } + else + { + TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); + } + + wait_and_free(synth, id, __func__); +} + +// this tests the soundfont loading API of the synth. +// might be expanded to test the soundfont loader as well... +int main(void) +{ + fluid_settings_t *settings = new_fluid_settings(); + TEST_ASSERT(settings != NULL); + + FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() with rendering"); + test_default_polyphony(settings, TRUE); + FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); + + FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() without rendering"); + test_default_polyphony(settings, FALSE); + FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); + + fluid_settings_setint(settings, "synth.polyphony", 2); + + FLUID_LOG(FLUID_INFO, "Begin test_after_polyphony_exceeded()"); + test_after_polyphony_exceeded(settings); + FLUID_LOG(FLUID_INFO, "End test_after_polyphony_exceeded()\n"); + + FLUID_LOG(FLUID_INFO, "Begin test_without_rendering()"); + test_without_rendering(settings); + FLUID_LOG(FLUID_INFO, "End test_without_rendering()"); + + delete_fluid_settings(settings); + + return EXIT_SUCCESS; +}