Merge branch 'master' into refactor-and-fix-sffile-defsfont

This commit is contained in:
derselbst 2021-04-10 16:21:16 +02:00
commit d2754028b4
22 changed files with 39366 additions and 93 deletions

View file

@ -1,6 +1,6 @@
name: FluidSynth Linux
on: [push]
on: [push, pull_request]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
@ -17,7 +17,7 @@ jobs:
matrix:
CC: [""]
CXX: [""]
CMAKE_FLAGS: ["-Denable-profiling=1","-Denable-floats=1 -Denable-profiling=1","-Denable-floats=1","-Denable-trap-on-fpe=1","-Denable-fpe-check=1","-Denable-ipv6=0","-Denable-network=0","-Denable-aufile=0","-DBUILD_SHARED_LIBS=0","-Denable-ubsan=1"]
CMAKE_FLAGS: ["-Denable-profiling=1","-Denable-floats=1 -Denable-profiling=1","-Denable-floats=1","-Denable-trap-on-fpe=1","-Denable-fpe-check=1","-Denable-ipv6=0","-Denable-network=0","-Denable-aufile=0","-DBUILD_SHARED_LIBS=0","-Denable-ubsan=1 -Denable-debug=1"]
include:
- CC: "gcc-7"
CXX: "g++-7"

View file

@ -9,7 +9,8 @@ on:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: RelWithDebInfo
# Use Debug build for better code coverage results
BUILD_TYPE: Debug
name: SonarCloud Workflow
jobs:
@ -29,7 +30,7 @@ jobs:
run: sudo apt-get update -y
- name: Install Dependencies
run: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cmake-data cmake libglib2.0-0 libsndfile-dev libasound2-dev libjack-dev portaudio19-dev libsdl2-dev libpulse-dev libdbus-1-dev libsystemd-dev libinstpatch-dev libreadline-dev
run: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cmake-data cmake libglib2.0-0 libsndfile-dev libasound2-dev libjack-dev portaudio19-dev libsdl2-dev libpulse-dev libdbus-1-dev libsystemd-dev libinstpatch-dev libreadline-dev lcov gcovr ggcov
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
@ -52,26 +53,31 @@ jobs:
# access regardless of the host operating system
shell: bash
working-directory: ${{github.workspace}}/build
run: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Werror=dev -Denable-portaudio=1 -Denable-ladspa=1 -DNO_GUI=1 $GITHUB_WORKSPACE
run: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Werror=dev -Denable-portaudio=1 -Denable-ladspa=1 -Denable-coverage=1 -DNO_GUI=1 $GITHUB_WORKSPACE
- name: Build
working-directory: ${{github.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: |
./build-wrapper-linux-x86-64 --out-dir bw-output make -j`nproc`
./build-wrapper-linux-x86-64 --out-dir bw-output make
- name: Test
working-directory: ${{github.workspace}}/build
shell: bash
# Execute tests defined by the CMake configuration.
run: |
./build-wrapper-linux-x86-64 --out-dir bw-output make -j`nproc` check
./build-wrapper-linux-x86-64 --out-dir bw-output make coverage
# sonar-scanner does not like utf8 filenames
- name: Prepare for Sonar
run: |
rm -rf ${{ github.workspace }}/sf2
gcovr --version
gcovr --help
ls -la ${{ github.workspace }}
ls -la ${{ github.workspace }}/build
ls -la ${{ github.workspace }}/build/coverage
# The offical sonarsource/sonarcloud-github-action@v1.5 action does not work properly.
# It keeps complaining that the build-wrapper.json cannot be found.
@ -86,5 +92,6 @@ jobs:
run: sonar-scanner
-Dsonar.login=${{ secrets.SONAR_TOKEN }}
-Dsonar.cfamily.build-wrapper-output=${{ github.workspace }}/build/bw-output
-Dsonar.coverageReportPaths=build/coverage/sonarqube.report
-Dsonar.verbose=false
-Dsonar.host.url=https://sonarcloud.io/

View file

@ -150,4 +150,4 @@ Bernat Arlandis i Mañó
Sven Meier
Marcus Weseloh
Jean-jacques Ceresa
Vladimir Davidovich

View file

@ -49,6 +49,7 @@ set ( LIB_VERSION_INFO
"${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" )
# Options disabled by default
option ( enable-coverage "enable gcov code coverage" off )
option ( enable-debug "enable debugging (default=no)" off )
option ( enable-floats "enable type float instead of double for DSP samples" off )
option ( enable-fpe-check "enable Floating Point Exception checks and debug messages" off )
@ -224,6 +225,22 @@ if ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_C
set ( CMAKE_SHARED_LINKER_FLAGS "-fsanitize=address,undefined ${CMAKE_SHARED_LINKER_FLAGS}" )
set ( ENABLE_UBSAN 1 )
endif ( enable-ubsan )
if ( enable-coverage )
if ( CMAKE_COMPILER_IS_GNUCXX )
include ( CodeCoverage )
set ( CODE_COVERAGE_VERBOSE TRUE )
append_coverage_compiler_flags()
setup_target_for_coverage_gcovr_html (
NAME coverage
EXECUTABLE ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure
DEPENDENCIES check )
set ( ENABLE_COVERAGE 1 )
else()
message ( SEND_ERROR "Code Coverage is currently only supported for GNU Compiler (GCC)" )
endif()
endif ()
endif (CMAKE_C_COMPILER_ID STREQUAL "Intel" )
endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Intel" )
@ -743,6 +760,9 @@ link_directories (
${LIBINSTPATCH_LIBRARY_DIRS}
)
# required to allow ctest to be called from top-level build directory
ENABLE_TESTING()
# Process subdirectories
add_subdirectory ( src )
add_subdirectory ( test )

View file

@ -0,0 +1,603 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
# 2019-05-06, Anatolii Kurotych
# - Remove unnecessary --coverage flag
#
# 2019-12-13, FeRD (Frank Dana)
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
# - Set lcov basedir with -b argument
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
# - Delete output dir, .info file on 'make clean'
# - Remove Python detection, since version mismatches will break gcovr
# - Minor cleanup (lowercase function names, update examples...)
#
# 2019-12-19, FeRD (Frank Dana)
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
#
# 2020-01-19, Bob Apthorpe
# - Added gfortran support
#
# 2020-02-17, FeRD (Frank Dana)
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
# in EXCLUDEs, and remove manual escaping from gcovr targets
#
# 2021-01-19, Robin Mueller
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
# flags to the gcovr command
#
# 2020-05-04, Mihchael Davis
# - Add -fprofile-abs-path to make gcno files contain absolute paths
# - Fix BASE_DIRECTORY not working when defined
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags:
# append_coverage_compiler_flags()
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
# 4. If you need to exclude additional directories from the report, specify them
# using full paths in the COVERAGE_EXCLUDES variable before calling
# setup_target_for_coverage_*().
# Example:
# set(COVERAGE_EXCLUDES
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
# '/path/to/my/src/dir2/*')
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
# Example:
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE testrunner
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
#
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
# Example:
# set(COVERAGE_EXCLUDES "dir1/*")
# setup_target_for_coverage_gcovr_html(
# NAME coverage
# EXECUTABLE testrunner
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
# EXCLUDE "dir2/*")
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
# Check prereqs
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program( CPPFILT_PATH NAMES c++filt )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
list(GET LANGUAGES 0 LANG)
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
# Do nothing; exit conditional without error if true
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
# Do nothing; exit conditional without error if true
else()
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
endif()
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
CACHE INTERNAL "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_lcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# )
function(setup_target_for_coverage_lcov)
set(options NO_DEMANGLE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(LCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# enable branch coverage
set ( Coverage_LCOV_ARGS ${Coverage_LCOV_ARGS} --rc lcov_branch_coverage=1 )
set ( Coverage_GENHTML_ARGS ${Coverage_GENHTML_ARGS} --branch-coverage )
# Setting up commands which will be run to generate coverage data.
# Cleanup lcov
set(LCOV_CLEAN_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
-b ${BASEDIR} --zerocounters
)
# Create baseline to make sure untouched files show up in the report
set(LCOV_BASELINE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
${BASEDIR} -o ${Coverage_NAME}.base
)
# Run tests
set(LCOV_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Capturing lcov counters and generating report
set(LCOV_CAPTURE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
)
# add baseline counters
set(LCOV_BASELINE_COUNT_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
)
# filter collected data to final coverage report
set(LCOV_FILTER_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
)
# Generate HTML output
set(LCOV_GEN_HTML_CMD
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to clean up lcov: ")
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
message(STATUS "Command to create baseline: ")
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
message(STATUS "Command to run the tests: ")
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to capture counters and generate report: ")
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
message(STATUS "Command to add baseline counters: ")
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
message(STATUS "Command to filter collected data: ")
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
endif()
# Setup target
add_custom_target(${Coverage_NAME}
COMMAND ${LCOV_CLEAN_CMD}
COMMAND ${LCOV_BASELINE_CMD}
COMMAND ${LCOV_EXEC_TESTS_CMD}
COMMAND ${LCOV_CAPTURE_CMD}
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.base
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show where to find the lcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_lcov
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_html(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_html)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_HTML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Create folder
set(GCOVR_HTML_FOLDER_CMD
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
)
# Running gcovr
set(GCOVR_HTML_CMD
${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
-o ${Coverage_NAME}/index.html
)
# The --keep option is broken since gcovr 4.0 all our gcov files are deleted before SonarQube
# can use them. Hence create an additional sonarqube report, but do not fail if this fails.
set(GCOVR_SONAR_CMD
${GCOVR_PATH} --sonarqube -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
-o ${Coverage_NAME}/sonarqube.report || true
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to create a folder: ")
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
message(STATUS "Command to generate gcovr HTML coverage data: ")
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_HTML_FOLDER_CMD}
COMMAND ${GCOVR_HTML_CMD}
COMMAND ${GCOVR_SONAR_CMD}
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce HTML code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_gcovr_html
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_fastcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# SKIP_HTML # Don't create html report
# )
function(setup_target_for_coverage_fastcov)
set(options NO_DEMANGLE SKIP_HTML)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FASTCOV_PATH)
message(FATAL_ERROR "fastcov not found! Aborting...")
endif()
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (Patterns, not paths, for fastcov)
set(FASTCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Set up commands which will be run to generate coverage data
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--process-gcno
--lcov
--output ${Coverage_NAME}.info
--exclude ${FASTCOV_EXCLUDES}
--exclude ${FASTCOV_EXCLUDES}
)
if(Coverage_SKIP_HTML)
set(FASTCOV_HTML_CMD ";")
else()
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
-o ${Coverage_NAME} ${Coverage_NAME}.info
)
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
message(" Running tests:")
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
message(" Capturing fastcov counters and generating report:")
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
if(NOT Coverage_SKIP_HTML)
message(" Generating HTML report: ")
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
message(" ${FASTCOV_HTML_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup fastcov
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--zerocounters
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
COMMAND ${FASTCOV_CAPTURE_CMD}
COMMAND ${FASTCOV_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.info
${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.")
if(NOT Coverage_SKIP_HTML)
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
endif()
# Show where to find the fastcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
)
endfunction() # setup_target_for_coverage_fastcov
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags

View file

@ -27,6 +27,53 @@ macro ( ADD_FLUID_TEST _test )
endmacro ( ADD_FLUID_TEST )
macro ( ADD_FLUID_TEST_UTIL _util )
ADD_EXECUTABLE(${_util} ${_util}.c $<TARGET_OBJECTS:libfluidsynth-OBJ> )
# only build this unit test when explicitly requested by "make check"
set_target_properties(${_util} PROPERTIES EXCLUDE_FROM_ALL TRUE)
# append no-op generator expression to avoid VS or XCode from adding per-config subdirectories
set_target_properties(${_util} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test/utils/$<0:>)
# import necessary compile flags and dependency libraries
if ( FLUID_CPPFLAGS )
set_target_properties ( ${_util} PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} )
endif ( FLUID_CPPFLAGS )
TARGET_LINK_LIBRARIES(${_util} $<TARGET_PROPERTY:libfluidsynth,INTERFACE_LINK_LIBRARIES>)
# use the local include path to look for fluidsynth.h, as we cannot be sure fluidsynth is already installed
target_include_directories(${_util}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include> # include auto generated headers
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> # include "normal" public (sub-)headers
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src> # include private headers
$<TARGET_PROPERTY:libfluidsynth,INCLUDE_DIRECTORIES> # include all other header search paths needed by libfluidsynth (esp. glib)
)
# append the current unit test to check-target as dependency
add_dependencies(check ${_util})
endmacro ( ADD_FLUID_TEST_UTIL )
# This macro adds a test that writes its output to a file called
# <test>.output (in the current working dir) and then compares
# the content with the file given in _expected_output
macro ( ADD_FLUID_SF_DUMP_TEST _sfname)
set( test_args "${CMAKE_SOURCE_DIR}/sf2/${_sfname} ${_sfname}.yml" )
ADD_TEST(${_sfname}_dump_test
${CMAKE_COMMAND}
-Dtest_cmd=${CMAKE_BINARY_DIR}/test/utils/dump_sfont${CMAKE_EXECUTABLE_SUFFIX}
-Dtest_args=${test_args}
-Dtest_output=${_sfname}.yml
-Dexpected_output=${CMAKE_SOURCE_DIR}/sf2/${_sfname}.yml
-P ${CMAKE_SOURCE_DIR}/cmake_admin/RunOutputTest.cmake
)
endmacro ( ADD_FLUID_SF_DUMP_TEST )
macro ( ADD_FLUID_DEMO _demo )
ADD_EXECUTABLE(${_demo} ${_demo}.c )

View file

@ -0,0 +1,31 @@
if( NOT test_cmd )
message( FATAL_ERROR "test_cmd not defined" )
endif( NOT test_cmd )
if( NOT test_output )
message( FATAL_ERROR "test_output not defined" )
endif( NOT test_output )
if( NOT expected_output )
message( FATAL_ERROR "expected_output not defined" )
endif( NOT expected_output )
separate_arguments( test_args )
execute_process(
COMMAND ${test_cmd} ${test_args}
RESULT_VARIABLE test_not_successful
)
if( test_not_successful )
message( FATAL_ERROR "${test_cmd} ${test_args} returned error ${test_not_successful}!" )
endif( test_not_successful )
execute_process(
COMMAND ${CMAKE_COMMAND} -E compare_files ${expected_output} ${test_output}
RESULT_VARIABLE compare_not_successful
)
if( compare_not_successful )
message( SEND_ERROR "${test_output} does not match ${expected_output}!" )
endif( compare_not_successful )

View file

@ -234,6 +234,12 @@ else ( ENABLE_UBSAN )
set ( DEVEL_REPORT "${DEVEL_REPORT} UBSan (debug): no\n" )
endif ( ENABLE_UBSAN )
if ( ENABLE_COVERAGE )
set ( DEVEL_REPORT "${DEVEL_REPORT} Coverage: yes\n" )
else ( ENABLE_COVERAGE )
set ( DEVEL_REPORT "${DEVEL_REPORT} Coverage: no\n" )
endif ( ENABLE_COVERAGE )
message( STATUS
"\n**************************************************************\n"
"Build Summary:\n"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1241,7 +1241,10 @@ fluid_handle_reverb_command(void *data, int ac, char **av, fluid_ostream_t out,
/* name and min/max values table */
static struct value values[FLUID_REVERB_PARAM_LAST] =
{
{"room size"}, {"damp"}, {"width"}, {"level"}
{"room size", 0, 0},
{"damp", 0, 0},
{"width", 0, 0},
{"level", 0, 0}
};
FLUID_ENTRY_COMMAND(data);
@ -3553,7 +3556,7 @@ enum
int fluid_handle_player_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd)
{
FLUID_ENTRY_COMMAND(data);
int arg, was_running;
int arg = 0, was_running;
int seek = -1; /* current seek position in tick */
/* commands name table */

View file

@ -187,14 +187,14 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func
dev->data = data;
// Open the default output unit
ComponentDescription desc;
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput; //kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
Component comp = FindNextComponent(NULL, &desc);
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
@ -202,7 +202,7 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func
goto error_recovery;
}
status = OpenAComponent(comp, &dev->outputUnit);
status = AudioComponentInstanceNew(comp, &dev->outputUnit);
if(status != noErr)
{
@ -372,7 +372,7 @@ delete_fluid_core_audio_driver(fluid_audio_driver_t *p)
fluid_core_audio_driver_t *dev = (fluid_core_audio_driver_t *) p;
fluid_return_if_fail(dev != NULL);
CloseComponent(dev->outputUnit);
AudioComponentInstanceDispose(dev->outputUnit);
if(dev->buffers[0])
{

View file

@ -1203,10 +1203,6 @@ print_help(fluid_settings_t *settings)
" Number of audio buffers\n");
printf(" -C, --chorus\n"
" Turn the chorus on or off [0|1|yes|no, default = on]\n");
#ifdef WASAPI_SUPPORT
printf(" -D, --query-audio-devices\n"
" Probe all available soundcards for supported modes, sample-rates and sample-formats.\n");
#endif
printf(" -d, --dump\n"
" Dump incoming and outgoing MIDI events to stdout\n");
printf(" -E, --audio-file-endian\n"
@ -1244,6 +1240,10 @@ print_help(fluid_settings_t *settings)
" Audio file format for fast rendering or aufile driver (\"help\" for list)\n");
printf(" -p, --portname=[label]\n"
" Set MIDI port name (alsa_seq, coremidi drivers)\n");
#ifdef WASAPI_SUPPORT
printf(" -Q, --query-audio-devices\n"
" Probe all available soundcards for supported modes, sample-rates and sample-formats.\n");
#endif
printf(" -q, --quiet\n"
" Do not print welcome message or other informational output\n"
" (Windows only: also suppress all log messages lower than PANIC\n");

View file

@ -233,11 +233,11 @@ static int process_pdta(SFData *sf, int size);
static int load_phdr(SFData *sf, unsigned int size);
static int load_pbag(SFData *sf, int size);
static int load_pmod(SFData *sf, int size);
static int load_pgen(SFData *sf, int size);
extern int load_pgen(SFData *sf, int size);
static int load_ihdr(SFData *sf, unsigned int size);
static int load_ibag(SFData *sf, int size);
static int load_imod(SFData *sf, int size);
static int load_igen(SFData *sf, int size);
extern int load_igen(SFData *sf, int size);
static int load_shdr(SFData *sf, unsigned int size);
static int chunkid(uint32_t id);
@ -248,11 +248,6 @@ static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist);
static int valid_inst_genid(unsigned short genid);
static int valid_preset_genid(unsigned short genid);
static void delete_preset(SFPreset *preset);
static void delete_inst(SFInst *inst);
static void delete_zone(SFZone *zone);
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data);
static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24);
@ -1283,7 +1278,7 @@ static int load_pmod(SFData *sf, int size)
* if a generator follows an instrument discard it
* if a duplicate generator exists replace previous one
* ------------------------------------------------------------------- */
static int load_pgen(SFData *sf, int size)
int load_pgen(SFData *sf, int size)
{
fluid_list_t *dup;
fluid_list_t *preset_list;
@ -1784,7 +1779,7 @@ static int load_imod(SFData *sf, int size)
}
/* load instrument generators (see load_pgen for loading rules) */
static int load_igen(SFData *sf, int size)
int load_igen(SFData *sf, int size)
{
fluid_list_t *dup;
fluid_list_t *inst_list;
@ -2042,7 +2037,7 @@ static int load_shdr(SFData *sf, unsigned int size)
return TRUE;
}
static void delete_preset(SFPreset *preset)
void delete_preset(SFPreset *preset)
{
fluid_list_t *entry;
SFZone *zone;
@ -2066,7 +2061,7 @@ static void delete_preset(SFPreset *preset)
FLUID_FREE(preset);
}
static void delete_inst(SFInst *inst)
void delete_inst(SFInst *inst)
{
fluid_list_t *entry;
SFZone *zone;
@ -2092,7 +2087,7 @@ static void delete_inst(SFInst *inst)
/* Free all elements of a zone (Preset or Instrument) */
static void delete_zone(SFZone *zone)
void delete_zone(SFZone *zone)
{
fluid_list_t *entry;

View file

@ -183,4 +183,12 @@ int fluid_sffile_parse_presets(SFData *sf);
int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end,
int sample_type, short **data, char **data24);
/* extern only for unit test purposes */
int load_igen(SFData *sf, int size);
int load_pgen(SFData *sf, int size);
void delete_preset(SFPreset *preset);
void delete_inst(SFInst *inst);
void delete_zone(SFZone *zone);
#endif /* _FLUID_SFFILE_H */

View file

@ -23,73 +23,76 @@
#include "fluid_chan.h"
#define _GEN(_name) GEN_ ## _name, #_name
/* See SFSpec21 $8.1.3 */
static const fluid_gen_info_t fluid_gen_info[] =
{
/* number/name init nrpn-scale min max def */
{ GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f },
{ GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f },
{ GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
{ GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
{ GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f },
{ GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f },
{ GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f },
{ GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f },
{ GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f },
{ GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f },
{ GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
{ GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
{ GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f },
{ GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
{ GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
{ GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
{ GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f },
{ GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f },
{ GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f },
{ GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
{ GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f },
{ GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f },
{ GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f },
{ GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f },
{ GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f },
{ GEN_CUSTOM_BALANCE, 1, 0, -960.0f, 960.0f, 0.0f },
{ GEN_CUSTOM_FILTERFC, 1, 2, 0.0f, 22050.0f, 0.0f },
{ GEN_CUSTOM_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }
{ _GEN(STARTADDROFS), 1, 1, 0.0f, 1e10f, 0.0f },
{ _GEN(ENDADDROFS), 1, 1, -1e10f, 0.0f, 0.0f },
{ _GEN(STARTLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f },
{ _GEN(ENDLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f },
{ _GEN(STARTADDRCOARSEOFS), 0, 1, 0.0f, 1e10f, 0.0f },
{ _GEN(MODLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f },
{ _GEN(VIBLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f },
{ _GEN(MODENVTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f },
{ _GEN(FILTERFC), 1, 2, 1500.0f, 13500.0f, 13500.0f },
{ _GEN(FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f },
{ _GEN(MODLFOTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f },
{ _GEN(MODENVTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f },
{ _GEN(ENDADDRCOARSEOFS), 0, 1, -1e10f, 0.0f, 0.0f },
{ _GEN(MODLFOTOVOL), 1, 1, -960.0f, 960.0f, 0.0f },
{ _GEN(UNUSED1), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(CHORUSSEND), 1, 1, 0.0f, 1000.0f, 0.0f },
{ _GEN(REVERBSEND), 1, 1, 0.0f, 1000.0f, 0.0f },
{ _GEN(PAN), 1, 1, -500.0f, 500.0f, 0.0f },
{ _GEN(UNUSED2), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(UNUSED3), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(UNUSED4), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(MODLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(MODLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f },
{ _GEN(VIBLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(VIBLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f },
{ _GEN(MODENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(MODENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(MODENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(MODENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(MODENVSUSTAIN), 0, 1, 0.0f, 1000.0f, 0.0f },
{ _GEN(MODENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(KEYTOMODENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f },
{ _GEN(KEYTOMODENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f },
{ _GEN(VOLENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(VOLENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(VOLENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ _GEN(VOLENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(VOLENVSUSTAIN), 0, 1, 0.0f, 1440.0f, 0.0f },
{ _GEN(VOLENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ _GEN(KEYTOVOLENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f },
{ _GEN(KEYTOVOLENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f },
{ _GEN(INSTRUMENT), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(RESERVED1), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(KEYRANGE), 0, 0, 0.0f, 127.0f, 0.0f },
{ _GEN(VELRANGE), 0, 0, 0.0f, 127.0f, 0.0f },
{ _GEN(STARTLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f },
{ _GEN(KEYNUM), 1, 0, 0.0f, 127.0f, -1.0f },
{ _GEN(VELOCITY), 1, 1, 0.0f, 127.0f, -1.0f },
{ _GEN(ATTENUATION), 1, 1, 0.0f, 1440.0f, 0.0f },
{ _GEN(RESERVED2), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(ENDLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f },
{ _GEN(COARSETUNE), 0, 1, -120.0f, 120.0f, 0.0f },
{ _GEN(FINETUNE), 0, 1, -99.0f, 99.0f, 0.0f },
{ _GEN(SAMPLEID), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(SAMPLEMODE), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(RESERVED3), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(SCALETUNE), 0, 1, 0.0f, 1200.0f, 100.0f },
{ _GEN(EXCLUSIVECLASS), 0, 0, 0.0f, 0.0f, 0.0f },
{ _GEN(OVERRIDEROOTKEY), 1, 0, 0.0f, 127.0f, -1.0f },
{ _GEN(PITCH), 1, 0, 0.0f, 127.0f, 0.0f },
{ _GEN(CUSTOM_BALANCE), 1, 0, -960.0f, 960.0f, 0.0f },
{ _GEN(CUSTOM_FILTERFC), 1, 2, 0.0f, 22050.0f, 0.0f },
{ _GEN(CUSTOM_FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f }
};
/* fluid_gen_init
@ -122,3 +125,9 @@ fluid_real_t fluid_gen_scale_nrpn(int gen, int data)
fluid_clip(data, -8192, 8192);
return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale);
}
const char *fluid_gen_name(int gen)
{
return fluid_gen_info[gen].name;
}

View file

@ -27,6 +27,7 @@
typedef struct _fluid_gen_info_t
{
char num; /* Generator number */
char *name;
char init; /* Does the generator need to be initialized (not used) */
char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
float min; /* The minimum value */
@ -60,6 +61,7 @@ enum fluid_gen_flags
fluid_real_t fluid_gen_scale(int gen, float value);
fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn);
void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel);
const char *fluid_gen_name(int gen);
#endif /* _FLUID_GEN_H */

View file

@ -319,3 +319,19 @@ fluid_list_str_compare_func(void *a, void *b)
return 1;
}
int fluid_list_idx(fluid_list_t *list, void *data)
{
int i = 0;
while(list)
{
if (list->data == data)
{
return i;
}
list = list->next;
}
return -1;
}

View file

@ -52,6 +52,7 @@ fluid_list_t *fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
fluid_list_t *fluid_list_nth(fluid_list_t *list, int n);
fluid_list_t *fluid_list_last(fluid_list_t *list);
fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data);
int fluid_list_idx(fluid_list_t *list, void *data);
int fluid_list_size(fluid_list_t *list);
#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)

View file

@ -22,6 +22,7 @@ ADD_FLUID_TEST(test_synth_process)
ADD_FLUID_TEST(test_ct2hz)
ADD_FLUID_TEST(test_sample_validate)
ADD_FLUID_TEST(test_sfont_unloading)
ADD_FLUID_TEST(test_sfont_zone)
ADD_FLUID_TEST(test_seq_event_queue_sort)
ADD_FLUID_TEST(test_seq_scale)
ADD_FLUID_TEST(test_seq_evt_order)
@ -29,6 +30,11 @@ ADD_FLUID_TEST(test_seq_event_queue_remove)
ADD_FLUID_TEST(test_jack_obtaining_synth)
ADD_FLUID_TEST(test_utf8_open)
ADD_FLUID_TEST_UTIL(dump_sfont)
ADD_FLUID_SF_DUMP_TEST(VintageDreamsWaves-v2.sf2)
if ( LIBSNDFILE_HASVORBIS )
ADD_FLUID_TEST(test_sf3_sfont_loading)
ADD_FLUID_SF_DUMP_TEST(VintageDreamsWaves-v2.sf3)
endif ( LIBSNDFILE_HASVORBIS )

430
test/dump_sfont.c Normal file
View file

@ -0,0 +1,430 @@
#include "test.h"
#include "fluidsynth.h"
#include "fluid_sfont.h"
#include "fluid_defsfont.h"
#include "fluid_sys.h"
static void dump_sample(fluid_sample_t *sample);
static void dump_gens(const fluid_gen_t gen[]);
static void dump_mod(const fluid_mod_t *mod);
static void dump_preset_zone(fluid_preset_zone_t *zone);
static void dump_preset(fluid_preset_t *preset);
static void dump_inst_zone(fluid_inst_zone_t *zone);
static void dump_inst(fluid_inst_t *inst);
static void dump_defsfont(fluid_defsfont_t *defsfont);
static int inst_compare_func(void *a, void *b);
static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list);
#define FMT_BUFSIZE (4096)
static int indent_level = 0;
static FILE *output = NULL;
static void fmt(const char *format, ...);
static void indent(void);
static void outdent(void);
int main(int argc, char **argv)
{
int ret = FLUID_FAILED;
int id;
fluid_sfont_t *sfont;
fluid_defsfont_t *defsfont;
fluid_settings_t *settings;
fluid_synth_t *synth;
const char *adrivers[1] = { NULL };
if (argc < 2)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " dump_sfont <input_soundfont> [output_file]\n");
return FLUID_FAILED;
}
fluid_audio_driver_register(adrivers);
settings = new_fluid_settings();
if (settings == NULL)
{
return FLUID_FAILED;
}
synth = new_fluid_synth(settings);
if (synth == NULL)
{
goto EXIT;
}
id = fluid_synth_sfload(synth, argv[1], 1);
if (id < 0)
{
goto EXIT;
}
sfont = fluid_synth_get_sfont_by_id(synth, id);
if (sfont == NULL)
{
goto EXIT;
}
if (sfont->free != &fluid_defsfont_sfont_delete)
{
fprintf(stderr, "This tool only supports SoundFonts loaded by the default loader\n");
goto EXIT;
}
defsfont = (fluid_defsfont_t *)fluid_sfont_get_data(sfont);
if (defsfont == NULL)
{
goto EXIT;
}
if (argc < 3)
{
output = stdout;
}
else
{
output = fopen(argv[2], "w");
if (output == NULL)
{
fprintf(stderr, "Unable to open output file %s", argv[2]);
goto EXIT;
}
}
dump_defsfont(defsfont);
ret = FLUID_OK;
EXIT:
if (output && output != stdout)
{
fclose(output);
}
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return ret;
}
static void dump_sample(fluid_sample_t *sample)
{
fmt("name: %s", sample->name);
fmt("source_start: %u", sample->source_start);
fmt("source_end: %u", sample->source_end);
fmt("source_loopstart: %u", sample->source_loopstart);
fmt("source_loopend: %u", sample->source_loopend);
fmt("start: %u", sample->start);
fmt("end: %u", sample->end);
fmt("loopstart: %u", sample->loopstart);
fmt("loopend: %u", sample->loopend);
fmt("samplerate: %u", sample->samplerate);
fmt("origpitch: %u", sample->origpitch);
fmt("pitchadj: %u", sample->pitchadj);
fmt("sampletype: %u", sample->sampletype);
}
static void dump_gens(const fluid_gen_t gen[])
{
int i;
/* only dump generators if at least one is set */
for (i = 0; i < GEN_LAST; i++)
{
if (gen[i].flags)
{
break;
}
}
if (i == GEN_LAST)
{
return;
}
fmt("generators:");
indent();
for (i = 0; i < GEN_LAST; i++)
{
if (gen[i].flags)
{
fmt("%s: %.2f", fluid_gen_name(i), gen[i].val);
}
}
outdent();
}
static void dump_mod(const fluid_mod_t *mod)
{
fmt("dest: %s", fluid_gen_name(mod->dest));
fmt("src1: %u", mod->src1);
fmt("flags1: %u", mod->flags1);
fmt("src2: %u", mod->src2);
fmt("flags2: %u", mod->flags2);
fmt("amount: %.2f", mod->amount);
}
static void dump_preset_zone(fluid_preset_zone_t *zone)
{
int i;
fluid_mod_t *mod;
fmt("name: %s", zone->name);
if (zone->inst)
{
fmt("instrument: %s (index %d)", zone->inst->name, zone->inst->source_idx);
}
fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi);
fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi);
dump_gens(zone->gen);
if (zone->mod)
{
fmt("modulators:");
for (i = 0, mod = zone->mod; mod; mod = mod->next, i++)
{
fmt("- modulator: %d", i);
indent();
dump_mod(mod);
outdent();
}
}
}
static void dump_preset(fluid_preset_t *preset)
{
int i;
fluid_preset_zone_t *zone;
fluid_defpreset_t *defpreset = fluid_preset_get_data(preset);
if (defpreset == NULL)
{
return;
}
fmt("name: %s", defpreset->name);
fmt("bank: %u", defpreset->bank);
fmt("num: %u", defpreset->num);
if (defpreset->global_zone)
{
fmt("global_zone:");
indent();
dump_preset_zone(defpreset->global_zone);
outdent();
}
fmt("zones:");
for (i = 0, zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone), i++)
{
fmt("- zone: %d", i);
if (zone == NULL)
{
continue;
}
indent();
dump_preset_zone(zone);
outdent();
}
}
static void dump_inst_zone(fluid_inst_zone_t *zone)
{
int i;
fluid_mod_t *mod;
fmt("name: %s", zone->name);
if (zone->sample)
{
fmt("sample: %s", zone->sample->name);
}
fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi);
fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi);
dump_gens(zone->gen);
if (zone->mod)
{
fmt("modulators:");
for (i = 0, mod = zone->mod; mod; mod = mod->next, i++)
{
fmt("- modulator: %d", i);
indent();
dump_mod(mod);
outdent();
}
}
}
static void dump_inst(fluid_inst_t *inst)
{
int i;
fluid_inst_zone_t *zone;
fmt("name: %s", inst->name);
if (inst->global_zone)
{
fmt("global_zone:");
indent();
dump_inst_zone(inst->global_zone);
outdent();
}
fmt("zones:");
for (i = 0, zone = inst->zone; zone; zone = fluid_inst_zone_next(zone), i++)
{
fmt("- zone: %d", i);
if (zone == NULL)
{
continue;
}
indent();
dump_inst_zone(zone);
outdent();
}
}
static int inst_compare_func(void *a, void *b)
{
const fluid_inst_t *inst_a = a;
const fluid_inst_t *inst_b = b;
return inst_a->source_idx - inst_b->source_idx;
}
static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list)
{
fluid_preset_zone_t *zone;
fluid_defpreset_t *defpreset = fluid_preset_get_data(preset);
if (defpreset == NULL)
{
return inst_list;
}
if (defpreset->global_zone && defpreset->global_zone->inst &&
fluid_list_idx(inst_list, defpreset->global_zone->inst) == -1)
{
inst_list = fluid_list_prepend(inst_list, defpreset->global_zone->inst);
}
for (zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone))
{
if (zone->inst && (fluid_list_idx(inst_list, zone->inst) == -1))
{
inst_list = fluid_list_prepend(inst_list, zone->inst);
}
}
return inst_list;
}
static void dump_defsfont(fluid_defsfont_t *defsfont)
{
int i;
fluid_list_t *list;
fluid_sample_t *sample;
fluid_preset_t *preset;
fluid_inst_t *inst;
fluid_list_t *inst_list = NULL;
fmt("samplepos: %u", defsfont->samplepos);
fmt("samplesize: %u", defsfont->samplesize);
fmt("sample24pos: %u", defsfont->sample24pos);
fmt("sample24size: %u", defsfont->sample24size);
fmt("presets:");
for (i = 0, list = defsfont->preset; list; list = fluid_list_next(list), i++)
{
preset = (fluid_preset_t *)fluid_list_get(list);
fmt("- preset: %d", i);
if (preset == NULL)
{
continue;
}
indent();
dump_preset(preset);
outdent();
fmt("");
inst_list = collect_preset_insts(preset, inst_list);
}
inst_list = fluid_list_sort(inst_list, (fluid_compare_func_t)inst_compare_func);
fmt("instruments:");
for (list = inst_list; list; list = fluid_list_next(list))
{
inst = (fluid_inst_t *)fluid_list_get(list);
fmt("- instrument: %d", inst->source_idx);
indent();
dump_inst(inst);
outdent();
fmt("");
}
delete_fluid_list(inst_list);
fmt("samples:");
for (i = 0, list = defsfont->sample; list; list = fluid_list_next(list), i++)
{
sample = (fluid_sample_t *)fluid_list_get(list);
fmt("- sample: %d", i);
if (sample == NULL)
{
continue;
}
indent();
dump_sample(sample);
outdent();
fmt("");
}
}
static void fmt(const char *format, ...)
{
char buf[FMT_BUFSIZE];
va_list args;
int len;
int i;
va_start(args, format);
len = FLUID_VSNPRINTF(buf, FMT_BUFSIZE, format, args);
va_end(args);
if (len < 0)
{
FLUID_LOG(FLUID_ERR, "max buffer size exceeded");
return;
}
buf[FMT_BUFSIZE - 1] = '\0';
for (i = 0; i < indent_level; i++)
{
fprintf(output, " ");
}
fwrite(buf, 1, FLUID_STRLEN(buf), output);
fprintf(output, "\n");
}
static void indent(void)
{
indent_level += 1;
}
static void outdent(void)
{
if (indent_level > 0)
{
indent_level -= 1;
}
}

479
test/test_sfont_zone.c Normal file
View file

@ -0,0 +1,479 @@
#include "test.h"
#include "fluidsynth.h"
#include "sfloader/fluid_sfont.h"
#include "sfloader/fluid_defsfont.h"
#include "sfloader/fluid_sffile.h"
#include "utils/fluid_sys.h"
#define SET_BUF2(START, SIZE) \
do \
{ \
file_buf = START; \
file_end = (START) + (SIZE); \
} while (0)
#define SET_BUF(BUF) SET_BUF2(BUF, FLUID_N_ELEMENTS(BUF))
#define UNSET_BUF \
do \
{ \
file_buf = NULL; \
file_end = NULL; \
} while (0)
typedef struct
{
// pointer to the start of the file_buf
const unsigned char *start;
// actual size of the buffer
unsigned int size;
// expected end address of the buffer
const unsigned char *end;
} buf_t;
static const unsigned char *file_buf = NULL;
static const unsigned char *file_end = NULL;
static int test_reader(void *buf, fluid_long_long_t count, void *h)
{
if (file_buf + count > file_end)
{
return FLUID_FAILED;
}
FLUID_MEMCPY(buf, file_buf, count);
file_buf += count;
return FLUID_OK;
}
static int test_seek(void *handle, fluid_long_long_t offset, int origin)
{
if (origin == SEEK_CUR)
{
file_buf += offset;
if (file_buf > file_end)
{
return FLUID_FAILED;
}
return FLUID_OK;
}
// shouldn't happen?
TEST_ASSERT(0);
}
static const fluid_file_callbacks_t fcb =
{
NULL, &test_reader, &test_seek, NULL, NULL
};
static SFZone* new_test_zone(fluid_list_t** parent_list, int gen_count)
{
int i;
SFZone *zone = FLUID_NEW(SFZone);
TEST_ASSERT(zone != NULL);
FLUID_MEMSET(zone, 0, sizeof(*zone));
for (i = 0; i < gen_count; i++)
{
zone->gen = fluid_list_prepend(zone->gen, NULL);
}
if(parent_list != NULL)
{
*parent_list = fluid_list_append(*parent_list, zone);
}
return zone;
}
// test the good case first: one zone, with two generators and one terminal generator
static void good_test_1zone_2gen_1termgen(int (*load_func)(SFData *sf, int size), SFData* sf, SFZone *zone)
{
const SFGen *gen;
static const unsigned char buf[] =
{
GEN_KEYRANGE, 0, 60, 127, GEN_VELRANGE, 0, 60, 127, 0, 0, 0, 0
};
SET_BUF(buf);
TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf)));
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_KEYRANGE);
TEST_ASSERT(gen->amount.range.lo == 60);
TEST_ASSERT(gen->amount.range.hi == 127);
gen = fluid_list_get(fluid_list_nth(zone->gen, 1));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_VELRANGE);
TEST_ASSERT(gen->amount.range.lo == 60);
TEST_ASSERT(gen->amount.range.hi == 127);
TEST_ASSERT(file_buf == buf + sizeof(buf));
UNSET_BUF;
}
// bad case: too few generators in buffer, triggering a chunk size mismatch
static void bad_test_too_short_gen_buffer(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone)
{
const unsigned char final_gen = (load_func == &load_pgen) ? GEN_INSTRUMENT : GEN_SAMPLEID;
SFGen *gen;
unsigned int i;
static const unsigned char buf1[] = { GEN_KEYRANGE, 0, 0 };
static const unsigned char buf2[] = { GEN_KEYRANGE, 0 };
static const unsigned char buf3[] = { GEN_KEYRANGE };
static const unsigned char buf8[] = { GEN_VELRANGE, 0, 0 };
static const unsigned char buf9[] = { GEN_VELRANGE, 0 };
static const unsigned char buf10[] = { GEN_VELRANGE };
static const unsigned char buf4[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE, 0, 4 };
static const unsigned char buf5[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE, 0 };
static const unsigned char buf6[] = { GEN_VELRANGE, 0, 0, 127, GEN_COARSETUNE };
const unsigned char buf11[] = { GEN_VELRANGE, 0, 0, 127, final_gen, 0, 4 };
const unsigned char buf12[] = { GEN_VELRANGE, 0, 0, 127, final_gen, 0 };
const unsigned char buf13[] = { GEN_VELRANGE, 0, 0, 127, final_gen };
static const unsigned char buf7[] = { GEN_KEYRANGE, 0, 60, 127, GEN_OVERRIDEROOTKEY };
static const buf_t buf_with_one_gen[] =
{
{ buf1, sizeof(buf1), buf1 + sizeof(buf1) },
{ buf2, sizeof(buf2),buf2 + sizeof(buf2) },
{ buf3, sizeof(buf3), buf3 },
{ buf8, sizeof(buf8), buf8 + sizeof(buf8) },
{ buf9, sizeof(buf9), buf9 + sizeof(buf9) },
{ buf10, sizeof(buf10), buf10 }
};
const buf_t buf_with_two_gen[] =
{
{ buf4, sizeof(buf4), buf4 + sizeof(buf4) -1 },
{ buf5, sizeof(buf5), buf5 + sizeof(buf5) },
{ buf6, sizeof(buf6), buf6 + sizeof(buf6) - 1 },
{ buf11, sizeof(buf11), buf11 + sizeof(buf11) - 1 },
{ buf12, sizeof(buf12), buf12 + sizeof(buf12) },
{ buf13, sizeof(buf13), buf13 + sizeof(buf13) -1}
};
for (i = 0; i < FLUID_N_ELEMENTS(buf_with_one_gen); i++)
{
SET_BUF2(buf_with_one_gen[i].start, buf_with_one_gen[i].size);
TEST_ASSERT(load_func(sf, 8 /* pretend that our input buffer is big enough, to make it fail in the fcbs later */) == FALSE);
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf_with_one_gen[i].end);
UNSET_BUF;
}
for (i = 0; i < FLUID_N_ELEMENTS(buf_with_two_gen); i++)
{
SET_BUF2(buf_with_two_gen[i].start, buf_with_two_gen[i].size);
TEST_ASSERT(load_func(sf, 8) == FALSE);
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen != NULL);
FLUID_FREE(gen);
zone->gen->data = NULL;
gen = fluid_list_get(fluid_list_nth(zone->gen, 1));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf_with_two_gen[i].end);
UNSET_BUF;
}
SET_BUF(buf7);
TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf7)) == FALSE);
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_KEYRANGE);
TEST_ASSERT(gen->amount.range.lo == 60);
TEST_ASSERT(gen->amount.range.hi == 127);
TEST_ASSERT(file_buf == buf7 + sizeof(buf7) - 1);
UNSET_BUF;
}
// bad case: one zone, with two similar generators
static void bad_test_duplicate_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone)
{
const SFGen *gen;
static const unsigned char buf[] = { GEN_COARSETUNE, 0, 5, 0, GEN_COARSETUNE, 0, 10, 0 };
SET_BUF(buf);
TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf)));
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_COARSETUNE);
TEST_ASSERT(gen->amount.range.lo == 10);
TEST_ASSERT(gen->amount.range.hi == 0);
gen = fluid_list_get(fluid_list_nth(zone->gen, 1));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf + sizeof(buf));
UNSET_BUF;
}
// bad case: with one zone, generators in wrong order
static void bad_test_gen_wrong_order(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone)
{
const SFGen *gen;
static const unsigned char buf[] =
{
GEN_VELRANGE, 0, 60, 127,
GEN_KEYRANGE, 0, 60, 127,
GEN_INSTRUMENT, 0, 0xDD, 0xDD
};
SET_BUF(buf);
TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf)));
gen = fluid_list_get(fluid_list_nth(zone->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_VELRANGE);
TEST_ASSERT(gen->amount.range.lo == 60);
TEST_ASSERT(gen->amount.range.hi == 127);
gen = fluid_list_get(fluid_list_nth(zone->gen, 1));
if (load_func == &load_igen)
{
TEST_ASSERT(gen == NULL);
}
else
{
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_INSTRUMENT);
TEST_ASSERT(gen->amount.uword == 0xDDDDu);
}
gen = fluid_list_get(fluid_list_nth(zone->gen, 2));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf + sizeof(buf));
UNSET_BUF;
}
// This test-case is derived from the invalid SoundFont provided in #808
static void bad_test_issue_808(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1)
{
const SFGen *gen;
static const unsigned char buf[] =
{
// zone 1
GEN_REVERBSEND, 0, 50, 0,
GEN_VOLENVRELEASE, 0, 206, 249,
// zone 2
GEN_KEYRANGE, 0, 0, 35,
GEN_OVERRIDEROOTKEY, 0, 43, 0,
GEN_STARTADDRCOARSEOFS, 0, 0, 0,
GEN_SAMPLEMODE, 0, 1, 0,
GEN_STARTADDROFS, 0, 0, 0
};
SET_BUF(buf);
TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf)));
gen = fluid_list_get(fluid_list_nth(zone1->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_REVERBSEND);
TEST_ASSERT(gen->amount.range.lo == 50);
TEST_ASSERT(gen->amount.range.hi == 0);
gen = fluid_list_get(fluid_list_nth(zone1->gen, 1));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_VOLENVRELEASE);
TEST_ASSERT(gen->amount.range.lo == 206);
TEST_ASSERT(gen->amount.range.hi == 249);
gen = fluid_list_get(fluid_list_nth(zone1->gen, 2));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf + sizeof(buf));
UNSET_BUF;
}
// This test-case has a single zone which has additional generators after the final generator, while some of them are incomplete and others still have an extra (maybe incomplete) terminal gen.
static void bad_test_additional_gens_after_final_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1)
{
unsigned int i;
SFGen *gen;
const unsigned char final_gen = (load_func == &load_pgen) ? GEN_INSTRUMENT : GEN_SAMPLEID;
const unsigned char buf1[] =
{
// zone 1
GEN_KEYRANGE, 0, 60, 127,
GEN_UNUSED1, 0, 0xFF, 0xFF,
final_gen, 0, 0xDD, 0xDD,
GEN_KEYRANGE, 0, 0, 35,
GEN_OVERRIDEROOTKEY, 0, 43, 0,
0, 0, 0, 0 // terminal generator
};
const unsigned char buf2[] =
{
// zone 1
GEN_KEYRANGE, 0, 60, 127,
GEN_UNUSED1, 0, 0xFF, 0xFF,
final_gen, 0, 0xDD, 0xDD,
GEN_KEYRANGE, 0, 0, 35,
GEN_OVERRIDEROOTKEY, 0, 43, 0,
0, 0, 0 // incomplete terminal generator
};
const unsigned char buf3[] =
{
// zone 1
GEN_KEYRANGE, 0, 60, 127,
GEN_UNUSED1, 0, 0xFF, 0xFF,
final_gen, 0, 0xDD, 0xDD,
GEN_KEYRANGE, 0, 0, 35,
GEN_OVERRIDEROOTKEY, 0, 43
};
const unsigned char buf4[] =
{
// zone 1
GEN_KEYRANGE, 0, 60, 127,
GEN_UNUSED1, 0, 0xFF, 0xFF,
final_gen, 0, 0xDD, 0xDD,
GEN_KEYRANGE, 0, 0, 35,
GEN_OVERRIDEROOTKEY, 0
};
const buf_t buf[] =
{
{ buf1, sizeof(buf1), buf1 + sizeof(buf1) },
{ buf2, sizeof(buf2), buf2 + sizeof(buf2) - 3 },
{ buf3, sizeof(buf3), buf3 + sizeof(buf3) - 3 },
{ buf4, sizeof(buf4), buf4 + sizeof(buf4) - 2 },
};
// the first test case should return true, all others false
int expected_ret_val = TRUE;
for (i = 0; i < FLUID_N_ELEMENTS(buf); i++)
{
SET_BUF2(buf[i].start, buf[i].size);
TEST_ASSERT(load_func(sf, buf[i].size) == expected_ret_val);
expected_ret_val = FALSE;
gen = fluid_list_get(fluid_list_nth(zone1->gen, 0));
TEST_ASSERT(gen != NULL);
TEST_ASSERT(gen->id == GEN_KEYRANGE);
TEST_ASSERT(gen->amount.range.lo == 60);
TEST_ASSERT(gen->amount.range.hi == 127);
// delete this generator
FLUID_FREE(gen);
zone1->gen->data = NULL;
gen = fluid_list_get(fluid_list_nth(zone1->gen, 1));
TEST_ASSERT(gen != NULL);
if (load_func == &load_igen)
{
TEST_ASSERT(gen->id == GEN_SAMPLEID);
}
else
{
TEST_ASSERT(gen->id == GEN_INSTRUMENT);
}
TEST_ASSERT(gen->amount.uword == 0xDDDDu);
// delete this generator
FLUID_FREE(gen);
zone1->gen->data = NULL;
gen = fluid_list_get(fluid_list_nth(zone1->gen, 2));
TEST_ASSERT(gen == NULL);
gen = fluid_list_get(fluid_list_nth(zone1->gen, 3));
TEST_ASSERT(gen == NULL);
gen = fluid_list_get(fluid_list_nth(zone1->gen, 4));
TEST_ASSERT(gen == NULL);
TEST_ASSERT(file_buf == buf[i].end);
UNSET_BUF;
// The test cases above expect zone1 to be pre-populated with 5 generators
delete_fluid_list(zone1->gen);
zone1->gen = NULL;
zone1->gen = fluid_list_prepend(zone1->gen, NULL);
zone1->gen = fluid_list_prepend(zone1->gen, NULL);
zone1->gen = fluid_list_prepend(zone1->gen, NULL);
zone1->gen = fluid_list_prepend(zone1->gen, NULL);
zone1->gen = fluid_list_prepend(zone1->gen, NULL);
}
}
int main(void)
{
// prepare a soundfont that has one preset and one instrument, with up to 2 zones
SFZone *zone1;
SFData *sf = FLUID_NEW(SFData);
SFPreset *preset = FLUID_NEW(SFPreset);
SFInst *inst = FLUID_NEW(SFInst);
TEST_ASSERT(sf != NULL);
FLUID_MEMSET(sf, 0, sizeof(*sf));
TEST_ASSERT(preset != NULL);
FLUID_MEMSET(preset, 0, sizeof(*preset));
TEST_ASSERT(inst != NULL);
FLUID_MEMSET(inst, 0, sizeof(*inst));
sf->fcbs = &fcb;
sf->preset = fluid_list_append(sf->preset, preset);
sf->inst = fluid_list_append(sf->inst, inst);
// Calls the given test function for 1 zone once for preset and once for inst case.
#define TEST_CASE_1(TEST_FUNC, GEN_COUNT) \
do \
{ \
zone1 = new_test_zone(&preset->zone, GEN_COUNT); \
TEST_FUNC(&load_pgen, sf, zone1); \
delete_zone(zone1); \
delete_fluid_list(preset->zone); \
preset->zone = NULL; \
\
zone1 = new_test_zone(&inst->zone, GEN_COUNT); \
TEST_FUNC(&load_igen, sf, zone1); \
delete_zone(zone1); \
delete_fluid_list(inst->zone); \
inst->zone = NULL; \
} while (0)
TEST_CASE_1(good_test_1zone_2gen_1termgen, 2);
TEST_CASE_1(good_test_1zone_2gen_1termgen, 3);
TEST_CASE_1(bad_test_too_short_gen_buffer, 2);
TEST_CASE_1(bad_test_duplicate_gen, 2);
TEST_CASE_1(bad_test_gen_wrong_order, 3);
TEST_CASE_1(bad_test_additional_gens_after_final_gen, 5);
zone1 = new_test_zone(&preset->zone, 2);
(void)new_test_zone(&preset->zone, 5);
bad_test_issue_808(&load_pgen, sf, zone1);
// zone 2 was dropped
TEST_ASSERT(preset->zone->next == NULL);
delete_zone(zone1);
// zone2 already deleted
delete_fluid_list(preset->zone);
preset->zone = NULL;
zone1 = new_test_zone(&inst->zone, 2);
(void)new_test_zone(&inst->zone, 5);
bad_test_issue_808(&load_igen, sf, zone1);
// zone 2 was dropped
TEST_ASSERT(inst->zone->next == NULL);
delete_zone(zone1);
// zone2 already deleted
delete_fluid_list(inst->zone);
inst->zone = NULL;
delete_inst(inst);
delete_preset(preset);
delete_fluid_list(sf->inst);
delete_fluid_list(sf->preset);
// we cannot call fluid_sffile_close here, because it would destroy the mutex which is not initialized
FLUID_FREE(sf);
return EXIT_SUCCESS;
}