Merge branch 'master' into cleanup21

This commit is contained in:
derselbst 2019-01-27 10:48:07 +01:00
commit 70727b2f7a
74 changed files with 4359 additions and 2288 deletions

View File

@ -1,5 +1,4 @@
image:
- Visual Studio 2015
- Visual Studio 2017
build:

View File

@ -1,6 +1,5 @@
image:
- Visual Studio 2015
- Visual Studio 2017
build:
parallel: true

View File

@ -1,5 +1,5 @@
---
Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cert-*,clang-analyzer-*,performance-*,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-delete-null-pointer,readability-implicit-bool-conversion,readability-inconsistent-declaration-parameter-name,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-simplify-boolean-expr'
Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cert-*,clang-analyzer-*,performance-*,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-delete-null-pointer,readability-implicit-bool-conversion,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-simplify-boolean-expr'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false

View File

@ -16,7 +16,7 @@ We strongly recommend to use it! Feel free to edit or remove inapplicable/unneed
-->
### FluidSynth version <!-- enter FluidSynths version you're using -->
1.1.x
2.0.x
### Current behavior
<!-- Describe the current situation, e.g. how the bug manifests. -->

View File

@ -1,221 +1,64 @@
language: c
#sudo: required
sudo: false
os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0
- llvm-toolchain-trusty-7
packages:
- cmake-data
- cmake
- libglib2.0-0
- libsndfile-dev
- libasound2-dev
- libjack-dev
- portaudio19-dev
- libpulse-dev
- libdbus-glib-1-dev
- ladspa-sdk
env:
- CMAKE_FLAGS="-Denable-profiling=1"
- CMAKE_FLAGS="-Denable-floats=1 -Denable-profiling=1"
- CMAKE_FLAGS="-Denable-floats=0"
- CMAKE_FLAGS="-Denable-trap-on-fpe=1"
- CMAKE_FLAGS="-Denable-fpe-check=1"
- CMAKE_FLAGS="-Denable-ipv6=0"
- CMAKE_FLAGS="-Denable-network=0"
- CMAKE_FLAGS="-Denable-aufile=0"
- CMAKE_FLAGS="-DBUILD_SHARED_LIBS=0"
matrix:
include:
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- cmake-data
- cmake
- libglib2.0-0
- libsndfile-dev
- libasound2-dev
- libjack-dev
- portaudio19-dev
- libpulse-dev
- libdbus-glib-1-dev
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8"
- CMAKE_FLAGS="-Denable-floats=1"
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- cmake-data
- cmake
- libglib2.0-0
- libsndfile-dev
- libasound2-dev
- libjack-dev
- portaudio19-dev
- libpulse-dev
- libdbus-glib-1-dev
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8"
- CMAKE_FLAGS="-Denable-floats=0"
# works on Precise and Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
- CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
# works on Precise and Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-6
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-6 && CXX=g++-6"
# works on Precise and Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-7
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
# works on Precise and Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
# works on Precise and Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.8
packages:
- clang-3.8
- cmake-data
- cmake
- libglib2.0-0
- libsndfile-dev
- libasound2-dev
- libjack-dev
- portaudio19-dev
- libpulse-dev
- libdbus-glib-1-dev
- ladspa-sdk
env:
- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
# works on Trusty
- os: linux
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
packages:
- clang-4.0
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0"
# works on Trusty
- os: linux
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"
# works on Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0
packages:
- clang-6.0
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0"
# works on Trusty
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
packages:
- clang-7
- cmake-data
- cmake
- libglib2.0-0
- ladspa-sdk
env:
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7"
- os: osx
osx_image: xcode8
env:
- MATRIX_EVAL="brew install gcc5 glib gettext && brew link gettext && CC=gcc-5 && CXX=g++-5" # gettext should provide libintl, but doesnt work
osx_image: xcode10
- os: osx
osx_image: xcode8
- os: linux
env:
- MATRIX_EVAL="brew install gcc6 glib gettext && brew link gettext && CC=gcc-6 && CXX=g++-6"
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && sudo apt-get install gcc-7"
- os: osx
osx_image: xcode8
- os: linux
env:
- MATRIX_EVAL="brew install gcc glib gettext libsndfile jack dbus-glib pulseaudio portaudio && brew link gettext && CC=gcc-8 && CXX=g++-8"
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && sudo apt-get install gcc-8"
- CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold"
- os: linux
env:
- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8 && sudo apt-get install clang-3.8"
- os: linux
env:
- MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0 && sudo apt-get install clang-6.0"
- os: linux
env:
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && sudo apt-get install clang-7"
before_install:
- if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get update; else brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib gettext libsndfile jack dbus-glib pulseaudio portaudio; fi # && brew link gettext
- eval "${MATRIX_EVAL}"
before_script:
@ -223,7 +66,7 @@ before_script:
- mkdir build && cd build
script:
- cmake -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install ${CMAKE_FLAGS} "-Denable-portaudio=1" "-Denable-ladspa=1" "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=1" ..
- cmake -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install ${CMAKE_FLAGS} -Denable-portaudio=1 -Denable-ladspa=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 ..
- make -j4
- make check
- if [ $TRAVIS_OS_NAME = linux ]; then make install; fi # 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.

View File

@ -29,7 +29,7 @@ set ( PACKAGE "fluidsynth" )
# FluidSynth package version
set ( FLUIDSYNTH_VERSION_MAJOR 2 )
set ( FLUIDSYNTH_VERSION_MINOR 0 )
set ( FLUIDSYNTH_VERSION_MICRO 1 )
set ( FLUIDSYNTH_VERSION_MICRO 3 )
set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" )
set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" )
@ -43,8 +43,8 @@ set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" )
# if any interfaces have been removed/changed (compatibility broken): AGE=0
# 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 0 )
set ( LIB_VERSION_REVISION 1 )
set ( LIB_VERSION_AGE 1 )
set ( LIB_VERSION_REVISION 0 )
set ( LIB_VERSION_INFO
"${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" )
@ -67,9 +67,14 @@ option ( enable-libsndfile "compile libsndfile support (if it is available)" on
option ( enable-midishare "compile MidiShare support (if it is available)" on )
option ( enable-network "enable network support (requires BSD sockets)" on )
option ( enable-oss "compile OSS support (if it is available)" on )
option ( enable-dsound "compile DirectSound support (if it is available)" on )
option ( enable-waveout "compile Windows WaveOut support (if it is available)" on )
option ( enable-winmidi "compile Windows MIDI support (if it is available)" on )
option ( enable-sdl2 "compile SDL2 audio support (if it is available)" on )
option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on )
option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on )
option ( enable-readline "compile readline lib line editing (if it is available)" on )
option ( enable-threads "enable multi-threading support (such as parallel voice synthesis)" on )
# Platform specific options
if ( CMAKE_SYSTEM MATCHES "Linux" )
@ -190,15 +195,40 @@ endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE
# Windows
unset ( WINDOWS_SUPPORT CACHE )
unset ( WINDOWS_LIBS CACHE )
unset ( DSOUND_SUPPORT CACHE )
unset ( WAVEOUT_SUPPORT CACHE )
unset ( WINMIDI_SUPPORT CACHE )
unset ( MINGW32 CACHE )
if ( WIN32 )
include ( CheckIncludeFiles )
# Check presence of MS include files
check_include_file ( windows.h HAVE_WINDOWS_H )
check_include_file ( io.h HAVE_IO_H )
check_include_file ( dsound.h HAVE_DSOUND_H )
check_include_files ( "windows.h;mmsystem.h" HAVE_MMSYSTEM_H )
set ( WINDOWS_SUPPORT ${HAVE_WINDOWS_H} )
set ( WINDOWS_LIBS "dsound;winmm;ws2_32" )
if ( enable-network )
set ( WINDOWS_LIBS "${WINDOWS_LIBS};ws2_32" )
endif ( enable-network )
if ( enable-dsound AND HAVE_DSOUND_H )
set ( WINDOWS_LIBS "${WINDOWS_LIBS};dsound" )
set ( DSOUND_SUPPORT 1 )
endif ()
if ( enable-winmidi AND HAVE_MMSYSTEM_H )
set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" )
set ( WINMIDI_SUPPORT 1 )
endif ()
if ( enable-waveout AND HAVE_MMSYSTEM_H )
set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" )
set ( WAVEOUT_SUPPORT 1 )
endif ()
set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" )
set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" )
if ( MSVC )
@ -229,7 +259,7 @@ if ( WIN32 )
# MinGW compiler (a Windows GCC port)
if ( MINGW )
set ( MINGW32 1 )
add_definitions ( -mms-bitfields )
add_compile_options ( -mms-bitfields )
endif ( MINGW )
else ( WIN32 )
# Check PThreads, but not in Windows
@ -320,6 +350,18 @@ if ( enable-profiling )
endif ( )
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPT_FLAGS}" )
if ( CMAKE_VERSION VERSION_GREATER "3.6.0" )
find_program( CLANG_TIDY
NAMES "clang-tidy"
DOC "Path to clang-tidy executable" )
if ( CLANG_TIDY )
message ( STATUS "Found clang-tidy" )
set ( CMAKE_C_CLANG_TIDY "clang-tidy" )
endif ( CLANG_TIDY )
endif ( CMAKE_VERSION VERSION_GREATER "3.6.0" )
endif ( enable-profiling )
unset ( ENABLE_TRAPONFPE CACHE )
@ -353,22 +395,6 @@ if ( CMAKE_BUILD_TYPE MATCHES "Debug" )
set ( DEBUG 1 )
endif ( CMAKE_BUILD_TYPE MATCHES "Debug" )
if ( CMAKE_VERSION VERSION_GREATER "3.6.0" )
find_program( CLANG_TIDY
NAMES "clang-tidy"
DOC "Path to clang-tidy executable" )
if ( CLANG_TIDY )
message ( STATUS "Found clang-tidy" )
# whenever clang-tidy is available, use it to automatically add braces after ever "make"
if ( WITH_PROFILING )
set ( CMAKE_C_CLANG_TIDY "clang-tidy" )
else ( WITH_PROFILING )
set ( CMAKE_C_CLANG_TIDY "clang-tidy;-checks=-*,readability-braces-*;-format-style=file" )
endif ( WITH_PROFILING )
endif ( CLANG_TIDY )
endif ( CMAKE_VERSION VERSION_GREATER "3.6.0" )
# Additional targets to perform clang-format/clang-tidy
# Get all project files
file(GLOB_RECURSE
@ -530,6 +556,20 @@ if ( enable-oss )
set ( OSS_SUPPORT ${OSS_FOUND} )
endif ( enable-oss )
unset ( SDL2_SUPPORT CACHE )
unset ( SDL2_INCLUDE_DIR CACHE )
unset ( SDL2_LIBRARY CACHE )
if ( enable-sdl2 )
find_package ( SDL2 )
if ( SDL2_FOUND )
set ( SDL2_SUPPORT ${SDL2_FOUND} )
else ( SDL2_FOUND)
unset ( SDL2_INCLUDE_DIR CACHE )
unset ( SDL2_LIBRARY CACHE )
endif ( SDL2_FOUND )
endif ( enable-sdl2 )
unset ( MIDISHARE_SUPPORT CACHE )
if ( enable-midishare )
find_package ( MidiShare QUIET )
@ -555,6 +595,11 @@ else ( enable-readline )
unset ( READLINE_LIBS CACHE )
endif ( enable-readline )
unset ( ENABLE_MIXER_THREADS CACHE )
if ( enable-threads )
set ( ENABLE_MIXER_THREADS 1 )
endif ( enable-threads )
unset ( HAVE_OPENMP CACHE )
find_package ( OpenMP QUIET )
if ( OpenMP_FOUND OR OpenMP_C_FOUND )

164
cmake_admin/FindSDL2.cmake Normal file
View File

@ -0,0 +1,164 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2_SEARCH_PATHS}
)
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

View File

@ -1,5 +1,5 @@
#include "VersionInfo.h"
#include "winres.h"
#include "winver.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION_RESOURCE

View File

@ -74,6 +74,30 @@ else ( WINDOWS_SUPPORT )
message ( "Windows: no" )
endif ( WINDOWS_SUPPORT )
if ( DSOUND_SUPPORT )
message ( "DSound: yes" )
else ( DSOUND_SUPPORT )
message ( "DSound: no" )
endif ( DSOUND_SUPPORT )
if ( WAVEOUT_SUPPORT )
message ( "WaveOut support: yes" )
else ( WAVEOUT_SUPPORT )
message ( "WaveOut support: no" )
endif ( WAVEOUT_SUPPORT )
if ( WINMIDI_SUPPORT )
message ( "WinMidi support: yes" )
else ( WINMIDI_SUPPORT )
message ( "WinMidi support: no" )
endif ( WINMIDI_SUPPORT )
if ( SDL2_SUPPORT )
message ( "SDL2 support: yes" )
else ( SDL2_SUPPORT )
message ( "SDL2 support: no" )
endif ( SDL2_SUPPORT )
if ( LADSPA_SUPPORT )
message ( "LADSPA support: yes" )
else ( LADSPA_SUPPORT )
@ -134,6 +158,12 @@ else ( HAVE_OPENMP )
message ( "OpenMP 4.0: no" )
endif ( HAVE_OPENMP )
if ( ENABLE_MIXER_THREADS )
message ( "Multi-thread support yes" )
else ( ENABLE_MIXER_THREADS )
message ( "Multi-thread support no" )
endif ( ENABLE_MIXER_THREADS )
if ( ENABLE_DEBUG )
message ( "Debug: yes" )
else ( ENABLE_DEBUG )

View File

@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = libfluidsynth
PROJECT_NUMBER = 2.0.1
PROJECT_NUMBER = 2.0.3
OUTPUT_DIRECTORY = api
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English

View File

@ -367,9 +367,9 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
coreaudio (Mac OS X),<br />
dart (OS/2)
</def>
<vals>jack, alsa, oss, pulseaudio, coreaudio, dsound, portaudio, sndman, dart, file</vals>
<vals>alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sdl2, sndman, waveout</vals>
<desc>
The audio system to be used.
The audio system to be used. In order to use sdl2 as audio driver, the application is responsible for initializing SDL's audio subsystem.<br /><br /><strong>Note:</strong> sdl2 and waveout are available since fluidsynth 2.1.
</desc>
</setting>
<setting>
@ -584,7 +584,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
<type>bool</type>
<def>0 (FALSE)</def>
<desc>
If 1 (TRUE), automatically connects FluidSynth to available MIDI input ports. alsa_seq and coremidi are currently the only drivers making use of this.
If 1 (TRUE), automatically connects FluidSynth to available MIDI input ports. alsa_seq, coremidi and jack are currently the only drivers making use of this.
</desc>
</setting>
<setting>

View File

@ -7,9 +7,9 @@
\author Josh Green
\author David Henningsson
\author Tom Moebert
\author Copyright &copy; 2003-2018 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert
\version Revision 2.0.1
\date 2018-10-07
\author Copyright &copy; 2003-2019 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert
\version Revision 2.0.3
\date 2019-01-01
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.
@ -21,6 +21,8 @@ All the source code examples in this document are in the public domain; you can
- \ref Disclaimer
- \ref Introduction
- \ref NewIn2_0_3
- \ref NewIn2_0_2
- \ref NewIn2_0_0
- \ref CreatingSettings
- \ref CreatingSynth
@ -60,6 +62,18 @@ What is FluidSynth?
- FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org
\section NewIn2_0_3 Whats new in 2.0.3?
- fix incorrect behaviour of fluid_sample_set_sound_data()
- add missing getters for midi events:
- fluid_midi_event_get_text()
- fluid_midi_event_get_lyrics()
\section NewIn2_0_2 Whats new in 2.0.2?
- fluid_synth_error() has been deprecated, use fluid_set_log_function() to interfere log messages
\section NewIn2_0_0 Whats new in 2.0.0?
FluidSynths major version was bumped. The API was reworked, deprecated functions were removed.
@ -67,6 +81,7 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions
<strong><span style="color:red">Important changes that may not result in a compilation error but may cause your app to misbehave:</span></strong>
- all public \c fluid_settings_* functions that return an integer which is not meant to be interpreted as bool consistently return either FLUID_OK or FLUID_FAILED
- fluid_settings_setstr() cannot be used to set integer (toggle) settings with "yes" or "no" values anymore. Use fluid_settings_setint() instead, for example: <br /><code>fluid_settings_setint(settings, "synth.reverb.active", 0)</code> instead of <code>fluid_settings_setstr(settings, "synth.reverb.active", "no")</code>
- explicit client unregistering is required for fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth()
- all public functions consistently receive signed integers for soundfont ids, bank and program numbers
- use unique device names for the "audio.portaudio.device" setting
@ -76,8 +91,9 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions
- all public \c delete_* functions return void and are safe when called with NULL
- the shell command handler was decoupled internally, as a consequence the param list of new_fluid_server() and new_fluid_cmd_handler() was adapted
- \c fluid_settings_set* functions no longer silently register unknown settings but return an error instead
- reverb: roomsize is now limited to an upper threshold of 1.0 to avoid exponential volume increase
- rename fluid_mod_new() and fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod()
- rename \c fluid_mod_new() and \c fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod()
- rename chorus getters to match naming conventions: fluid_synth_get_chorus_speed() and fluid_synth_get_chorus_depth()
- fluid_synth_remove_sfont() returns FLUID_OK or FLUID_FAILED
- introduce a separate data type for sequencer client IDs: #fluid_seq_id_t
@ -193,7 +209,7 @@ For a full list of available <strong>synthesizer settings</strong>, please refer
The synthesizer itself does not write any audio to the audio output. This allows application developers to manage the audio output themselves if they wish. The next section describes the use of the synthesizer without an audio driver in more detail.
Creating the audio driver is straightforward: set the appropriate settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description.
Creating the audio driver is straightforward: set the <code>audio.driver</code> settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description.
- jack: JACK Audio Connection Kit (Linux, Mac OS X, Windows)
- alsa: Advanced Linux Sound Architecture (Linux)
@ -205,6 +221,7 @@ Creating the audio driver is straightforward: set the appropriate settings and c
- sndman: Apple SoundManager (Mac OS Classic)
- dart: DART sound driver (OS/2)
- file: Driver to output audio to a file
- sdl2*: Simple DirectMedia Layer (Linux, Windows, Mac OS X, iOS, Android, FreeBSD, Haiku, etc.)
The default audio driver depends on the settings with which FluidSynth was compiled. You can get the default driver with fluid_settings_getstr_default(). To get the list of available drivers use the fluid_settings_foreach_option() function. Finally, you can set the driver with fluid_settings_setstr(). In most cases, the default driver should work out of the box.
@ -232,7 +249,7 @@ As soon as the audio driver is created, it will start playing. The audio driver
There are a number of general audio driver settings. The audio.driver settings define the audio subsystem that will be used. The audio.periods and audio.period-size settings define the latency and robustness against scheduling delays. There are additional settings for the audio subsystems used. For a full list of available <strong>audio driver settings</strong>, please refer to <a href="fluidsettings.xml" target="_blank"><strong>FluidSettings Documentation</strong></a>.
<strong>*Note:</strong> In order to use sdl2 as audio driver, the application is responsible for initializing SDL (e.g. with SDL_Init()). This must be done <strong>before</strong> the first call to <code>new_fluid_settings()</code>! Also make sure to call SDL_Quit() after all fluidsynth instances have been destroyed.
\section UsingSynth Using the synthesizer without an audio driver
@ -579,8 +596,8 @@ void createsynth()
{
fluid_settings_t* settings;
settings = new_fluid_settings();
fluid_settings_setstr(settings, "synth.reverb.active", "yes");
fluid_settings_setstr(settings, "synth.chorus.active", "no");
fluid_settings_setint(settings, "synth.reverb.active", 0);
fluid_settings_setint(settings, "synth.chorus.active", 0);
synth = new_fluid_synth(settings);
adriver = new_fluid_audio_driver(settings, synth);
sequencer = new_fluid_sequencer2(0);

View File

@ -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 "Oct 6, 2018"
.TH FluidSynth 1 "Nov 18, 2018"
.\" Please update the above date whenever this man page is modified.
.\"
.\" Some roff macros, for reference:

View File

@ -53,8 +53,12 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *dat
int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt,
void *data, int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt,
void **data, int *size);
FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt,
void *data, int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt,
void **data, int *size);
/**
* MIDI router rule type.

View File

@ -184,12 +184,10 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont);
/**
* Virtual SoundFont preset iteration function.
* @param sfont Virtual SoundFont
* @param preset Caller supplied uninitialized buffer to fill in with current preset information
* @return NULL when no more presets are available, otherwise the a pointer to the current preset
*
* Should store preset information to the caller supplied \a preset structure
* and advance the internal iteration state to the next preset for subsequent
* calls.
* Returns preset information to the caller. The returned buffer is only valid until a subsequent
* call to this function.
*/
typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont);

View File

@ -233,7 +233,7 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p
/* Misc */
FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth);
FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth);
FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth);
/* Default modulators */

View File

@ -32,6 +32,7 @@ include_directories (
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PTHREADS_INCLUDE_DIR}
${SDL2_INCLUDE_DIR}
)
include_directories (
@ -78,9 +79,21 @@ if ( PORTAUDIO_SUPPORT )
include_directories ( ${PORTAUDIO_INCLUDE_DIRS} )
endif ( PORTAUDIO_SUPPORT )
if ( WINDOWS_SUPPORT )
set ( fluid_windows_SOURCES drivers/fluid_dsound.c drivers/fluid_winmidi.c )
endif ( WINDOWS_SUPPORT )
if ( DSOUND_SUPPORT )
set ( fluid_dsound_SOURCES drivers/fluid_dsound.c )
endif ( DSOUND_SUPPORT )
if ( WAVEOUT_SUPPORT )
set ( fluid_waveout_SOURCES drivers/fluid_waveout.c )
endif ( WAVEOUT_SUPPORT )
if ( WINMIDI_SUPPORT )
set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c )
endif ( WINMIDI_SUPPORT )
if ( SDL2_SUPPORT )
set ( fluid_sdl2_SOURCES drivers/fluid_sdl2.c )
endif ( SDL2_SUPPORT )
if ( OSS_SUPPORT )
set ( fluid_oss_SOURCES drivers/fluid_oss.c )
@ -105,6 +118,10 @@ if ( MIDISHARE_SUPPORT )
include_directories ( ${MidiShare_INCLUDE_DIRS} )
endif ( MIDISHARE_SUPPORT )
if ( AUFILE_SUPPORT )
set ( fluid_aufile_SOURCES drivers/fluid_aufile.c )
endif ( AUFILE_SUPPORT )
set ( config_SOURCES ${CMAKE_BINARY_DIR}/config.h )
set ( libfluidsynth_SOURCES
@ -172,7 +189,6 @@ set ( libfluidsynth_SOURCES
drivers/fluid_adriver.h
drivers/fluid_mdriver.c
drivers/fluid_mdriver.h
drivers/fluid_aufile.c
bindings/fluid_cmd.c
bindings/fluid_cmd.h
bindings/fluid_filerenderer.c
@ -209,7 +225,7 @@ configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth/version.h.in
configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth.cmake
${public_main_HEADER} )
if ( WIN32 AND NOT MINGW )
if ( WIN32 )
include(generate_product_version)
generate_product_version(
VersionFilesOutputVariable
@ -224,11 +240,12 @@ generate_product_version(
ORIGINAL_FILENAME "libfluidsynth.dll"
FILE_DESCRIPTION "Fluidsynth"
)
endif ( WIN32 AND NOT MINGW )
endif ( WIN32 )
add_library ( libfluidsynth-OBJ OBJECT
${config_SOURCES}
${fluid_alsa_SOURCES}
${fluid_aufile_SOURCES}
${fluid_coreaudio_SOURCES}
${fluid_coremidi_SOURCES}
${fluid_dart_SOURCES}
@ -239,7 +256,10 @@ add_library ( libfluidsynth-OBJ OBJECT
${fluid_oss_SOURCES}
${fluid_portaudio_SOURCES}
${fluid_pulse_SOURCES}
${fluid_windows_SOURCES}
${fluid_dsound_SOURCES}
${fluid_waveout_SOURCES}
${fluid_winmidi_SOURCES}
${fluid_sdl2_SOURCES}
${libfluidsynth_SOURCES}
${public_HEADERS}
${public_main_HEADER}
@ -307,6 +327,7 @@ target_link_libraries ( libfluidsynth
${PULSE_LIBRARIES}
${PORTAUDIO_LIBRARIES}
${LIBSNDFILE_LIBRARIES}
${SDL2_LIBRARY}
${DBUS_LIBRARIES}
${READLINE_LIBS}
${DART_LIBS}
@ -351,3 +372,13 @@ else ( MACOSX_FRAMEWORK )
install ( FILES ${public_main_HEADER} DESTINATION ${INCLUDE_INSTALL_DIR} )
endif ( MACOSX_FRAMEWORK )
# ******* Auto Generated Lookup Tables ******
include(ExternalProject)
ExternalProject_Add(gentables
DOWNLOAD_COMMAND ""
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gentables
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gentables
INSTALL_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gentables/make_tables.exe "${CMAKE_BINARY_DIR}/"
)
add_dependencies(libfluidsynth-OBJ gentables)

View File

@ -28,11 +28,6 @@
#include "fluid_sfont.h"
#include "fluid_chan.h"
#if WITH_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
/* FIXME: LADSPA used to need a lot of parameters on a single line. This is not
* necessary anymore, so the limits below could probably be reduced */
#define MAX_TOKENS 100
@ -522,15 +517,6 @@ fluid_shell_run(void *data)
break;
}
#if WITH_READLINE
if(shell->in == fluid_get_stdin())
{
add_history(workline);
}
#endif
/* handle the command */
switch(fluid_command(shell->handler, workline, shell->out))
{

View File

@ -76,6 +76,9 @@
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#cmakedefine HAVE_NETINET_TCP_H @HAVE_NETINET_TCP_H@
/* Define if compiling the mixer with multi-thread support */
#cmakedefine ENABLE_MIXER_THREADS @ENABLE_MIXER_THREADS@
/* Define if compiling with openMP to enable parallel audio rendering */
#cmakedefine HAVE_OPENMP @HAVE_OPENMP@
@ -190,6 +193,18 @@
/* Define to enable PulseAudio driver */
#cmakedefine PULSE_SUPPORT @PULSE_SUPPORT@
/* Define to enable DirectSound driver */
#cmakedefine DSOUND_SUPPORT @DSOUND_SUPPORT@
/* Define to enable Windows WaveOut driver */
#cmakedefine WAVEOUT_SUPPORT @WAVEOUT_SUPPORT@
/* Define to enable Windows MIDI driver */
#cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@
/* Define to enable SDL2 audio driver */
#cmakedefine SDL2_SUPPORT @SDL2_SUPPORT@
/* Define to 1 if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS @STDC_HEADERS@

View File

@ -25,7 +25,7 @@
* fluid_adriver_definition_t
*/
typedef struct _fluid_audriver_definition_t
struct _fluid_audriver_definition_t
{
const char *name;
fluid_audio_driver_t *(*new)(fluid_settings_t *settings, fluid_synth_t *synth);
@ -34,7 +34,7 @@ typedef struct _fluid_audriver_definition_t
void *data);
void (*free)(fluid_audio_driver_t *driver);
void (*settings)(fluid_settings_t *settings);
} fluid_audriver_definition_t;
};
/* Available audio drivers, listed in order of preference */
static const fluid_audriver_definition_t fluid_audio_drivers[] =
@ -59,16 +59,6 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if OSS_SUPPORT
{
"oss",
new_fluid_oss_audio_driver,
new_fluid_oss_audio_driver2,
delete_fluid_oss_audio_driver,
fluid_oss_audio_driver_settings
},
#endif
#if PULSE_SUPPORT
{
"pulseaudio",
@ -79,6 +69,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if OSS_SUPPORT
{
"oss",
new_fluid_oss_audio_driver,
new_fluid_oss_audio_driver2,
delete_fluid_oss_audio_driver,
fluid_oss_audio_driver_settings
},
#endif
#if COREAUDIO_SUPPORT
{
"coreaudio",
@ -99,13 +99,13 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if PORTAUDIO_SUPPORT
#if WAVEOUT_SUPPORT
{
"portaudio",
new_fluid_portaudio_driver,
"waveout",
new_fluid_waveout_audio_driver,
NULL,
delete_fluid_portaudio_driver,
fluid_portaudio_driver_settings
delete_fluid_waveout_audio_driver,
fluid_waveout_audio_driver_settings
},
#endif
@ -119,6 +119,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if PORTAUDIO_SUPPORT
{
"portaudio",
new_fluid_portaudio_driver,
NULL,
delete_fluid_portaudio_driver,
fluid_portaudio_driver_settings
},
#endif
#if DART_SUPPORT
{
"dart",
@ -129,6 +139,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if SDL2_SUPPORT
{
"sdl2",
new_fluid_sdl2_audio_driver,
NULL,
delete_fluid_sdl2_audio_driver,
fluid_sdl2_audio_driver_settings
},
#endif
#if AUFILE_SUPPORT
{
"file",
@ -138,6 +158,8 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
NULL
},
#endif
/* NULL terminator to avoid zero size array if no driver available */
{ NULL, NULL, NULL, NULL, NULL }
};
#define ENABLE_AUDIO_DRIVER(_drv, _idx) \
@ -151,6 +173,7 @@ static uint8_t fluid_adriver_disable_mask[(FLUID_N_ELEMENTS(fluid_audio_drivers)
void fluid_audio_driver_settings(fluid_settings_t *settings)
{
unsigned int i;
const char *def_name = NULL;
fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0);
fluid_settings_add_option(settings, "audio.sample-format", "16bits");
@ -169,72 +192,32 @@ void fluid_audio_driver_settings(fluid_settings_t *settings)
fluid_settings_register_int(settings, "audio.realtime-prio",
FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0);
/* Set the default driver */
#if JACK_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "jack", 0);
#elif ALSA_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "alsa", 0);
#elif PULSE_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "pulseaudio", 0);
#elif OSS_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "oss", 0);
#elif COREAUDIO_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "coreaudio", 0);
#elif DSOUND_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "dsound", 0);
#elif SNDMAN_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "sndman", 0);
#elif PORTAUDIO_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "portaudio", 0);
#elif DART_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "dart", 0);
#elif AUFILE_SUPPORT
fluid_settings_register_str(settings, "audio.driver", "file", 0);
#else
fluid_settings_register_str(settings, "audio.driver", "", 0);
#endif
/* Add all drivers to the list of options */
#if PULSE_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "pulseaudio");
#endif
#if ALSA_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "alsa");
#endif
#if OSS_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "oss");
#endif
#if COREAUDIO_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "coreaudio");
#endif
#if DSOUND_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "dsound");
#endif
#if SNDMAN_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "sndman");
#endif
#if PORTAUDIO_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "portaudio");
#endif
#if JACK_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "jack");
#endif
#if DART_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "dart");
#endif
#if AUFILE_SUPPORT
fluid_settings_add_option(settings, "audio.driver", "file");
#endif
for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++)
for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++)
{
/* Select the default driver */
if (def_name == NULL)
{
def_name = fluid_audio_drivers[i].name;
}
/* Add the driver to the list of options */
fluid_settings_add_option(settings, "audio.driver", fluid_audio_drivers[i].name);
if(fluid_audio_drivers[i].settings != NULL &&
IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i))
{
fluid_audio_drivers[i].settings(settings);
}
}
/* Set the default driver, if any */
if(def_name != NULL)
{
fluid_settings_setstr(settings, "audio.driver", def_name);
}
}
static const fluid_audriver_definition_t *
@ -244,7 +227,7 @@ find_fluid_audio_driver(fluid_settings_t *settings)
char *name;
char *allnames;
for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++)
for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++)
{
/* If this driver is de-activated, just ignore it */
if(!IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i))
@ -259,21 +242,26 @@ find_fluid_audio_driver(fluid_settings_t *settings)
}
}
allnames = fluid_settings_option_concat(settings, "audio.driver", NULL);
fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */
FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver %s. Valid drivers are: %s.",
name ? name : "NULL", allnames ? allnames : "ERROR");
if(name)
FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver '%s'.", name ? name : "NULL");
allnames = fluid_settings_option_concat(settings, "audio.driver", NULL);
if(allnames != NULL)
{
FLUID_FREE(name);
}
if(allnames[0] != '\0')
{
FLUID_LOG(FLUID_INFO, "Valid drivers are: %s", allnames);
}
else
{
FLUID_LOG(FLUID_INFO, "No audio drivers available.");
}
if(allnames)
{
FLUID_FREE(allnames);
}
FLUID_FREE(name);
return NULL;
}
@ -298,7 +286,7 @@ new_fluid_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
if(driver)
{
driver->name = def->name;
driver->define = def;
}
return driver;
@ -340,7 +328,7 @@ new_fluid_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, voi
if(driver)
{
driver->name = def->name;
driver->define = def;
}
}
@ -359,18 +347,8 @@ new_fluid_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, voi
void
delete_fluid_audio_driver(fluid_audio_driver_t *driver)
{
unsigned int i;
fluid_return_if_fail(driver != NULL);
/* iterate over fluid_audio_drivers_template to ensure deleting even drivers currently not registered */
for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++)
{
if(fluid_audio_drivers[i].name == driver->name)
{
fluid_audio_drivers[i].free(driver);
return;
}
}
driver->define->free(driver);
}
@ -417,8 +395,8 @@ int fluid_audio_driver_register(const char **adrivers)
{
unsigned int j;
/* search the requested audio driver in the template and copy it over if found */
for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers); j++)
/* search the requested audio driver in the template and enable it if found */
for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; j++)
{
if(FLUID_STRCMP(adrivers[i], fluid_audio_drivers[j].name) == 0)
{
@ -427,19 +405,13 @@ int fluid_audio_driver_register(const char **adrivers)
}
}
if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers))
if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers) - 1)
{
/* requested driver not found, failure */
return FLUID_FAILED;
}
}
if(i >= FLUID_N_ELEMENTS(fluid_audio_drivers))
{
/* user requested more drivers than this build of fluidsynth supports, failure */
return FLUID_FAILED;
}
/* Update list of activated drivers */
FLUID_MEMCPY(fluid_adriver_disable_mask, disable_mask, sizeof(disable_mask));

View File

@ -27,9 +27,11 @@
* fluid_audio_driver_t
*/
typedef struct _fluid_audriver_definition_t fluid_audriver_definition_t;
struct _fluid_audio_driver_t
{
const char *name;
const fluid_audriver_definition_t *define;
};
void fluid_audio_driver_settings(fluid_settings_t *settings);
@ -81,6 +83,13 @@ void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *p);
void fluid_dsound_audio_driver_settings(fluid_settings_t *settings);
#endif
#if WAVEOUT_SUPPORT
fluid_audio_driver_t *new_fluid_waveout_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);
void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *p);
void fluid_waveout_audio_driver_settings(fluid_settings_t *settings);
#endif
#if PORTAUDIO_SUPPORT
void fluid_portaudio_driver_settings(fluid_settings_t *settings);
fluid_audio_driver_t *new_fluid_portaudio_driver(fluid_settings_t *settings,
@ -112,6 +121,13 @@ void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p);
void fluid_dart_audio_driver_settings(fluid_settings_t *settings);
#endif
#if SDL2_SUPPORT
fluid_audio_driver_t *new_fluid_sdl2_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);
void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *p);
void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings);
#endif
#if AUFILE_SUPPORT
fluid_audio_driver_t *new_fluid_file_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);

View File

@ -29,6 +29,8 @@
#include "fluid_settings.h"
#if AUFILE_SUPPORT
/** fluid_file_audio_driver_t
*
* This structure should not be accessed directly. Use audio port
@ -129,3 +131,5 @@ static int fluid_file_audio_run_s16(void *d, unsigned int clock_time)
return fluid_file_renderer_process_block(dev->renderer) == FLUID_OK ? 1 : 0;
}
#endif /* AUFILE_SUPPORT */

View File

@ -144,7 +144,7 @@ fluid_audio_driver_t *
new_fluid_core_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
return new_fluid_core_audio_driver2(settings,
fluid_synth_process,
NULL,
synth);
}
@ -266,10 +266,7 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func
}
}
if(devname)
{
FLUID_FREE(devname); /* free device name */
}
FLUID_FREE(devname); /* free device name */
dev->buffer_size = period_size * periods;
@ -322,6 +319,12 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func
dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size);
dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size);
if(dev->buffers[0] == NULL || dev->buffers[1] == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
goto error_recovery;
}
// Initialize the audio unit
status = AudioUnitInitialize(dev->outputUnit);

View File

@ -82,6 +82,8 @@ struct _fluid_jack_midi_driver_t
int midi_port_count;
jack_port_t **midi_port; // array of midi port handles
fluid_midi_parser_t *parser;
int autoconnect_inputs;
int autoconnect_is_outdated;
};
static fluid_jack_client_t *new_fluid_jack_client(fluid_settings_t *settings,
@ -94,7 +96,7 @@ void fluid_jack_driver_shutdown(void *arg);
int fluid_jack_driver_srate(jack_nframes_t nframes, void *arg);
int fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg);
int fluid_jack_driver_process(jack_nframes_t nframes, void *arg);
void fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg);
static fluid_mutex_t last_client_mutex = FLUID_MUTEX_INIT; /* Probably not necessary, but just in case drivers are created by multiple threads */
static fluid_jack_client_t *last_client = NULL; /* Last unpaired client. For audio/MIDI driver pairing. */
@ -109,6 +111,31 @@ fluid_jack_audio_driver_settings(fluid_settings_t *settings)
fluid_settings_register_str(settings, "audio.jack.server", "", 0);
}
/*
* Connect all midi input ports to all terminal midi output ports
*/
void
fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *midi_driver) {
int i, j;
const char ** midi_source_ports;
midi_source_ports = jack_get_ports(client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal);
if(midi_source_ports != NULL)
{
for(j = 0; midi_source_ports[j] != NULL; j++)
{
for(i = 0; i < midi_driver->midi_port_count; i++)
{
FLUID_LOG(FLUID_INFO, "jack midi autoconnect \"%s\" to \"%s\"", midi_source_ports[j], jack_port_name(midi_driver->midi_port[i]));
jack_connect(client, midi_source_ports[j], jack_port_name(midi_driver->midi_port[i]));
}
}
jack_free(midi_source_ports);
}
midi_driver->autoconnect_is_outdated = FALSE;
}
/*
* Create Jack client as necessary, share clients of the same server.
* @param settings Settings object
@ -213,6 +240,7 @@ new_fluid_jack_client(fluid_settings_t *settings, int isaudio, void *driver)
goto error_recovery;
}
jack_set_port_registration_callback(client_ref->client, fluid_jack_port_registration, client_ref);
jack_set_process_callback(client_ref->client, fluid_jack_driver_process, client_ref);
jack_set_buffer_size_callback(client_ref->client, fluid_jack_driver_bufsize, client_ref);
jack_set_sample_rate_callback(client_ref->client, fluid_jack_driver_srate, client_ref);
@ -629,6 +657,11 @@ fluid_jack_driver_process(jack_nframes_t nframes, void *arg)
if(midi_driver)
{
if(midi_driver->autoconnect_is_outdated)
{
fluid_jack_midi_autoconnect(client->client, midi_driver);
}
for(i = 0; i < midi_driver->midi_port_count; i++)
{
midi_buffer = jack_port_get_buffer(midi_driver->midi_port[i], 0);
@ -728,6 +761,15 @@ fluid_jack_driver_shutdown(void *arg)
/* exit (1); */
}
void
fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg)
{
fluid_jack_client_t *client_ref = (fluid_jack_client_t *)arg;
if(client_ref->midi_driver != NULL)
{
client_ref->midi_driver->autoconnect_is_outdated = client_ref->midi_driver->autoconnect_inputs && is_registering != 0;
}
}
void fluid_jack_midi_driver_settings(fluid_settings_t *settings)
{
@ -770,6 +812,9 @@ new_fluid_jack_midi_driver(fluid_settings_t *settings,
return NULL;
}
fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconnect_inputs);
dev->autoconnect_is_outdated = dev->autoconnect_inputs;
dev->client_ref = new_fluid_jack_client(settings, FALSE, dev);
if(!dev->client_ref)

View File

@ -21,21 +21,11 @@
#include "fluid_mdriver.h"
#include "fluid_settings.h"
#undef FLUID_MIDI_SUPPORT
#if ALSA_SUPPORT || JACK_SUPPORT || OSS_SUPPORT || \
WINMIDI_SUPPORT || MIDISHARE_SUPPORT || COREMIDI_SUPPORT
/* At least an input driver exits */
#define FLUID_MIDI_SUPPORT 1
#endif
#ifdef FLUID_MIDI_SUPPORT
/*
* fluid_mdriver_definition
*/
struct fluid_mdriver_definition_t
struct _fluid_mdriver_definition_t
{
const char *name;
fluid_midi_driver_t *(*new)(fluid_settings_t *settings,
@ -46,8 +36,22 @@ struct fluid_mdriver_definition_t
};
static const struct fluid_mdriver_definition_t fluid_midi_drivers[] =
static const fluid_mdriver_definition_t fluid_midi_drivers[] =
{
#if ALSA_SUPPORT
{
"alsa_seq",
new_fluid_alsa_seq_driver,
delete_fluid_alsa_seq_driver,
fluid_alsa_seq_driver_settings
},
{
"alsa_raw",
new_fluid_alsa_rawmidi_driver,
delete_fluid_alsa_rawmidi_driver,
fluid_alsa_rawmidi_driver_settings
},
#endif
#if JACK_SUPPORT
{
"jack",
@ -64,20 +68,6 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] =
fluid_oss_midi_driver_settings
},
#endif
#if ALSA_SUPPORT
{
"alsa_raw",
new_fluid_alsa_rawmidi_driver,
delete_fluid_alsa_rawmidi_driver,
fluid_alsa_rawmidi_driver_settings
},
{
"alsa_seq",
new_fluid_alsa_seq_driver,
delete_fluid_alsa_seq_driver,
fluid_alsa_seq_driver_settings
},
#endif
#if WINMIDI_SUPPORT
{
"winmidi",
@ -102,70 +92,45 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] =
fluid_coremidi_driver_settings
},
#endif
/* NULL terminator to avoid zero size array if no driver available */
{ NULL, NULL, NULL, NULL }
};
#endif /* FLUID_MIDI_SUPPORT */
void fluid_midi_driver_settings(fluid_settings_t *settings)
{
#ifdef FLUID_MIDI_SUPPORT
unsigned int i;
#endif
const char *def_name = NULL;
fluid_settings_register_int(settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED);
fluid_settings_register_int(settings, "midi.realtime-prio",
FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0);
/* Set the default driver */
#if ALSA_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "alsa_seq", 0);
#elif JACK_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "jack", 0);
#elif OSS_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "oss", 0);
#elif WINMIDI_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "winmidi", 0);
#elif MIDISHARE_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "midishare", 0);
#elif COREMIDI_SUPPORT
fluid_settings_register_str(settings, "midi.driver", "coremidi", 0);
#else
fluid_settings_register_str(settings, "midi.driver", "", 0);
#endif
/* Add all drivers to the list of options */
#if ALSA_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "alsa_seq");
fluid_settings_add_option(settings, "midi.driver", "alsa_raw");
#endif
#if JACK_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "jack");
#endif
#if OSS_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "oss");
#endif
#if WINMIDI_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "winmidi");
#endif
#if MIDISHARE_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "midishare");
#endif
#if COREMIDI_SUPPORT
fluid_settings_add_option(settings, "midi.driver", "coremidi");
#endif
#ifdef FLUID_MIDI_SUPPORT
for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++)
for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++)
{
/* Select the default driver */
if (def_name == NULL)
{
def_name = fluid_midi_drivers[i].name;
}
/* Add the driver to the list of options */
fluid_settings_add_option(settings, "midi.driver", fluid_midi_drivers[i].name);
if(fluid_midi_drivers[i].settings != NULL)
{
fluid_midi_drivers[i].settings(settings);
}
}
#endif
/* Set the default driver, if any */
if(def_name != NULL)
{
fluid_settings_setstr(settings, "midi.driver", def_name);
}
}
/**
@ -178,37 +143,42 @@ void fluid_midi_driver_settings(fluid_settings_t *settings)
*/
fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data)
{
#ifdef FLUID_MIDI_SUPPORT
fluid_midi_driver_t *driver = NULL;
char *allnames;
unsigned int i;
const fluid_mdriver_definition_t *def;
for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++)
for(def = fluid_midi_drivers; def->name != NULL; def++)
{
if(fluid_settings_str_equal(settings, "midi.driver", fluid_midi_drivers[i].name))
if(fluid_settings_str_equal(settings, "midi.driver", def->name))
{
FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", fluid_midi_drivers[i].name);
driver = fluid_midi_drivers[i].new(settings, handler, event_handler_data);
FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", def->name);
driver = def->new(settings, handler, event_handler_data);
if(driver)
{
driver->name = fluid_midi_drivers[i].name;
driver->define = def;
}
return driver;
}
}
FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver.");
allnames = fluid_settings_option_concat(settings, "midi.driver", NULL);
FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver. Valid drivers are: %s.",
allnames ? allnames : "ERROR");
if(allnames)
if(allnames != NULL)
{
if(allnames[0] != '\0')
{
FLUID_LOG(FLUID_INFO, "Valid drivers are: %s", allnames);
}
else
{
FLUID_LOG(FLUID_INFO, "No MIDI drivers available.");
}
FLUID_FREE(allnames);
}
#endif
return NULL;
}
@ -218,18 +188,6 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi
*/
void delete_fluid_midi_driver(fluid_midi_driver_t *driver)
{
#ifdef FLUID_MIDI_SUPPORT
unsigned int i;
fluid_return_if_fail(driver != NULL);
for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++)
{
if(fluid_midi_drivers[i].name == driver->name)
{
fluid_midi_drivers[i].free(driver);
return;
}
}
#endif
driver->define->free(driver);
}

View File

@ -27,9 +27,11 @@
* fluid_midi_driver_t
*/
typedef struct _fluid_mdriver_definition_t fluid_mdriver_definition_t;
struct _fluid_midi_driver_t
{
const char *name;
const fluid_mdriver_definition_t *define;
handle_midi_event_func_t handler;
void *data;
};

View File

@ -176,7 +176,6 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
}
buf = FLUID_ARRAY(float, period_size * 2);
if(buf == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
@ -196,29 +195,17 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
goto error_recovery;
}
if(server)
{
FLUID_FREE(server); /* -- free server string */
}
if(device)
{
FLUID_FREE(device); /* -- free device string */
}
FLUID_FREE(server); /* -- free server string */
FLUID_FREE(device); /* -- free device string */
return (fluid_audio_driver_t *) dev;
error_recovery:
if(server)
{
FLUID_FREE(server); /* -- free server string */
}
if(device)
{
FLUID_FREE(device); /* -- free device string */
}
FLUID_FREE(server); /* -- free server string */
FLUID_FREE(device); /* -- free device string */
FLUID_FREE(left);
FLUID_FREE(right);
FLUID_FREE(buf);
delete_fluid_pulse_audio_driver((fluid_audio_driver_t *) dev);
return NULL;

251
src/drivers/fluid_sdl2.c Normal file
View File

@ -0,0 +1,251 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
* Copyright (C) 2018 Carlo Bramini
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "fluid_synth.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#if SDL2_SUPPORT
#include "SDL.h"
typedef struct
{
fluid_audio_driver_t driver;
fluid_synth_t *synth;
fluid_audio_callback_t write_ptr;
SDL_AudioDeviceID devid;
int frame_size;
} fluid_sdl2_audio_driver_t;
static void
SDLAudioCallback(void *data, void *stream, int len)
{
fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *)data;
len /= dev->frame_size;
dev->write_ptr(dev->synth, len, stream, 0, 2, stream, 1, 2);
}
void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings)
{
int n, nDevs;
fluid_settings_register_str(settings, "audio.sdl2.device", "default", 0);
fluid_settings_add_option(settings, "audio.sdl2.device", "default");
if(!SDL_WasInit(SDL_INIT_AUDIO))
{
FLUID_LOG(FLUID_ERR, "SDL2 not initialized");
return;
}
nDevs = SDL_GetNumAudioDevices(0);
for(n = 0; n < nDevs; n++)
{
const char *dev_name = SDL_GetAudioDeviceName(n, 0);
if(dev_name != NULL)
{
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name);
fluid_settings_add_option(settings, "audio.sdl2.device", dev_name);
}
}
}
/*
* new_fluid_sdl2_audio_driver
*/
fluid_audio_driver_t *
new_fluid_sdl2_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
fluid_sdl2_audio_driver_t *dev = NULL;
fluid_audio_callback_t write_ptr;
double sample_rate;
int period_size, sample_size;
SDL_AudioSpec aspec, rspec;
char *device;
const char *dev_name;
/* Retrieve the settings */
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.period-size", &period_size);
/* Lower values do not seem to give good results */
if(period_size < 1024)
{
period_size = 1024;
}
else
/* According to documentation, it MUST be a power of two */
if((period_size & (period_size - 1)) != 0)
{
FLUID_LOG(FLUID_DBG, "\"audio.period-size\" must be a power of 2");
return NULL;
}
/* Clear the format buffer */
FLUID_MEMSET(&aspec, 0, sizeof(aspec));
/* Setup mixing frequency */
aspec.freq = (int)sample_rate;
/* Check the format */
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
{
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
sample_size = sizeof(float);
write_ptr = fluid_synth_write_float;
aspec.format = AUDIO_F32SYS;
}
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
{
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
sample_size = sizeof(short);
write_ptr = fluid_synth_write_s16;
aspec.format = AUDIO_S16SYS;
}
else
{
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
return NULL;
}
/* Compile the format buffer */
aspec.channels = 2;
aspec.samples = aspec.channels * ((period_size + 7) & ~7);
aspec.callback = (SDL_AudioCallback)SDLAudioCallback;
/* Check if SDL library has been started */
if(!SDL_WasInit(SDL_INIT_AUDIO))
{
FLUID_LOG(FLUID_ERR, "SDL2 not initialized");
return NULL;
}
/* Set default device to use */
device = NULL;
dev_name = NULL;
/* get the selected device name. if none is specified, use default device. */
if(fluid_settings_dupstr(settings, "audio.sdl2.device", &device) == FLUID_OK
&& device != NULL && device[0] != '\0')
{
int n, nDevs = SDL_GetNumAudioDevices(0);
for(n = 0; n < nDevs; n++)
{
dev_name = SDL_GetAudioDeviceName(n, 0);
if(FLUID_STRCASECMP(dev_name, device) == 0)
{
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name);
break;
}
}
if(n >= nDevs)
{
FLUID_LOG(FLUID_DBG, "Audio device %s, using \"default\"", device);
dev_name = NULL;
}
}
if(device != NULL)
{
FLUID_FREE(device);
}
do
{
/* create and clear the driver data */
dev = FLUID_NEW(fluid_sdl2_audio_driver_t);
if(dev == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
break;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_sdl2_audio_driver_t));
/* set device pointer to userdata */
aspec.userdata = dev;
/* Save copy of synth */
dev->synth = synth;
/* Save copy of other variables */
dev->write_ptr = write_ptr;
dev->frame_size = sample_size * aspec.channels;
/* Open audio device */
dev->devid = SDL_OpenAudioDevice(dev_name, 0, &aspec, &rspec, 0);
if(!dev->devid)
{
FLUID_LOG(FLUID_ERR, "Failed to open audio device");
break;
}
/* Start to play */
SDL_PauseAudioDevice(dev->devid, 0);
return (fluid_audio_driver_t *) dev;
}
while(0);
delete_fluid_sdl2_audio_driver(&dev->driver);
return NULL;
}
void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *d)
{
fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *) d;
if(dev != NULL)
{
if(dev->devid)
{
/* Stop audio and close */
SDL_PauseAudioDevice(dev->devid, 1);
SDL_CloseAudioDevice(dev->devid);
}
FLUID_FREE(dev);
}
}
#endif /* SDL2_SUPPORT */

386
src/drivers/fluid_waveout.c Normal file
View File

@ -0,0 +1,386 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
* Copyright (C) 2018 Carlo Bramini
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "fluid_synth.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#if WAVEOUT_SUPPORT
#include <mmsystem.h>
#define NOBITMAP
#include <mmreg.h>
/* Number of buffers in the chain */
#define NB_SOUND_BUFFERS 4
/* Milliseconds of a single sound buffer */
#define MS_BUFFER_LENGTH 20
typedef struct
{
fluid_audio_driver_t driver;
fluid_synth_t *synth;
fluid_audio_callback_t write_ptr;
HWAVEOUT hWaveOut;
WAVEHDR waveHeader[NB_SOUND_BUFFERS];
int sample_size;
int num_frames;
HANDLE hThread;
DWORD dwThread;
int nQuit;
HANDLE hQuit;
} fluid_waveout_audio_driver_t;
/* Thread for playing sample buffers */
static DWORD WINAPI fluid_waveout_synth_thread(void *data)
{
fluid_waveout_audio_driver_t *dev;
WAVEHDR *pWave;
MSG msg;
int code;
/* Forces creation of message queue */
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
for(;;)
{
code = GetMessage(&msg, NULL, 0, 0);
if(code < 0)
{
FLUID_LOG(FLUID_ERR, "fluid_waveout_synth_thread: GetMessage() failed.");
break;
}
if(msg.message == WM_CLOSE)
{
break;
}
switch(msg.message)
{
case MM_WOM_DONE:
pWave = (WAVEHDR *)msg.lParam;
dev = (fluid_waveout_audio_driver_t *)pWave->dwUser;
if(dev->nQuit > 0)
{
/* Release the sample buffer */
waveOutUnprepareHeader((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR));
if(--dev->nQuit == 0)
{
SetEvent(dev->hQuit);
}
}
else
{
dev->write_ptr(dev->synth, dev->num_frames, pWave->lpData, 0, 2, pWave->lpData, 1, 2);
waveOutWrite((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR));
}
break;
}
}
return 0;
}
void fluid_waveout_audio_driver_settings(fluid_settings_t *settings)
{
UINT n, nDevs = waveOutGetNumDevs();
#ifdef _UNICODE
char dev_name[MAXPNAMELEN];
#endif
fluid_settings_register_str(settings, "audio.waveout.device", "default", 0);
fluid_settings_add_option(settings, "audio.waveout.device", "default");
for(n = 0; n < nDevs; n++)
{
WAVEOUTCAPS caps;
MMRESULT res;
res = waveOutGetDevCaps(n, &caps, sizeof(caps));
if(res == MMSYSERR_NOERROR)
{
#ifdef _UNICODE
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, dev_name, MAXPNAMELEN, 0, 0);
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name);
fluid_settings_add_option(settings, "audio.waveout.device", dev_name);
#else
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", caps.szPname);
fluid_settings_add_option(settings, "audio.waveout.device", caps.szPname);
#endif
}
}
}
/*
* new_fluid_waveout_audio_driver
*/
fluid_audio_driver_t *
new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
fluid_waveout_audio_driver_t *dev = NULL;
fluid_audio_callback_t write_ptr;
double sample_rate;
int periods, period_size, frequency, sample_size;
LPSTR ptrBuffer;
int lenBuffer;
int device;
int i;
WAVEFORMATEX wfx;
char dev_name[MAXPNAMELEN];
MMRESULT errCode;
/* Retrieve the settings */
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size);
/* Clear the format buffer */
ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
/* check the format */
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
{
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
sample_size = sizeof(float);
write_ptr = fluid_synth_write_float;
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
}
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
{
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
sample_size = sizeof(short);
write_ptr = fluid_synth_write_s16;
wfx.wFormatTag = WAVE_FORMAT_PCM;
}
else
{
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
return NULL;
}
/* Set frequency to integer */
frequency = (int)sample_rate;
/* Compile the format buffer */
wfx.nChannels = 2;
wfx.wBitsPerSample = sample_size * 8;
wfx.nSamplesPerSec = frequency;
wfx.nBlockAlign = sample_size * wfx.nChannels;
wfx.nAvgBytesPerSec = frequency * wfx.nBlockAlign;
/* Calculate the length of a single buffer */
lenBuffer = (MS_BUFFER_LENGTH * wfx.nAvgBytesPerSec + 999) / 1000;
/* Round to 8-bytes size */
lenBuffer = (lenBuffer + 7) & ~7;
/* create and clear the driver data */
dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(fluid_waveout_audio_driver_t) + lenBuffer * NB_SOUND_BUFFERS);
if(dev == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
/* Save copy of synth */
dev->synth = synth;
/* Save copy of other variables */
dev->write_ptr = write_ptr;
dev->sample_size = sample_size;
/* Calculate the number of frames in a block */
dev->num_frames = lenBuffer / wfx.nBlockAlign;
/* Set default device to use */
device = WAVE_MAPPER;
/* get the selected device name. if none is specified, use default device. */
if(fluid_settings_copystr(settings, "audio.waveout.device", dev_name, MAXPNAMELEN) == FLUID_OK
&& dev_name[0] != '\0')
{
UINT nDevs = waveOutGetNumDevs();
UINT n;
#ifdef _UNICODE
WCHAR lpwDevName[MAXPNAMELEN];
MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, lpwDevName, MAXPNAMELEN);
#endif
for(n = 0; n < nDevs; n++)
{
WAVEOUTCAPS caps;
MMRESULT res;
res = waveOutGetDevCaps(n, &caps, sizeof(caps));
if(res == MMSYSERR_NOERROR)
{
#ifdef _UNICODE
if(wcsicmp(lpwDevName, caps.szPname) == 0)
#else
if(FLUID_STRCASECMP(dev_name, caps.szPname) == 0)
#endif
{
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name);
device = n;
break;
}
}
}
}
do
{
dev->hQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
if(dev->hQuit == NULL)
{
FLUID_LOG(FLUID_ERR, "Failed to create quit event");
break;
}
/* Create thread which processes re-adding SYSEX buffers */
dev->hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)
fluid_waveout_synth_thread,
dev,
0,
&dev->dwThread);
if(dev->hThread == NULL)
{
FLUID_LOG(FLUID_ERR, "Failed to create waveOut thread");
break;
}
errCode = waveOutOpen(&dev->hWaveOut,
device,
&wfx,
(DWORD_PTR)dev->dwThread,
0,
CALLBACK_THREAD);
if(errCode != MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_ERR, "Failed to open waveOut device");
break;
}
/* Get pointer to sound buffer memory */
ptrBuffer = (LPSTR)(dev + 1);
/* Setup the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
{
/* Clear the sample buffer */
memset(ptrBuffer, 0, lenBuffer);
/* Clear descriptor buffer */
memset(dev->waveHeader + i, 0, sizeof(WAVEHDR));
/* Compile descriptor buffer */
dev->waveHeader[i].lpData = ptrBuffer;
dev->waveHeader[i].dwBufferLength = lenBuffer;
dev->waveHeader[i].dwUser = (DWORD_PTR)dev;
waveOutPrepareHeader(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR));
ptrBuffer += lenBuffer;
}
/* Play the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
{
waveOutWrite(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR));
}
return (fluid_audio_driver_t *) dev;
}
while(0);
delete_fluid_waveout_audio_driver(&dev->driver);
return NULL;
}
void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *d)
{
int i;
fluid_waveout_audio_driver_t *dev = (fluid_waveout_audio_driver_t *) d;
fluid_return_if_fail(dev != NULL);
/* release all the allocated resources */
if(dev->hWaveOut != NULL)
{
dev->nQuit = NB_SOUND_BUFFERS;
WaitForSingleObject(dev->hQuit, INFINITE);
waveOutClose(dev->hWaveOut);
}
if(dev->hThread != NULL)
{
PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0);
WaitForSingleObject(dev->hThread, INFINITE);
CloseHandle(dev->hThread);
}
if(dev->hQuit != NULL)
{
CloseHandle(dev->hQuit);
}
HeapFree(GetProcessHeap(), 0, dev);
}
#endif /* WAVEOUT_SUPPORT */

View File

@ -57,17 +57,93 @@ typedef struct
} fluid_winmidi_driver_t;
static char fluid_winmidi_error_buffer[256];
#define msg_type(_m) ((unsigned char)(_m & 0xf0))
#define msg_chan(_m) ((unsigned char)(_m & 0x0f))
#define msg_p1(_m) ((_m >> 8) & 0x7f)
#define msg_p2(_m) ((_m >> 16) & 0x7f)
void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
DWORD_PTR msg, DWORD_PTR extra);
static char *fluid_winmidi_input_error(MMRESULT no);
static char *
fluid_winmidi_input_error(char *strError, MMRESULT no)
{
#ifdef _UNICODE
WCHAR wStr[MAXERRORLENGTH];
midiInGetErrorText(no, wStr, MAXERRORLENGTH);
WideCharToMultiByte(CP_UTF8, 0, wStr, -1, strError, MAXERRORLENGTH, 0, 0);
#else
midiInGetErrorText(no, strError, MAXERRORLENGTH);
#endif
return strError;
}
static void CALLBACK
fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) dwInstance;
fluid_midi_event_t event;
LPMIDIHDR pMidiHdr;
unsigned char *data;
unsigned int msg_param = (unsigned int) dwParam1;
switch(wMsg)
{
case MIM_OPEN:
break;
case MIM_CLOSE:
break;
case MIM_DATA:
event.type = msg_type(msg_param);
event.channel = msg_chan(msg_param);
if(event.type != PITCH_BEND)
{
event.param1 = msg_p1(msg_param);
event.param2 = msg_p2(msg_param);
}
else /* Pitch bend is a 14 bit value */
{
event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param);
event.param2 = 0;
}
(*dev->driver.handler)(dev->driver.data, &event);
break;
case MIM_LONGDATA: /* SYSEX data */
if(dev->hThread == NULL)
{
break;
}
pMidiHdr = (LPMIDIHDR)dwParam1;
data = (unsigned char *)(pMidiHdr->lpData);
/* We only process complete SYSEX messages (discard those that are too small or too large) */
if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0
&& data[pMidiHdr->dwBytesRecorded - 1] == 0xF7)
{
fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1,
pMidiHdr->dwBytesRecorded - 2, FALSE);
(*dev->driver.handler)(dev->driver.data, &event);
}
PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, 0, dwParam1);
break;
case MIM_ERROR:
break;
case MIM_LONGERROR:
break;
case MIM_MOREDATA:
break;
}
}
void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings)
{
@ -138,7 +214,8 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
MMRESULT res;
UINT i, num, midi_num = 0;
MIDIINCAPS in_caps;
char *devname = NULL;
char strError[MAXERRORLENGTH];
char dev_name[MAXPNAMELEN];
/* not much use doing anything */
if(handler == NULL)
@ -147,6 +224,49 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
return NULL;
}
/* get the device name. if none is specified, use the default device. */
if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK)
{
FLUID_LOG(FLUID_DBG, "No MIDI in device selected, using \"default\"");
FLUID_STRCPY(dev_name, "default");
}
/* check if there any midi devices installed */
num = midiInGetNumDevs();
if(num == 0)
{
FLUID_LOG(FLUID_ERR, "no MIDI in devices found");
return NULL;
}
/* find the device */
if(FLUID_STRCASECMP("default", dev_name) != 0)
{
for(i = 0; i < num; i++)
{
res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
if(res == MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_DBG, "Testing midi device: %s\n", in_caps.szPname);
if(FLUID_STRCASECMP(dev_name, in_caps.szPname) == 0)
{
FLUID_LOG(FLUID_DBG, "Selected midi device number: %d\n", i);
midi_num = i;
break;
}
}
}
if(midi_num != i)
{
FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", dev_name);
return NULL;
}
}
dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t));
if(dev == NULL)
@ -160,54 +280,6 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
dev->driver.handler = handler;
dev->driver.data = data;
/* get the device name. if none is specified, use the default device. */
if(fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) != FLUID_OK || !devname)
{
devname = FLUID_STRDUP("default");
if(!devname)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
}
/* check if there any midi devices installed */
num = midiInGetNumDevs();
if(num == 0)
{
FLUID_LOG(FLUID_ERR, "no MIDI in devices found");
goto error_recovery;
}
/* find the device */
if(FLUID_STRCASECMP("default", devname) != 0)
{
for(i = 0; i < num; i++)
{
res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
if(res == MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_DBG, "Testing midi device: %s\n", in_caps.szPname);
if(FLUID_STRCASECMP(devname, in_caps.szPname) == 0)
{
FLUID_LOG(FLUID_DBG, "Selected midi device number: %d\n", i);
midi_num = i;
break;
}
}
}
if(midi_num != i)
{
FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname);
goto error_recovery;
}
}
/* try opening the device */
res = midiInOpen(&dev->hmidiin, midi_num,
(DWORD_PTR) fluid_winmidi_callback,
@ -216,7 +288,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
if(res != MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)",
fluid_winmidi_input_error(res), res);
fluid_winmidi_input_error(strError, res), res);
goto error_recovery;
}
@ -238,13 +310,13 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
if(res != MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
fluid_winmidi_input_error(res), res);
fluid_winmidi_input_error(strError, res), res);
midiInUnprepareHeader(dev->hmidiin, hdr, sizeof(MIDIHDR));
}
}
else
FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
fluid_winmidi_input_error(res), res);
fluid_winmidi_input_error(strError, res), res);
}
/* Create thread which processes re-adding SYSEX buffers */
@ -270,20 +342,10 @@ new_fluid_winmidi_driver(fluid_settings_t *settings,
goto error_recovery;
}
if(devname)
{
FLUID_FREE(devname); /* -- free device name */
}
return (fluid_midi_driver_t *) dev;
error_recovery:
if(devname)
{
FLUID_FREE(devname); /* -- free device name */
}
delete_fluid_winmidi_driver((fluid_midi_driver_t *) dev);
return NULL;
}
@ -294,6 +356,8 @@ error_recovery:
void
delete_fluid_winmidi_driver(fluid_midi_driver_t *p)
{
int i;
fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) p;
fluid_return_if_fail(dev != NULL);
@ -302,6 +366,7 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t *p)
PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0);
WaitForSingleObject(dev->hThread, INFINITE);
CloseHandle(dev->hThread);
dev->hThread = NULL;
}
@ -309,85 +374,21 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t *p)
{
midiInStop(dev->hmidiin);
midiInReset(dev->hmidiin);
for(i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
{
MIDIHDR *hdr = &dev->sysExHdrs[i];
if ((hdr->dwFlags & MHDR_PREPARED))
{
midiInUnprepareHeader(dev->hmidiin, hdr, sizeof(MIDIHDR));
}
}
midiInClose(dev->hmidiin);
}
FLUID_FREE(dev);
}
void CALLBACK
fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) dwInstance;
fluid_midi_event_t event;
LPMIDIHDR pMidiHdr;
unsigned char *data;
unsigned int msg_param = (unsigned int) dwParam1;
switch(wMsg)
{
case MIM_OPEN:
break;
case MIM_CLOSE:
break;
case MIM_DATA:
event.type = msg_type(msg_param);
event.channel = msg_chan(msg_param);
if(event.type != PITCH_BEND)
{
event.param1 = msg_p1(msg_param);
event.param2 = msg_p2(msg_param);
}
else /* Pitch bend is a 14 bit value */
{
event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param);
event.param2 = 0;
}
(*dev->driver.handler)(dev->driver.data, &event);
break;
case MIM_LONGDATA: /* SYSEX data */
if(dev->hThread == NULL)
{
break;
}
pMidiHdr = (LPMIDIHDR)dwParam1;
data = (unsigned char *)(pMidiHdr->lpData);
/* We only process complete SYSEX messages (discard those that are too small or too large) */
if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0
&& data[pMidiHdr->dwBytesRecorded - 1] == 0xF7)
{
fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1,
pMidiHdr->dwBytesRecorded - 2, FALSE);
(*dev->driver.handler)(dev->driver.data, &event);
}
PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, 0, dwParam1);
break;
case MIM_ERROR:
break;
case MIM_LONGERROR:
break;
case MIM_MOREDATA:
break;
}
}
static char *
fluid_winmidi_input_error(MMRESULT no)
{
midiInGetErrorText(no, fluid_winmidi_error_buffer, 256);
return fluid_winmidi_error_buffer;
}
#endif /* WINMIDI_SUPPORT */

View File

@ -1034,7 +1034,7 @@ void
print_welcome()
{
printf("FluidSynth runtime version %s\n"
"Copyright (C) 2000-2018 Peter Hanappe and others.\n"
"Copyright (C) 2000-2019 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());

View File

@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.1)
# remove $CC from the current environment and by that force cmake to look for a (working) C compiler,
# which hopefully will be the host compiler
unset(ENV{CC})
project (gentables C)
set ( CMAKE_BUILD_TYPE Debug )
# hardcode ".exe" as suffix to the binary, else in case of cross-platform cross-compiling the calling cmake will not know the suffix used here and fail to find the binary
set ( CMAKE_EXECUTABLE_SUFFIX ".exe" )
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../)
# Add the executable that generates the table
add_executable( make_tables
make_tables.c
gen_conv.c
gen_rvoice_dsp.c)
if ( WIN32 )
add_definitions ( -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS )
else ( WIN32 )
target_link_libraries (make_tables "m")
endif ()

81
src/gentables/gen_conv.c Normal file
View File

@ -0,0 +1,81 @@
#include "utils/fluid_conv_tables.h"
#include "make_tables.h"
/* conversion tables */
static double fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
static double fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
static double fluid_concave_tab[FLUID_VEL_CB_SIZE];
static double fluid_convex_tab[FLUID_VEL_CB_SIZE];
static double fluid_pan_tab[FLUID_PAN_SIZE];
/*
* void fluid_synth_init
*
* Does all the initialization for this module.
*/
static void fluid_conversion_config(void)
{
int i;
double x;
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
{
fluid_ct2hz_tab[i] = pow(2.0, (double) i / 1200.0);
}
/* centibels to amplitude conversion
* Note: SF2.01 section 8.1.3: Initial attenuation range is
* between 0 and 144 dB. Therefore a negative attenuation is
* not allowed.
*/
for(i = 0; i < FLUID_CB_AMP_SIZE; i++)
{
fluid_cb2amp_tab[i] = pow(10.0, (double) i / -200.0);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
/* concave unipolar positive transform curve */
fluid_concave_tab[0] = 0.0;
fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* convex unipolar positive transform curve */
fluid_convex_tab[0] = 0;
fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* There seems to be an error in the specs. The equations are
implemented according to the pictures on SF2.01 page 73. */
for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++)
{
x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((double)(i * i) / ((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10;
fluid_convex_tab[i] = (1.0 - x);
fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = x;
}
/* initialize the pan conversion table */
x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
for(i = 0; i < FLUID_PAN_SIZE; i++)
{
fluid_pan_tab[i] = sin(i * x);
}
}
void gen_conv_table(FILE *fp)
{
/* Calculate the values */
fluid_conversion_config();
/* fluid_ct2hz_tab */
EMIT_ARRAY(fp, fluid_ct2hz_tab);
EMIT_ARRAY(fp, fluid_cb2amp_tab);
EMIT_ARRAY(fp, fluid_concave_tab);
EMIT_ARRAY(fp, fluid_convex_tab);
EMIT_ARRAY(fp, fluid_pan_tab);
}

View File

@ -0,0 +1,81 @@
#include "rvoice/fluid_rvoice_dsp_tables.h"
#include "make_tables.h"
/* Linear interpolation table (2 coefficients centered on 1st) */
static double interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
static double interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
static double sinc_table7[FLUID_INTERP_MAX][SINC_INTERP_ORDER];
static double cb_interp_coeff_linear(int y, int x) { return interp_coeff_linear[y][x]; }
static double cb_interp_coeff (int y, int x) { return interp_coeff[y][x]; }
static double cb_sinc_table7 (int y, int x) { return sinc_table7[y][x]; }
/* Initializes interpolation tables */
void fluid_rvoice_dsp_config(void)
{
int i, i2;
double x, v;
double i_shifted;
/* Initialize the coefficients for the interpolation. The math comes
* from a mail, posted by Olli Niemitalo to the music-dsp mailing
* list (I found it in the music-dsp archives
* http://www.smartelectronix.com/musicdsp/). */
for(i = 0; i < FLUID_INTERP_MAX; i++)
{
x = (double) i / (double) FLUID_INTERP_MAX;
interp_coeff[i][0] = (x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (1.0 - x);
interp_coeff_linear[i][1] = x;
}
/* i: Offset in terms of whole samples */
for(i = 0; i < SINC_INTERP_ORDER; i++)
{
/* i2: Offset in terms of fractional samples ('subsamples') */
for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
{
/* center on middle of table */
i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ (double)i2 / (double)FLUID_INTERP_MAX;
/* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
if(fabs(i_shifted) > 0.000001)
{
double arg = M_PI * i_shifted;
v = sin(arg) / (arg);
/* Hanning window */
v *= 0.5 * (1.0 + cos(2.0 * arg / (double)SINC_INTERP_ORDER));
}
else
{
v = 1.0;
}
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
}
void gen_rvoice_table_dsp (FILE *fp)
{
/* Calculate the values */
fluid_rvoice_dsp_config();
/* Emit the matrices */
emit_matrix(fp, "interp_coeff_linear", cb_interp_coeff_linear, FLUID_INTERP_MAX, 2);
emit_matrix(fp, "interp_coeff", cb_interp_coeff, FLUID_INTERP_MAX, 4);
emit_matrix(fp, "sinc_table7", cb_sinc_table7, FLUID_INTERP_MAX, 7);
}

View File

@ -0,0 +1,84 @@
#include "make_tables.h"
static void write_value(FILE *fp, double val, int i)
{
fprintf(fp, " %.15e%c /* %d */\n",
val,
',',
i
);
}
/* Emit an array of real numbers */
void emit_array(FILE *fp, const char *tblname, const double *tbl, int size)
{
int i;
fprintf(fp, "static const fluid_real_t %s[%d] = {\n", tblname, size);
for (i = 0; i < size; i++)
{
write_value(fp, tbl[i], i);
}
fprintf(fp, "};\n\n");
}
/* Emit a matrix of real numbers */
void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel)
{
int i, j;
fprintf(fp, "static const fluid_real_t %s[%d][%d] = {\n {\n", tblname, sizeh, sizel);
for (i = 0; i < sizeh; i++)
{
for (j = 0; j < sizel; j++)
{
write_value(fp, tbl_cb(i, j), i*sizel+j);
}
if (i < (sizeh-1))
fprintf(fp, " }, {\n");
else
fprintf(fp, " }\n};\n\n");
}
}
static void open_table(FILE**fp, const char* dir, const char* file)
{
char buf[2048] = {0};
strcat(buf, dir);
strcat(buf, file);
/* open the output file */
*fp = fopen(buf, "w");
if (*fp == NULL)
{
exit(-2);
}
/* Emit warning header */
fprintf(*fp, "/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */\n\n");
}
int main (int argc, char *argv[])
{
FILE *fp;
// make sure we have enough arguments
if (argc < 2)
return -1;
open_table(&fp, argv[1], "fluid_conv_tables.c");
gen_conv_table(fp);
fclose(fp);
open_table(&fp, argv[1], "fluid_rvoice_dsp_tables.c");
gen_rvoice_table_dsp(fp);
fclose(fp);
return 0;
}

View File

@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define EMIT_ARRAY(__fp__, __arr__) emit_array(__fp__, #__arr__, __arr__, sizeof(__arr__)/sizeof(*__arr__))
/* callback for general access to matrices */
typedef double (*emit_matrix_cb)(int y, int x);
/* Generators */
void gen_rvoice_table_dsp(FILE *fp);
void gen_conv_table(FILE *fp);
/* Emit an array of real numbers */
void emit_array(FILE *fp, const char *tblname, const double *tbl, int size);
/* Emit a matrix of real numbers */
void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel);

View File

@ -36,6 +36,7 @@ static long fluid_getlength(unsigned char *s);
*/
static char *fluid_file_read_full(fluid_file fp, size_t *length);
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic);
static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size);
#define READ_FULL_INITIAL_BUFLEN 1024
static fluid_track_t *new_fluid_track(int num);
@ -81,6 +82,41 @@ static int fluid_midi_file_get_division(fluid_midi_file *midifile);
* MIDIFILE
*/
/**
* Check if a file is a MIDI file.
* @param filename Path to the file to check
* @return TRUE if it could be a MIDI file, FALSE otherwise
*
* The current implementation only checks for the "MThd" header in the file.
* It is useful only to distinguish between SoundFont and MIDI files.
*/
int fluid_is_midifile(const char *filename)
{
FILE *fp = FLUID_FOPEN(filename, "rb");
uint32_t id;
int retcode = FALSE;
do
{
if(fp == NULL)
{
return retcode;
}
if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1)
{
break;
}
retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd'));
}
while(0);
FLUID_FCLOSE(fp);
return retcode;
}
/**
* Return a new MIDI file handle for parsing an already-loaded MIDI file.
* @internal
@ -1270,8 +1306,6 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
* should be freed when the event is freed (only applies if event gets destroyed
* with delete_fluid_midi_event())
* @return Always returns #FLUID_OK
*
* @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1290,7 +1324,6 @@ fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dy
* @return Always returns #FLUID_OK
*
* @since 2.0.0
* @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1299,6 +1332,25 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
return FLUID_OK;
}
/**
* Get the text of a MIDI event structure.
* @param evt MIDI event structure
* @param data Pointer to return text data on.
* @param size Pointer to return text size on.
* @return Returns #FLUID_OK if \p data and \p size previously set by
* fluid_midi_event_set_text() have been successfully retrieved.
* Else #FLUID_FAILED is returned and \p data and \p size are not changed.
* @since 2.0.3
*/
int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size)
{
fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED);
fluid_midi_event_get_sysex_LOCAL(evt, data, size);
return FLUID_OK;
}
/**
* Assign lyric data to a MIDI event structure.
* @param evt MIDI event structure
@ -1309,7 +1361,6 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
* @return Always returns #FLUID_OK
*
* @since 2.0.0
* @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1318,6 +1369,25 @@ fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int d
return FLUID_OK;
}
/**
* Get the lyric of a MIDI event structure.
* @param evt MIDI event structure
* @param data Pointer to return lyric data on.
* @param size Pointer to return lyric size on.
* @return Returns #FLUID_OK if \p data and \p size previously set by
* fluid_midi_event_set_lyrics() have been successfully retrieved.
* Else #FLUID_FAILED is returned and \p data and \p size are not changed.
* @since 2.0.3
*/
int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size)
{
fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED);
fluid_midi_event_get_sysex_LOCAL(evt, data, size);
return FLUID_OK;
}
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic)
{
evt->type = type;
@ -1326,6 +1396,19 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type,
evt->param2 = dynamic;
}
static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size)
{
if(data)
{
*data = evt->paramptr;
}
if(size)
{
*size = evt->param1;
}
}
/******************************************************
*
* fluid_track_t
@ -1597,7 +1680,7 @@ new_fluid_player(fluid_synth_t *synth)
fluid_settings_getint(synth->settings, "player.reset-synth", &i);
fluid_player_handle_reset_synth(player, NULL, i);
fluid_settings_callback_int(synth->settings, "player.reset-synth",
fluid_player_handle_reset_synth, player);
@ -2152,7 +2235,7 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
*/
int fluid_player_set_bpm(fluid_player_t *player, int bpm)
{
return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm));
return fluid_player_set_midi_tempo(player, 60000000L / bpm);
}
/**
@ -2225,7 +2308,7 @@ int fluid_player_get_total_ticks(fluid_player_t *player)
*/
int fluid_player_get_bpm(fluid_player_t *player)
{
return (int)(60e6 / player->miditempo);
return 60000000L / player->miditempo;
}
/**

View File

@ -678,18 +678,18 @@ fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event)
* Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all
* input channels is mapped to the same synth channel.
*/
chan = (int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul
+ (fluid_real_t)rule->chan_add + 0.5);
chan = rule->chan_add + (int)((fluid_real_t)event->channel * rule->chan_mul
+ (fluid_real_t)0.5);
/* Par 1 scaling / offset */
par1 = (int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul
+ (fluid_real_t)rule->par1_add + 0.5);
par1 = rule->par1_add + (int)((fluid_real_t)event_par1 * rule->par1_mul
+ (fluid_real_t)0.5);
/* Par 2 scaling / offset, if applicable */
if(event_has_par2)
{
par2 = (int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul
+ (fluid_real_t)rule->par2_add + 0.5);
par2 = rule->par2_add + (int)((fluid_real_t)event_par2 * rule->par2_mul
+ (fluid_real_t)0.5);
}
else
{

View File

@ -117,7 +117,7 @@ new_fluid_sequencer2(int use_system_timer)
if(seq == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return NULL;
}
@ -132,7 +132,7 @@ new_fluid_sequencer2(int use_system_timer)
if(-1 == _fluid_seq_queue_init(seq, FLUID_SEQUENCER_EVENTS_MAX))
{
FLUID_FREE(seq);
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return NULL;
}
@ -144,7 +144,7 @@ new_fluid_sequencer2(int use_system_timer)
{
_fluid_seq_queue_end(seq);
FLUID_FREE(seq);
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return NULL;
}
@ -297,7 +297,7 @@ fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name,
if(client == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return FLUID_FAILED;
}
@ -305,7 +305,7 @@ fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name,
if(nameCopy == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_FREE(client);
return FLUID_FAILED;
}
@ -575,7 +575,7 @@ fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale)
{
if(scale <= 0)
{
fluid_log(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale);
FLUID_LOG(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale);
return;
}
@ -722,7 +722,7 @@ _fluid_seq_queue_init(fluid_sequencer_t *seq, int maxEvents)
if(seq->heap == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return -1;
}
@ -801,7 +801,7 @@ _fluid_seq_queue_pre_insert(fluid_sequencer_t *seq, fluid_event_t *evt)
if(evtentry == NULL)
{
/* should not happen */
fluid_log(FLUID_PANIC, "sequencer: no more free events\n");
FLUID_LOG(FLUID_PANIC, "sequencer: no more free events\n");
return -1;
}
@ -839,7 +839,7 @@ _fluid_seq_queue_pre_remove(fluid_sequencer_t *seq, fluid_seq_id_t src, fluid_se
if(evtentry == NULL)
{
/* should not happen */
fluid_log(FLUID_PANIC, "sequencer: no more free events\n");
FLUID_LOG(FLUID_PANIC, "sequencer: no more free events\n");
return;
}

View File

@ -108,7 +108,7 @@ fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth
if(seqbind == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return FLUID_FAILED;
}
@ -125,7 +125,7 @@ fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth
if(seqbind->sample_timer == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
delete_fluid_seqbind(seqbind);
return FLUID_FAILED;
}
@ -359,5 +359,3 @@ fluid_sequencer_add_midi_event_to_buffer(void *data, fluid_midi_event_t *event)
/* Schedule for sending at next call to fluid_sequencer_process */
return fluid_sequencer_send_at(seq, &evt, 0, 0);
}

View File

@ -149,7 +149,7 @@ new_fluid_chorus(fluid_real_t sample_rate)
if(chorus == NULL)
{
fluid_log(FLUID_PANIC, "chorus: Out of memory");
FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
return NULL;
}
@ -191,7 +191,7 @@ new_fluid_chorus(fluid_real_t sample_rate)
if(chorus->lookup_tab == NULL)
{
fluid_log(FLUID_PANIC, "chorus: Out of memory");
FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
@ -201,7 +201,7 @@ new_fluid_chorus(fluid_real_t sample_rate)
if(chorus->chorusbuf == NULL)
{
fluid_log(FLUID_PANIC, "chorus: Out of memory");
FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
@ -293,32 +293,32 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
if(chorus->number_blocks < 0)
{
fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
chorus->number_blocks = 0;
}
else if(chorus->number_blocks > MAX_CHORUS)
{
fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
FLUID_LOG(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
MAX_CHORUS);
chorus->number_blocks = MAX_CHORUS;
}
if(chorus->speed_Hz < MIN_SPEED_HZ)
{
fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.",
FLUID_LOG(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.",
(double) MIN_SPEED_HZ);
chorus->speed_Hz = MIN_SPEED_HZ;
}
else if(chorus->speed_Hz > MAX_SPEED_HZ)
{
fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.",
FLUID_LOG(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.",
(double) MAX_SPEED_HZ);
chorus->speed_Hz = MAX_SPEED_HZ;
}
if(chorus->depth_ms < 0.0)
{
fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
FLUID_LOG(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
chorus->depth_ms = 0.0;
}
@ -326,12 +326,12 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
if(chorus->level < 0.0)
{
fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
chorus->level = 0.0;
}
else if(chorus->level > 10)
{
fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
"Setting it to 0.1.");
chorus->level = 0.1;
}
@ -346,7 +346,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
if(modulation_depth_samples > MAX_SAMPLES)
{
fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
modulation_depth_samples = MAX_SAMPLES;
// set depth to maximum to avoid spamming console with above warning
chorus->depth_ms = (modulation_depth_samples * 1000) / chorus->sample_rate;
@ -356,7 +356,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
switch(chorus->type)
{
default:
fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
chorus->type = FLUID_CHORUS_MOD_SINE;
/* fall-through */
@ -383,7 +383,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
}
void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in,
void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;
@ -456,7 +456,7 @@ void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in,
}
/* Duplication of code ... (replaces sample data instead of mixing) */
void fluid_chorus_processreplace(fluid_chorus_t *chorus, fluid_real_t *in,
void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;

View File

@ -55,9 +55,9 @@ void fluid_chorus_reset(fluid_chorus_t *chorus);
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
fluid_real_t speed, fluid_real_t depth_ms, int type);
void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in,
void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_chorus_processreplace(fluid_chorus_t *chorus, fluid_real_t *in,
void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);

View File

@ -275,8 +275,8 @@ fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter,
* into account for both significant frequency relocation and for
* bandwidth readjustment'. */
fluid_real_t omega = (fluid_real_t)(2.0 * M_PI *
(iir_filter->last_fres / ((float) output_rate)));
fluid_real_t omega = (fluid_real_t)(2.0 * M_PI) *
(iir_filter->last_fres / output_rate);
fluid_real_t sin_coeff = (fluid_real_t) sin(omega);
fluid_real_t cos_coeff = (fluid_real_t) cos(omega);
fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin);

View File

@ -22,8 +22,6 @@
#ifndef _FLUID_PHASE_H
#define _FLUID_PHASE_H
#include "config.h"
/*
* phase
*/
@ -31,7 +29,7 @@
#define FLUID_INTERP_BITS 8
#define FLUID_INTERP_BITS_MASK 0xff000000
#define FLUID_INTERP_BITS_SHIFT 24
#define FLUID_INTERP_MAX 256
#define FLUID_FRACT_MAX ((double)4294967296.0)

File diff suppressed because it is too large Load Diff

View File

@ -61,10 +61,10 @@ typedef struct _fluid_revmodel_presets_t
fluid_revmodel_t *new_fluid_revmodel(fluid_real_t sample_rate);
void delete_fluid_revmodel(fluid_revmodel_t *rev);
void fluid_revmodel_processmix(fluid_revmodel_t *rev, fluid_real_t *in,
void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in,
void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_revmodel_reset(fluid_revmodel_t *rev);

View File

@ -305,6 +305,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
{
int ticks = voice->envlfo.ticks;
int count, is_looping;
fluid_real_t modenv_val;
/******************* sample sanity check **********/
@ -361,6 +362,12 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
/******************* phase **********************/
/* SF2.04 section 8.1.2 #26:
* attack of modEnv is convex ?!?
*/
modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv))
: fluid_adsr_env_get_val(&voice->envlfo.modenv);
/* Calculate the number of samples, that the DSP loop advances
* through the original waveform with each step in the output
* buffer. It is the ratio between the frequencies of original
@ -369,7 +376,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
voice->dsp.pitchoffset +
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
+ fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
+ fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch)
+ modenv_val * voice->envlfo.modenv_to_pitch)
/ voice->dsp.root_pitch_hz;
/******************* portamento ****************/
@ -455,7 +462,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
modenv_val * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
@ -585,19 +592,37 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
/* A voice is turned off during the attack section of the volume
* envelope. The attack section ramps up linearly with
* amplitude. The other sections use logarithmic scaling. Calculate new
* volenv_val to achieve equievalent amplitude during the release phase
* volenv_val to achieve equivalent amplitude during the release phase
* for seamless volume transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)
{
fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;
fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo);
fluid_real_t env_value = - ((-200 * log(amp) / log(10.0) - lfo) / FLUID_PEAK_ATTENUATION - 1);
fluid_real_t env_value = - (((-200 / M_LN10) * log(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1);
fluid_clip(env_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);
}
}
if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
{
/* A voice is turned off during the attack section of the modulation
* envelope. The attack section use convex scaling with pitch and filter
* frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)
* The other sections use linear scaling: modenv_val = modenv.val
*
* Calculate new modenv.val to achieve equivalent modenv_val during the release phase
* for seamless pitch and filter frequency cutoff transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)
{
fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));
fluid_clip(env_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);
}
}
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
}
@ -650,11 +675,12 @@ static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voi
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
{
fluid_rvoice_t *voice = obj;
int section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
int section; /* volume or modulation section */
/*-------------------------------------------------------------------------
Section skip for volume envelope
--------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new
@ -672,16 +698,30 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
/* skips to Attack section from any section */
/* Update vol and attack data */
fluid_rvoice_local_retrigger_attack(voice);
/*-------------------------------------------------------------------------
Section skip for modulation envelope
--------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.modenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use linear scaling.
Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section
uses convex shape (see fluid_rvoice_write() - fluid_convex()).
Calculate new modenv value (new_value) for seamless attack transition.
Here we need the inverse of fluid_convex() function defined as:
new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)
For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with
val = (1 current_val) . FLUID_PEAK_ATTENUATION / 2.0
*/
fluid_real_t new_value; /* new modenv value */
new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))
* FLUID_PEAK_ATTENUATION / 2.0);
fluid_clip(new_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);
}
/* Skips from any section to ATTACK section */
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);
/* Actually (v 1.1.6) all sections are linear, so there is no need to
correct val value. However soundfont 2.01/2.4 spec. says that Attack should
be convex (see issue #153 from Christian Collins). In the case Attack
section would be changed to a non linear shape it will be necessary to do
a correction for seamless val transition. Here is the place to do this */
}
/**

View File

@ -39,7 +39,7 @@ typedef struct _fluid_rvoice_t fluid_rvoice_t;
* 24 bits => 144-4 = 140 dB dynamic range => 1.e-7
* 1.e-7 * 2 == 2.e-7 :)
*/
#define FLUID_NOISE_FLOOR 2.e-7
#define FLUID_NOISE_FLOOR ((fluid_real_t)2.e-7)
enum fluid_loop
{

View File

@ -22,6 +22,7 @@
#include "fluid_phase.h"
#include "fluid_rvoice.h"
#include "fluid_sys.h"
#include "fluid_rvoice_dsp_tables.c"
/* Purpose:
*
@ -47,85 +48,6 @@
/* Interpolation (find a value between two samples of the original waveform) */
/* Linear interpolation table (2 coefficients centered on 1st) */
static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
#define SINC_INTERP_ORDER 7 /* 7th order constant */
/* Initializes interpolation tables */
void fluid_rvoice_dsp_config(void)
{
int i, i2;
double x, v;
double i_shifted;
/* Initialize the coefficients for the interpolation. The math comes
* from a mail, posted by Olli Niemitalo to the music-dsp mailing
* list (I found it in the music-dsp archives
* http://www.smartelectronix.com/musicdsp/). */
for(i = 0; i < FLUID_INTERP_MAX; i++)
{
x = (double) i / (double) FLUID_INTERP_MAX;
interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x);
interp_coeff_linear[i][1] = (fluid_real_t)x;
}
/* i: Offset in terms of whole samples */
for(i = 0; i < SINC_INTERP_ORDER; i++)
{
/* i2: Offset in terms of fractional samples ('subsamples') */
for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
{
/* center on middle of table */
i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ (double)i2 / (double)FLUID_INTERP_MAX;
/* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
if(fabs(i_shifted) > 0.000001)
{
double arg = M_PI * i_shifted;
v = (fluid_real_t)sin(arg) / (arg);
/* Hanning window */
v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * arg / (fluid_real_t)SINC_INTERP_ORDER));
}
else
{
v = 1.0;
}
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
#if 0
for(i = 0; i < FLUID_INTERP_MAX; i++)
{
printf("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n",
i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i],
sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]);
}
#endif
fluid_check_fpe("interpolation table calculation");
}
static FLUID_INLINE fluid_real_t
fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx)
{

View File

@ -0,0 +1,8 @@
#ifndef _FLUID_RVOICE_DSP_TABLES_H
#define _FLUID_RVOICE_DSP_TABLES_H
#define FLUID_INTERP_MAX 256
#define SINC_INTERP_ORDER 7 /* 7th order constant */
#endif

View File

@ -28,8 +28,6 @@
#include "fluid_synth.h"
#define ENABLE_MIXER_THREADS 1
// If less than x voices, the thread overhead is larger than the gain,
// so don't activate the thread(s).
#define VOICES_PER_THREAD 8
@ -41,13 +39,12 @@ struct _fluid_mixer_buffers_t
fluid_rvoice_mixer_t *mixer; /**< Owner of object */
#if ENABLE_MIXER_THREADS
fluid_thread_t *thread; /**< Thread object */
fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */
#endif
fluid_rvoice_t **finished_voices; /* List of voices who have finished */
int finished_voice_count;
fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */
fluid_real_t *local_buf;
int buf_count;
@ -130,8 +127,8 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun
const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units;
int i, f;
void (*reverb_process_func)(fluid_revmodel_t *rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
void (*chorus_process_func)(fluid_chorus_t *chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r;
@ -375,7 +372,7 @@ get_dest_buf(fluid_rvoice_buffers_t *buffers, int index,
*/
static void
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
fluid_real_t *FLUID_RESTRICT dsp_buf,
const fluid_real_t *FLUID_RESTRICT dsp_buf,
int start_block, int sample_count,
fluid_real_t **dest_bufs, int dest_bufcount)
{

File diff suppressed because it is too large Load Diff

View File

@ -196,7 +196,7 @@ struct _fluid_inst_t
};
fluid_inst_t *new_fluid_inst(void);
fluid_inst_t *fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont);
fluid_inst_t *fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont);
void delete_fluid_inst(fluid_inst_t *inst);
int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone);
int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone);

View File

@ -60,8 +60,10 @@ struct _fluid_samplecache_entry_t
static fluid_list_t *samplecache_list = NULL;
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start,
unsigned int sample_end, int sample_type, time_t mtime);
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start,
unsigned int sample_end, int sample_type, time_t mtime);
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
@ -75,14 +77,20 @@ int fluid_samplecache_load(SFData *sf,
{
fluid_samplecache_entry_t *entry;
int ret;
time_t mtime;
fluid_mutex_lock(samplecache_mutex);
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type);
if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
{
mtime = 0;
}
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL)
{
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL)
{
@ -180,7 +188,8 @@ unlock_exit:
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_end,
int sample_type)
int sample_type,
time_t mtime)
{
fluid_samplecache_entry_t *entry;
@ -202,12 +211,6 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
goto error_exit;
}
if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
entry->modification_time = 0;
}
entry->sf_samplepos = sf->samplepos;
entry->sf_samplesize = sf->samplesize;
entry->sf_sample24pos = sf->sample24pos;
@ -215,6 +218,7 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
entry->sample_start = sample_start;
entry->sample_end = sample_end;
entry->sample_type = sample_type;
entry->modification_time = mtime;
entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
&entry->sample_data, &entry->sample_data24);
@ -244,18 +248,12 @@ static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_end,
int sample_type)
int sample_type,
time_t mtime)
{
time_t mtime;
fluid_list_t *entry_list;
fluid_samplecache_entry_t *entry;
if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
mtime = 0;
}
entry_list = samplecache_list;
while(entry_list)

View File

@ -34,54 +34,80 @@
Borrowed from Smurf SoundFont Editor by Josh Green
=================================================================*/
/* FOURCC definitions */
#define RIFF_FCC FLUID_FOURCC('R','I','F','F')
#define LIST_FCC FLUID_FOURCC('L','I','S','T')
#define SFBK_FCC FLUID_FOURCC('s','f','b','k')
#define INFO_FCC FLUID_FOURCC('I','N','F','O')
#define SDTA_FCC FLUID_FOURCC('s','d','t','a')
#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */
#define IFIL_FCC FLUID_FOURCC('i','f','i','l')
#define ISNG_FCC FLUID_FOURCC('i','s','n','g')
#define INAM_FCC FLUID_FOURCC('I','N','A','M')
#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */
#define IVER_FCC FLUID_FOURCC('i','v','e','r')
#define ICRD_FCC FLUID_FOURCC('I','C','R','D')
#define IENG_FCC FLUID_FOURCC('I','E','N','G')
#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */
#define ICOP_FCC FLUID_FOURCC('I','C','O','P')
#define ICMT_FCC FLUID_FOURCC('I','C','M','T')
#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */
#define SNAM_FCC FLUID_FOURCC('s','n','a','m')
#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */
#define PHDR_FCC FLUID_FOURCC('p','h','d','r')
#define PBAG_FCC FLUID_FOURCC('p','b','a','g')
#define PMOD_FCC FLUID_FOURCC('p','m','o','d')
#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */
#define IHDR_FCC FLUID_FOURCC('i','n','s','t')
#define IBAG_FCC FLUID_FOURCC('i','b','a','g')
#define IMOD_FCC FLUID_FOURCC('i','m','o','d')
#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */
#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */
#define SM24_FCC FLUID_FOURCC('s','m','2','4')
/* Set when the FCC code is unknown */
#define UNKN_ID FLUID_N_ELEMENTS(idlist)
/*
functions for loading data from sfont files, with appropriate byte swapping
on big endian machines. Sfont IDs are not swapped because the ID read is
equivalent to the matching ID list in memory regardless of LE/BE machine
*/
/* sf file chunk IDs */
enum
* This declares a uint32_t array containing the SF2 chunk identifiers.
*/
static const uint32_t idlist[] =
{
UNKN_ID,
RIFF_ID,
LIST_ID,
SFBK_ID,
INFO_ID,
SDTA_ID,
PDTA_ID, /* info/sample/preset */
RIFF_FCC,
LIST_FCC,
SFBK_FCC,
INFO_FCC,
SDTA_FCC,
PDTA_FCC,
IFIL_ID,
ISNG_ID,
INAM_ID,
IROM_ID, /* info ids (1st byte of info strings) */
IVER_ID,
ICRD_ID,
IENG_ID,
IPRD_ID, /* more info ids */
ICOP_ID,
ICMT_ID,
ISFT_ID, /* and yet more info ids */
IFIL_FCC,
ISNG_FCC,
INAM_FCC,
IROM_FCC,
IVER_FCC,
ICRD_FCC,
IENG_FCC,
IPRD_FCC,
ICOP_FCC,
ICMT_FCC,
ISFT_FCC,
SNAM_ID,
SMPL_ID, /* sample ids */
PHDR_ID,
PBAG_ID,
PMOD_ID,
PGEN_ID, /* preset ids */
IHDR_ID,
IBAG_ID,
IMOD_ID,
IGEN_ID, /* instrument ids */
SHDR_ID, /* sample info */
SM24_ID
SNAM_FCC,
SMPL_FCC,
PHDR_FCC,
PBAG_FCC,
PMOD_FCC,
PGEN_FCC,
IHDR_FCC,
IBAG_FCC,
IMOD_FCC,
IGEN_FCC,
SHDR_FCC,
SM24_FCC
};
static const char idlist[] = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
"ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24"
};
/* generator types */
typedef enum
{
@ -183,8 +209,6 @@ static const unsigned short invalid_preset_gen[] =
};
#define CHNKIDSTR(id) &idlist[(id - 1) * 4]
/* sfont file chunk sizes */
#define SF_PHDR_SIZE (38)
#define SF_BAG_SIZE (4)
@ -284,7 +308,7 @@ static int load_shdr(SFData *sf, unsigned int size);
static int fixup_pgen(SFData *sf);
static int fixup_igen(SFData *sf);
static int chunkid(unsigned int id);
static int chunkid(uint32_t id);
static int read_listchunk(SFData *sf, SFChunk *chunk);
static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size);
static int preset_compare_func(void *a, void *b);
@ -300,6 +324,56 @@ 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);
/**
* Check if a file is a SoundFont file.
* @param filename Path to the file to check
* @return TRUE if it could be a SoundFont, FALSE otherwise
*
* @note The current implementation only checks for the "RIFF" and "sfbk" headers in
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
*/
int fluid_is_soundfont(const char *filename)
{
FILE *fp = FLUID_FOPEN(filename, "rb");
uint32_t fcc;
int retcode = FALSE;
do
{
if(fp == NULL)
{
return retcode;
}
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{
break;
}
if(fcc != RIFF_FCC)
{
break;
}
if(FLUID_FSEEK(fp, 4, SEEK_CUR))
{
break;
}
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{
break;
}
retcode = (fcc == SFBK_FCC);
}
while(0);
FLUID_FCLOSE(fp);
return retcode;
}
/*
* Open a SoundFont file and parse it's contents into a SFData structure.
*
@ -486,22 +560,20 @@ void fluid_sffile_close(SFData *sf)
*/
/* sound font file load functions */
static int chunkid(unsigned int id)
static int chunkid(uint32_t id)
{
unsigned int i;
const unsigned int *p;
p = (const unsigned int *)&idlist;
for(i = 0; i < sizeof(idlist) / sizeof(int); i++, p += 1)
for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++)
{
if(*p == id)
if(idlist[i] == id)
{
return (i + 1);
break;
}
}
return UNKN_ID;
/* Return chunk id or UNKN_ID if not found */
return i;
}
static int load_header(SFData *sf)
@ -510,7 +582,7 @@ static int load_header(SFData *sf)
READCHUNK(sf, &chunk); /* load RIFF chunk */
if(chunkid(chunk.id) != RIFF_ID)
if(chunk.id != RIFF_FCC)
{
/* error if not RIFF */
FLUID_LOG(FLUID_ERR, "Not a RIFF file");
@ -519,7 +591,7 @@ static int load_header(SFData *sf)
READID(sf, &chunk.id); /* load file ID */
if(chunkid(chunk.id) != SFBK_ID)
if(chunk.id != SFBK_FCC)
{
/* error if not SFBK_ID */
FLUID_LOG(FLUID_ERR, "Not a SoundFont file");
@ -538,7 +610,7 @@ static int load_header(SFData *sf)
return FALSE;
}
if(chunkid(chunk.id) != INFO_ID)
if(chunk.id != INFO_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk");
return FALSE;
@ -555,7 +627,7 @@ static int load_header(SFData *sf)
return FALSE;
}
if(chunkid(chunk.id) != SDTA_ID)
if(chunk.id != SDTA_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk");
return FALSE;
@ -572,7 +644,7 @@ static int load_header(SFData *sf)
return FALSE;
}
if(chunkid(chunk.id) != PDTA_ID)
if(chunk.id != PDTA_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
return FALSE;
@ -617,7 +689,7 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
{
READCHUNK(sf, chunk); /* read list chunk */
if(chunkid(chunk->id) != LIST_ID) /* error if ! list chunk */
if(chunk->id != LIST_FCC) /* error if ! list chunk */
{
FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse");
return FALSE;
@ -631,8 +703,11 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
static int process_info(SFData *sf, int size)
{
SFChunk chunk;
unsigned char id;
char *item;
union
{
char *chr;
uint32_t *fcc;
} item;
unsigned short ver;
while(size > 0)
@ -640,9 +715,7 @@ static int process_info(SFData *sf, int size)
READCHUNK(sf, &chunk);
size -= 8;
id = chunkid(chunk.id);
if(id == IFIL_ID)
if(chunk.id == IFIL_FCC)
{
/* sound font version chunk? */
if(chunk.size != 4)
@ -683,7 +756,7 @@ static int process_info(SFData *sf, int size)
return FALSE;
}
}
else if(id == IVER_ID)
else if(chunk.id == IVER_FCC)
{
/* ROM version chunk? */
if(chunk.size != 4)
@ -697,34 +770,35 @@ static int process_info(SFData *sf, int size)
READW(sf, ver);
sf->romver.minor = ver;
}
else if(id != UNKN_ID)
else if(chunkid(chunk.id) != UNKN_ID)
{
if((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
{
FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes",
&chunk.id, chunk.size);
return FALSE;
}
/* alloc for chunk id and da chunk */
if(!(item = FLUID_MALLOC(chunk.size + 1)))
/* alloc for chunk fcc and da chunk */
if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1)))
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
/* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */
sf->info = fluid_list_append(sf->info, item);
sf->info = fluid_list_append(sf->info, item.fcc);
*(unsigned char *)item = id;
/* save chunk fcc and update pointer to data value */
*item.fcc++ = chunk.id;
if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED)
if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED)
{
return FALSE;
}
/* force terminate info item (don't forget uint8 info ID) */
*(item + chunk.size) = '\0';
/* force terminate info item */
item.chr[chunk.size] = '\0';
}
else
{
@ -757,7 +831,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk);
size -= 8;
if(chunkid(chunk.id) != SMPL_ID)
if(chunk.id != SMPL_FCC)
{
FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead");
return FALSE;
@ -790,7 +864,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk);
size -= 8;
if(chunkid(chunk.id) == SM24_ID)
if(chunk.id == SM24_FCC)
{
int sm24size, sdtahalfsize;
@ -830,29 +904,24 @@ ret:
static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size)
{
unsigned int id;
const char *expstr;
expstr = CHNKIDSTR(expid); /* in case we need it */
READCHUNK(sf, chunk);
*size -= 8;
if((id = chunkid(chunk->id)) != expid)
if(chunk->id != expid)
{
FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr);
FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", &expid);
return FALSE;
}
if(chunk->size % reclen) /* valid chunk size? */
{
FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", expstr, reclen);
FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", &expid, reclen);
return FALSE;
}
if((*size -= chunk->size) < 0)
{
FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", expstr);
FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", &expid);
return FALSE;
}
@ -863,7 +932,7 @@ static int process_pdta(SFData *sf, int size)
{
SFChunk chunk;
if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size))
if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size))
{
return FALSE;
}
@ -873,7 +942,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size))
if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{
return FALSE;
}
@ -883,7 +952,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size))
if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{
return FALSE;
}
@ -893,7 +962,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size))
if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{
return FALSE;
}
@ -903,7 +972,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size))
if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size))
{
return FALSE;
}
@ -913,7 +982,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size))
if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{
return FALSE;
}
@ -923,7 +992,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size))
if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{
return FALSE;
}
@ -933,7 +1002,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size))
if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{
return FALSE;
}
@ -943,7 +1012,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size))
if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size))
{
return FALSE;
}
@ -987,6 +1056,7 @@ static int load_phdr(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
sf->preset = fluid_list_append(sf->preset, preset);
preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */
READSTR(sf, &preset->name); /* possible read failure ^ */
@ -1078,6 +1148,7 @@ static int load_pbag(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p2->data = z;
z->gen = NULL; /* Init gen and mod before possible failure, */
z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */
@ -1211,6 +1282,7 @@ static int load_pmod(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p3->data = m;
READW(sf, m->src);
READW(sf, m->dest);
@ -1367,6 +1439,7 @@ static int load_pgen(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p3->data = g;
g->id = genid;
}
@ -1508,6 +1581,7 @@ static int load_ihdr(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
sf->inst = fluid_list_append(sf->inst, p);
p->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */
p->idx = i;
@ -1593,6 +1667,7 @@ static int load_ibag(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p2->data = z;
z->gen = NULL; /* In case of failure, */
z->mod = NULL; /* fluid_sffile_close can clean up */
@ -1727,6 +1802,7 @@ static int load_imod(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p3->data = m;
READW(sf, m->src);
READW(sf, m->dest);
@ -1872,6 +1948,7 @@ static int load_igen(SFData *sf, int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
p3->data = g;
g->id = genid;
}
@ -2011,6 +2088,7 @@ static int load_shdr(SFData *sf, unsigned int size)
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
sf->sample = fluid_list_append(sf->sample, p);
READSTR(sf, &p->name);
READD(sf, p->start);
@ -2138,7 +2216,7 @@ static void delete_preset(SFPreset *preset)
}
delete_fluid_list(preset->zone);
FLUID_FREE(preset);
}
@ -2162,7 +2240,7 @@ static void delete_inst(SFInst *inst)
}
delete_fluid_list(inst->zone);
FLUID_FREE(inst);
}

View File

@ -512,9 +512,13 @@ delete_fluid_sample(fluid_sample_t *sample)
/**
* Returns the size of the fluid_sample_t structure.
*
* Useful in low latency scenarios e.g. to allocate a sample on the stack.
* Useful in low latency scenarios e.g. to allocate a pool of samples.
*
* @return Size of fluid_sample_t in bytes
*
* @note It is recommend to zero initialize the memory before using the object.
*
* @warning Do NOT allocate samples on the stack and assign them to a voice!
*/
size_t fluid_sample_sizeof()
{
@ -563,16 +567,17 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
fluid_return_val_if_fail(data != NULL, FLUID_FAILED);
fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED);
fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED);
/* in case we already have some data */
if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)
{
FLUID_FREE(sample->data);
FLUID_FREE(sample->data24);
sample->data = NULL;
sample->data24 = NULL;
}
sample->data = NULL;
sample->data24 = NULL;
if(copy_data)
{
@ -635,6 +640,8 @@ error_rec:
FLUID_LOG(FLUID_ERR, "Out of memory");
FLUID_FREE(sample->data);
FLUID_FREE(sample->data24);
sample->data = NULL;
sample->data24 = NULL;
return FLUID_FAILED;
#undef SAMPLE_LOOP_MARGIN

View File

@ -183,8 +183,6 @@ struct _fluid_sample_t
* @return Should return #FLUID_OK
*/
int (*notify)(fluid_sample_t *sample, int reason);
void *userdata; /**< User defined data */
};

View File

@ -62,7 +62,7 @@ new_fluid_event()
if(evt == NULL)
{
fluid_log(FLUID_PANIC, "event: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "event: Out of memory\n");
return NULL;
}
@ -751,7 +751,7 @@ _fluid_evt_heap_init(int nbEvents)
if(heap == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return NULL;
}
@ -782,7 +782,7 @@ _fluid_evt_heap_init(int nbEvents)
if(heap == NULL)
{
fluid_log(FLUID_PANIC, "sequencer: Out of memory\n");
FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n");
return NULL;
}

View File

@ -126,7 +126,7 @@ fluid_real_t fluid_gen_scale(int gen, float value)
fluid_real_t fluid_gen_scale_nrpn(int gen, int data)
{
fluid_real_t value = (float) data - 8192.0f;
fluid_clip(value, -8192, 8192);
return value * (float) fluid_gen_info[gen].nrpn_scale;
data = data - 8192;
fluid_clip(data, -8192, 8192);
return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale);
}

View File

@ -245,10 +245,7 @@ fluid_mod_get_source_value(const unsigned char mod_src,
static fluid_real_t
fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range)
{
/* normalized value, i.e. usually in the range [0;1]
*
* if val was retrieved from pitch_bend then [-0.5;0.5]
*/
/* normalized value, i.e. usually in the range [0;1] */
const fluid_real_t val_norm = val / range;
/* we could also only switch case the lower nibble of mod_flags, however
@ -364,7 +361,22 @@ fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, cons
}
/*
* fluid_mod_get_value
* fluid_mod_get_value.
* Computes and return modulator output following SF2.01
* (See SoundFont Modulator Controller Model Chapter 9.5).
*
* Output = Transform(Amount * Map(primary source input) * Map(secondary source input))
*
* Notes:
* 1)fluid_mod_get_value, ignores the Transform operator. The result is:
*
* Output = Amount * Map(primary source input) * Map(secondary source input)
*
* 2)When primary source input (src1) is set to General Controller 'No Controller',
* output is forced to 0.
*
* 3)When secondary source input (src2) is set to General Controller 'No Controller',
* output is forced to +1.0
*/
fluid_real_t
fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
@ -418,6 +430,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the input value */
v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1);
}
/* When primary source input (src1) is set to General Controller 'No Controller',
output is forced to 0.0
*/
else
{
return 0.0;
@ -437,6 +452,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the second input value */
v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2);
}
/* When secondary source input (src2) is set to General Controller 'No Controller',
output is forced to +1.0
*/
else
{
v2 = 1.0f;
@ -486,6 +504,177 @@ size_t fluid_mod_sizeof()
return sizeof(fluid_mod_t);
}
/**
* Checks if modulator with source 1 other than CC is FLUID_MOD_NONE.
*
* @param mod, modulator.
* @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise.
*/
static int
fluid_mod_is_src1_none(const fluid_mod_t *mod)
{
return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE));
}
/**
* Checks if modulators source other than CC source is invalid.
* (specs SF 2.01 7.4, 7.8, 8.2.1)
*
* @param mod, modulator.
* @param src1_select, source input selection to check.
* 1 to check src1 source.
* 0 to check src2 source.
* @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */
/* SF2.01 section 8.2.1: Constant value */
|| ((src == FLUID_MOD_NONE)
|| (src == FLUID_MOD_VELOCITY) /* Note-on velocity */
|| (src == FLUID_MOD_KEY) /* Note-on key number */
|| (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */
|| (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
|| (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
|| (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */
));
}
/**
* Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1).
* @param mod, modulator.
* @src1_select, source input selection:
* 1 to check src1 source or
* 0 to check src2 source.
* @return FALSE if selected modulator's source CC is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */
|| ((src != BANK_SELECT_MSB)
&& (src != BANK_SELECT_LSB)
&& (src != DATA_ENTRY_MSB)
&& (src != DATA_ENTRY_LSB)
/* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */
&& ((src < NRPN_LSB) || (RPN_MSB < src))
/* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */
/* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */
&& (src < ALL_SOUND_OFF)
/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1)
However, as long fluidsynth will use only CC 7 bits resolution,
it is safe to ignore these SF recommendations on CC receive.
See explanations in fluid_synth_cc_LOCAL() */
/* uncomment next line to forbid CC lsb */
/* && ((src < 32) || (63 < src)) */
));
}
/**
* Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1)
* @param mod, modulator.
* @param name,if not NULL, pointer on a string displayed as a warning.
* @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.
*/
int fluid_mod_check_sources(const fluid_mod_t *mod, char *name)
{
static const char *invalid_non_cc_src =
"Invalid modulator, using non-CC source %s.src%d=%d";
static const char *invalid_cc_src =
"Invalid modulator, using CC source %s.src%d=%d";
static const char *src1_is_none =
"Modulator with source 1 none %s.src1=%d";
/* checks valid non cc sources */
if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1);
}
return FALSE;
}
/*
When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but
the output of this modulator will be forced to 0 at synthesis time.
Also this modulator cannot be used to overwrite a default modulator (as
there is no default modulator with src1 source equal to FLUID_MOD_NONE).
Consequently it is useful to return FALSE to indicate this modulator
being useless. It will be removed later with others invalid modulators.
*/
if(fluid_mod_is_src1_none(mod))
{
if(name)
{
FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2);
}
return FALSE;
}
/* checks valid cc sources */
if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2);
}
return FALSE;
}
return TRUE;
}
/**
* Checks if two modulators are identical in sources, flags and destination.
* @param mod1 First modulator

View File

@ -44,6 +44,7 @@ struct _fluid_mod_t
};
fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
int fluid_mod_check_sources(const fluid_mod_t *mod, char *name);
#ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod);

View File

@ -88,15 +88,13 @@ static void fluid_synth_update_presets(fluid_synth_t *synth);
static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth);
static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony);
static void init_dither(void);
static FLUID_INLINE int roundi(float x);
static FLUID_INLINE int16_t round_clip_to_i16(float x);
static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount);
static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth);
static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth,
fluid_voice_t *new_voice);
static int fluid_synth_sfunload_callback(void *data, unsigned int msec);
void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth,
int chan, int key);
static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth,
int bank, int prog);
static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth,
@ -147,8 +145,6 @@ static int fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int
/* has the synth module been initialized? */
/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */
static fluid_atomic_int_t fluid_synth_initialized = {0};
static void fluid_synth_init(void);
static void init_dither(void);
/* default modulators
* SF2.01 page 52 ff:
@ -226,7 +222,11 @@ void fluid_synth_settings(fluid_settings_t *settings)
fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0);
fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0);
fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0);
#ifdef ENABLE_MIXER_THREADS
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0);
#else
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0);
#endif
fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
@ -286,10 +286,6 @@ fluid_synth_init(void)
feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID);
#endif
fluid_conversion_config();
fluid_rvoice_dsp_config();
init_dither();
/* custom_breath2att_mod is not a default modulator specified in SF2.01.
@ -1108,6 +1104,8 @@ delete_fluid_synth(fluid_synth_t *synth)
* @return Pointer to string of last error message. Valid until the same
* calling thread calls another FluidSynth function which fails. String is
* internal and should not be modified or freed.
* @deprecated This function is not thread-safe and does not work with multiple synths.
* It has been deprecated. It may return "" in a future release and will eventually be removed.
*/
const char *
fluid_synth_error(fluid_synth_t *synth)
@ -1318,7 +1316,7 @@ fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan)
* @param mod Modulator info (values copied, passed in object can be freed immediately afterwards)
* @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod)
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
*
*
* @note Not realtime safe (due to internal memory allocation) and therefore should not be called
* from synthesis context at the risk of stalling audio output.
*/
@ -1331,6 +1329,13 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
/* Checks if modulators sources are valid */
if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod"))
{
return FLUID_FAILED;
}
fluid_synth_api_enter(synth);
default_mod = synth->default_mod;
@ -1388,7 +1393,7 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo
* @param synth synth instance
* @param mod The modulator to remove
* @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise
*
*
* @note Not realtime safe (due to internal memory allocation) and therefore should not be called
* from synthesis context at the risk of stalling audio output.
*/
@ -1513,7 +1518,37 @@ fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val)
FLUID_API_RETURN(result);
}
/* Local synthesis thread variant of MIDI CC set function. */
/* Local synthesis thread variant of MIDI CC set function.
Most of CC are allowed to modulate but not all. A comment describes if CC num
isn't allowed to modulate.
Following explanations should help to understand both MIDI specifications and
Soundfont specifications in regard to MIDI specs.
MIDI specs:
CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31).
It's up to the synthesizer to decide to take LSB values into account or not.
Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param()
(once fluid_voice_modulate() has been triggered). This is because actually
fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs.
So fluidsynth is using only 7 bit MSB (except for portamento time).
In regard to MIDI specs Fluidsynth behaves correctly.
Soundfont specs 2.01 - 8.2.1:
To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit)
or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of
CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators
with CC LSB connected to sources inputs should be ignored).
These specifics are particularly suited for synths that use 14 bits CCs. In this case,
the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives
both CC LSB and CC MSB but only CC MSB will trigger the modulation.
This will produce correct synthesis parameters update from a correct 14 bits CC.
If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and
CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters
update followed by correct synthesis parameters update.
However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore
these SF recommendations on CC receive.
*/
static int
fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
{
@ -1525,8 +1560,11 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
switch(num)
{
case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
break;
/* CC omnioff, omnion, mono, poly */
/* not allowed to modulate (spec SF 2.01 - 8.2.1) */
case POLY_OFF:
case POLY_ON:
case OMNI_OFF:
@ -1581,18 +1619,18 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
return FLUID_FAILED;
case LEGATO_SWITCH:
case LEGATO_SWITCH: /* not allowed to modulate */
/* handles Poly/mono commutation on Legato pedal On/Off.*/
fluid_channel_cc_legato(chan, value);
break;
case PORTAMENTO_SWITCH:
case PORTAMENTO_SWITCH: /* not allowed to modulate */
/* Special handling of the monophonic list */
/* Invalids the most recent note played in a staccato manner */
fluid_channel_invalid_prev_note_staccato(chan);
break;
case SUSTAIN_SWITCH:
case SUSTAIN_SWITCH: /* not allowed to modulate */
/* Release voices if Sustain switch is released */
if(value < 64) /* Sustain is released */
@ -1602,7 +1640,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
break;
case SOSTENUTO_SWITCH:
case SOSTENUTO_SWITCH: /* not allowed to modulate */
/* Release voices if Sostetuno switch is released */
if(value < 64) /* Sostenuto is released */
@ -1617,28 +1655,31 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
break;
case BANK_SELECT_MSB:
case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_channel_set_bank_msb(chan, value & 0x7F);
break;
case BANK_SELECT_LSB:
case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_channel_set_bank_lsb(chan, value & 0x7F);
break;
case ALL_NOTES_OFF:
case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_synth_all_notes_off_LOCAL(synth, channum);
break;
case ALL_SOUND_OFF:
case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_synth_all_sounds_off_LOCAL(synth, channum);
break;
case ALL_CTRL_OFF:
case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_channel_init_ctrl(chan, 1);
fluid_synth_modulate_voices_all_LOCAL(synth, channum);
break;
case DATA_ENTRY_MSB:
case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
break;
case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
{
int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB);
@ -1698,13 +1739,13 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
break;
}
case NRPN_MSB:
case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
fluid_channel_set_cc(chan, NRPN_LSB, 0);
chan->nrpn_select = 0;
chan->nrpn_active = 1;
break;
case NRPN_LSB:
case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if(fluid_channel_get_cc(chan, NRPN_MSB) == 120)
@ -1730,8 +1771,8 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
chan->nrpn_active = 1;
break;
case RPN_MSB:
case RPN_LSB:
case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */
chan->nrpn_active = 0;
break;
@ -1741,7 +1782,14 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num)
/* fall-through */
default:
return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num);
/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */
/* However, as long fluidsynth will use only CC 7 bits resolution, it
is safe to ignore these SF recommendations on CC receive. See
explanations above */
/* if (! (32 <= num && num <= 63)) */
{
return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num);
}
}
return FLUID_OK;
@ -1859,7 +1907,7 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
int bank = 0, prog, channels;
double tunedata[128];
int keys[128];
char name[17];
char name[17]={0};
int note, frac, frac2;
uint8_t chksum;
int i, count, index;
@ -1930,7 +1978,8 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
}
*resptr++ = prog;
FLUID_STRNCPY(resptr, name, 16);
/* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */
FLUID_MEMCPY(resptr, name, 16);
resptr += 16;
for(i = 0; i < 128; i++)
@ -3325,7 +3374,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(left != NULL, FLUID_FAILED);
fluid_return_val_if_fail(right != NULL, FLUID_FAILED);
/* First, take what's still available in the buffer */
count = 0;
num = synth->cur;
@ -3478,7 +3527,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
/**
* mixes the samples of \p in to \p out
*
*
* @param out the output sample buffer to mix to
* @param ooff sample offset in \p out
* @param in the rvoice_mixer input sample buffer to mix from
@ -3496,6 +3545,7 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out
if(out != NULL)
{
int j;
for(j = 0; j < num; j++)
{
out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];
@ -3644,7 +3694,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);
@ -3687,10 +3737,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);
out_buf = fx[(buf_idx * 2 + 1) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);
}
@ -3741,7 +3791,7 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
float cpu_load;
fluid_profile_ref_var(prof_ref);
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
@ -3806,17 +3856,31 @@ init_dither(void)
}
/* A portable replacement for roundf(), seems it may actually be faster too! */
static FLUID_INLINE int
roundi(float x)
static FLUID_INLINE int16_t
round_clip_to_i16(float x)
{
long i;
if(x >= 0.0f)
{
return (int)(x + 0.5f);
i = (long)(x + 0.5f);
if(FLUID_UNLIKELY(i > 32767))
{
i = 32767;
}
}
else
{
return (int)(x - 0.5f);
i = (long)(x - 0.5f);
if(FLUID_UNLIKELY(i < -32768))
{
i = -32768;
}
}
return (int16_t)i;
}
/**
@ -3845,12 +3909,10 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
void *rout, int roff, int rincr)
{
int i, j, k, cur;
signed short *left_out = (signed short *) lout;
signed short *right_out = (signed short *) rout;
int16_t *left_out = lout;
int16_t *right_out = rout;
fluid_real_t *left_in;
fluid_real_t *right_in;
fluid_real_t left_sample;
fluid_real_t right_sample;
double time = fluid_utime();
int di;
float cpu_load;
@ -3875,39 +3937,13 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
cur = 0;
}
left_sample = roundi(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]);
right_sample = roundi(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]);
left_out[j] = round_clip_to_i16(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]);
right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]);
di++;
if(di >= DITHER_SIZE)
if(++di >= DITHER_SIZE)
{
di = 0;
}
/* digital clipping */
if(left_sample > 32767.0f)
{
left_sample = 32767.0f;
}
if(left_sample < -32768.0f)
{
left_sample = -32768.0f;
}
if(right_sample > 32767.0f)
{
right_sample = 32767.0f;
}
if(right_sample < -32768.0f)
{
right_sample = -32768.0f;
}
left_out[j] = (signed short) left_sample;
right_out[k] = (signed short) right_sample;
}
synth->cur = cur;
@ -3941,54 +3977,25 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
* @note Currently private to libfluidsynth.
*/
void
fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin,
fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin,
void *lout, int loff, int lincr,
void *rout, int roff, int rincr)
{
int i, j, k;
signed short *left_out = (signed short *) lout;
signed short *right_out = (signed short *) rout;
fluid_real_t left_sample;
fluid_real_t right_sample;
int16_t *left_out = lout;
int16_t *right_out = rout;
int di = *dither_index;
fluid_profile_ref_var(prof_ref);
for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr)
{
left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]);
right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]);
left_sample = roundi(lin[i] * 32766.0f + rand_table[0][di]);
right_sample = roundi(rin[i] * 32766.0f + rand_table[1][di]);
di++;
if(di >= DITHER_SIZE)
if(++di >= DITHER_SIZE)
{
di = 0;
}
/* digital clipping */
if(left_sample > 32767.0f)
{
left_sample = 32767.0f;
}
if(left_sample < -32768.0f)
{
left_sample = -32768.0f;
}
if(right_sample > 32767.0f)
{
right_sample = 32767.0f;
}
if(right_sample < -32768.0f)
{
right_sample = -32768.0f;
}
left_out[j] = (signed short) left_sample;
right_out[k] = (signed short) right_sample;
}
*dither_index = di; /* keep dither buffer continous */
@ -4350,11 +4357,11 @@ fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int
)
{
// Replacement of default_vel2att modulator by custom_breath2att_modulator
fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT);
fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);
}
else
{
fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT);
fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);
}
// Next default modulator to add to the voice

View File

@ -190,7 +190,7 @@ fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth,
int prognum);
void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont);
void fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin,
void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin,
void *lout, int loff, int lincr,
void *rout, int roff, int rincr);

View File

@ -290,11 +290,6 @@ static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan,
* Sust.on/off >------------------------->|_______________|
* Sost.on/off
------------------------------------------------------------------------------*/
int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key,
char Mono);
int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan,
int fromkey, int tokey, int vel);
/**
* Plays a noteon event for a Synth instance in "monophonic playing" state.

View File

@ -516,7 +516,6 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t *voice)
voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice));
}
/*
* fluid_voice_calculate_runtime_synthesis_parameters
*
@ -685,7 +684,7 @@ calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base,
* will cause (60-72)*100=-1200 timecents of time variation.
* The time is cut in half.
*/
timecents = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (60.0 - fluid_voice_get_actual_key(voice)));
timecents = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (fluid_real_t)(60 - fluid_voice_get_actual_key(voice)));
/* Range checking */
if(is_decay)
@ -1153,7 +1152,9 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* Recalculate voice parameters for a given control.
* @param voice the synthesis voice
* @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...)
* @param ctrl the control number
* @param ctrl the control number:
* when >=0, only modulators's destination having ctrl as source are updated.
* when -1, all modulators's destination are updated (regardless of ctrl).
*
* In this implementation, I want to make sure that all controllers
* are event based: the parameter values of the DSP algorithm should
@ -1161,57 +1162,83 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* iteration of the audio cycle (which would probably be feasible if
* the synth was made in silicon).
*
* The update is done in three steps:
* The update is done in two steps:
*
* - first, we look for all the modulators that have the changed
* controller as a source. This will yield a list of generators that
* will be changed because of the controller event.
* - step 1: first, we look for all the modulators that have the changed
* controller as a source. This will yield a generator that will be changed
* because of the controller event.
*
* - For every changed generator, calculate its new value. This is the
* sum of its original value plus the values of al the attached
* modulators.
*
* - For every changed generator, convert its value to the correct
* unit of the corresponding DSP parameter
* - step 2: For this generator, calculate its new value. This is the
* sum of its original value plus the values of all the attached modulators.
* The generator flag is set to indicate the parameters must be updated.
* This avoid the risk to call 'fluid_voice_update_param' several
* times for the same generator if several modulators have that generator as
* destination. So every changed generators are updated only once.
*/
/* bit table for each generator being updated. The bits are packed in variables
Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2.
The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT.
Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu)
but this could be set to 6 for 64 bits cpu.
*/
#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */
#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2)
#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1)
#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR)
#define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
#define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
{
int i, k;
fluid_mod_t *mod;
int gen;
uint32_t gen;
fluid_real_t modval;
/* Clears registered bits table of updated generators */
uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0};
/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */
for(i = 0; i < voice->mod_count; i++)
{
mod = &voice->mod[i];
/* step 1: find all the modulators that have the changed controller
* as input source. */
if(fluid_mod_has_source(mod, cc, ctrl))
as input source. When ctrl is -1 all modulators destination
are updated */
if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl))
{
gen = fluid_mod_get_dest(mod);
modval = 0.0;
/* step 2: for every changed modulator, calculate the modulation
* value of its associated generator */
for(k = 0; k < voice->mod_count; k++)
/* Skip if this generator has already been updated */
if(!is_gen_updated(updated_gen_bit, gen))
{
if(fluid_mod_has_dest(&voice->mod[k], gen))
modval = 0.0;
/* step 2: for every attached modulator, calculate the modulation
* value for the generator gen */
for(k = 0; k < voice->mod_count; k++)
{
modval += fluid_mod_get_value(&voice->mod[k], voice);
if(fluid_mod_has_dest(&voice->mod[k], gen))
{
modval += fluid_mod_get_value(&voice->mod[k], voice);
}
}
fluid_gen_set_mod(&voice->gen[gen], modval);
/* now recalculate the parameter values that are derived from the
generator */
fluid_voice_update_param(voice, gen);
/* set the bit that indicates this generator is updated */
set_gen_updated(updated_gen_bit, gen);
}
fluid_gen_set_mod(&voice->gen[gen], modval);
/* step 3: now that we have the new value of the generator,
* recalculate the parameter values that are derived from the
* generator */
fluid_voice_update_param(voice, gen);
}
}
@ -1222,47 +1249,11 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
* Update all the modulators. This function is called after a
* ALL_CTRL_OFF MIDI message has been received (CC 121).
*
* All destination of all modulators must be updated.
*/
int fluid_voice_modulate_all(fluid_voice_t *voice)
{
fluid_mod_t *mod;
int i, k, gen;
fluid_real_t modval;
/* Loop through all the modulators.
FIXME: we should loop through the set of generators instead of
the set of modulators. We risk to call 'fluid_voice_update_param'
several times for the same generator if several modulators have
that generator as destination. It's not an error, just a wast of
energy (think polution, global warming, unhappy musicians,
...) */
for(i = 0; i < voice->mod_count; i++)
{
mod = &voice->mod[i];
gen = fluid_mod_get_dest(mod);
modval = 0.0;
/* Accumulate the modulation values of all the modulators with
* destination generator 'gen' */
for(k = 0; k < voice->mod_count; k++)
{
if(fluid_mod_has_dest(&voice->mod[k], gen))
{
modval += fluid_mod_get_value(&voice->mod[k], voice);
}
}
fluid_gen_set_mod(&voice->gen[gen], modval);
/* Update the parameter values that are depend on the generator
* 'gen' */
fluid_voice_update_param(voice, gen);
}
return FLUID_OK;
return fluid_voice_modulate(voice, 0, -1);
}
/** legato update functions --------------------------------------------------*/
@ -1474,10 +1465,10 @@ fluid_voice_stop(fluid_voice_t *voice)
}
/**
* Adds a modulator to the voice.
* @param voice Voice instance
* @param mod Modulator info (copied)
* @param mode Determines how to handle an existing identical modulator
* Adds a modulator to the voice if the modulator has valid sources.
* @param voice Voice instance.
* @param mod Modulator info (copied).
* @param mode Determines how to handle an existing identical modulator.
* #FLUID_VOICE_ADD to add (offset) the modulator amounts,
* #FLUID_VOICE_OVERWRITE to replace the modulator,
* #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should
@ -1485,33 +1476,43 @@ fluid_voice_stop(fluid_voice_t *voice)
*/
void
fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* Ignore the modulator if its sources inputs are invalid */
if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod"))
{
fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD);
}
}
/**
* Adds a modulator to the voice.
* local version of fluid_voice_add_mod function. Called at noteon time.
* @param voice, mod, mode, same as for fluid_voice_add_mod() (see above).
* @param check_limit_count is the modulator number limit to handle with existing
* identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD).
* - When FLUID_NUM_MOD, all the voices modulators (since the previous call)
* are checked for identity.
* - When check_count_limit is below the actual number of voices modulators
* (voice->mod_count), this will restrict identity check to this number,
* This is usefull when we know by advance that there is no duplicate with
* modulators at index above this limit. This avoid wasting cpu cycles at noteon.
*/
void
fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count)
{
int i;
/*
* Some soundfonts come with a huge number of non-standard
* controllers, because they have been designed for one particular
* sound card. Discard them, maybe print a warning.
*/
if(((mod->flags1 & FLUID_MOD_CC) == 0)
&& ((mod->src1 != FLUID_MOD_NONE) /* SF2.01 section 8.2.1: Constant value */
&& (mod->src1 != FLUID_MOD_VELOCITY) /* Note-on velocity */
&& (mod->src1 != FLUID_MOD_KEY) /* Note-on key number */
&& (mod->src1 != FLUID_MOD_KEYPRESSURE) /* Poly pressure */
&& (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
&& (mod->src1 != FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
&& (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
/* check_limit_count cannot be above voice->mod_count */
if(check_limit_count > voice->mod_count)
{
FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
return;
check_limit_count = voice->mod_count;
}
if(mode == FLUID_VOICE_ADD)
{
/* if identical modulator exists, add them */
for(i = 0; i < voice->mod_count; i++)
for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{
@ -1526,7 +1527,7 @@ fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* if identical modulator exists, replace it (only the amount has to be changed) */
for(i = 0; i < voice->mod_count; i++)
for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{
@ -1704,9 +1705,10 @@ int fluid_voice_get_velocity(const fluid_voice_t *voice)
* A lower boundary for the attenuation (as in 'the minimum
* attenuation of this voice, with volume pedals, modulators
* etc. resulting in minimum attenuation, cannot fall below x cB) is
* calculated. This has to be called during fluid_voice_init, after
* calculated. This has to be called during fluid_voice_start, after
* all modulators have been run on the voice once. Also,
* voice->attenuation has to be initialized.
* (see fluid_voice_calculate_runtime_synthesis_parameters())
*/
static fluid_real_t
fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
@ -1733,30 +1735,42 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
{
fluid_real_t current_val = fluid_mod_get_value(mod, voice);
fluid_real_t v = fabs(mod->amount);
/* min_val is the possible minimum value for this modulator.
it depends of 3 things :
1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar
or 0 if mapping is unipolar).
2)the sign of amount.
3)absolute value of amount.
if((mod->src1 == FLUID_MOD_PITCHWHEEL)
|| (mod->flags1 & FLUID_MOD_BIPOLAR)
When at least one source mapping is bipolar:
min_val is -|amount| regardless the sign of amount.
When both sources mapping are unipolar:
min_val is -|amount|, if amount is negative.
min_val is 0, if amount is positive
*/
fluid_real_t min_val = fabs(mod->amount);
/* Can this modulator produce a negative contribution? */
if((mod->flags1 & FLUID_MOD_BIPOLAR)
|| (mod->flags2 & FLUID_MOD_BIPOLAR)
|| (mod->amount < 0))
{
/* Can this modulator produce a negative contribution? */
v *= -1.0;
min_val *= -1.0; /* min_val = - |amount|*/
}
else
{
/* No negative value possible. But still, the minimum contribution is 0. */
v = 0;
min_val = 0;
}
/* For example:
* - current_val=100
* - min_val=-4000
* - possible_att_reduction_cB += 4100
* - possible reduction contribution of this modulator = current_val - min_val = 4100
*/
if(current_val > v)
if(current_val > min_val)
{
possible_att_reduction_cB += (current_val - v);
possible_att_reduction_cB += (current_val - min_val);
}
}
}

View File

@ -157,6 +157,7 @@ void fluid_voice_release(fluid_voice_t *voice);
void fluid_voice_noteoff(fluid_voice_t *voice);
void fluid_voice_off(fluid_voice_t *voice);
void fluid_voice_stop(fluid_voice_t *voice);
void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice);
int fluid_voice_kill_excl(fluid_voice_t *voice);

View File

@ -19,74 +19,7 @@
*/
#include "fluid_conv.h"
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
/* conversion tables */
static fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
static fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
static fluid_real_t fluid_concave_tab[FLUID_VEL_CB_SIZE];
static fluid_real_t fluid_convex_tab[FLUID_VEL_CB_SIZE];
static fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
/*
* void fluid_synth_init
*
* Does all the initialization for this module.
*/
void
fluid_conversion_config(void)
{
int i;
double x;
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
{
fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
}
/* centibels to amplitude conversion
* Note: SF2.01 section 8.1.3: Initial attenuation range is
* between 0 and 144 dB. Therefore a negative attenuation is
* not allowed.
*/
for(i = 0; i < FLUID_CB_AMP_SIZE; i++)
{
fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
/* concave unipolar positive transform curve */
fluid_concave_tab[0] = 0.0;
fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* convex unipolar positive transform curve */
fluid_convex_tab[0] = 0;
fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* There seems to be an error in the specs. The equations are
implemented according to the pictures on SF2.01 page 73. */
for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++)
{
x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((i * i) / (fluid_real_t)((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10;
fluid_convex_tab[i] = (fluid_real_t)(1.0 - x);
fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = (fluid_real_t) x;
}
/* initialize the pan conversion table */
x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
for(i = 0; i < FLUID_PAN_SIZE; i++)
{
fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
}
}
#include "fluid_conv_tables.c"
/*
* fluid_ct2hz
@ -299,6 +232,13 @@ fluid_tc2sec_release(fluid_real_t tc)
* fluid_act2hz
*
* Convert from absolute cents to Hertz
*
* The inverse operation, converting from Hertz to cents, was unused and implemented as
*
fluid_hz2ct(fluid_real_t f)
{
return (fluid_real_t)(6900 + (1200 / M_LN2) * log(f / 440.0));
}
*/
fluid_real_t
fluid_act2hz(fluid_real_t c)
@ -306,17 +246,6 @@ fluid_act2hz(fluid_real_t c)
return (fluid_real_t)(8.176 * pow(2.0, (double) c / 1200.0));
}
/*
* fluid_hz2ct
*
* Convert from Hertz to cents
*/
fluid_real_t
fluid_hz2ct(fluid_real_t f)
{
return (fluid_real_t)(6900 + 1200 * log(f / 440.0) / M_LN2);
}
/*
* fluid_pan
*/
@ -407,3 +336,4 @@ fluid_convex(fluid_real_t val)
return fluid_convex_tab[(int) val];
}

View File

@ -22,39 +22,7 @@
#define _FLUID_CONV_H
#include "fluidsynth_priv.h"
/*
Attenuation range in centibels.
Attenuation range is the dynamic range of the volume envelope generator
from 0 to the end of attack segment.
fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
However the spec makes no distinction between 16 or 24 bit synths, so use
96 dB here.
Note about usefulness of 24 bits:
1)Even fluidsynth is a 24 bit synth, this format is only relevant if
the sample format coming from the soundfont is 24 bits and the audio sample format
choosen by the application (audio.sample.format) is not 16 bits.
2)When the sample soundfont is 16 bits, the internal 24 bits number have
16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
even if this sample is produced by the audio driver using an audio sample format
compatible for a 24 bit DAC.
3)When the audio sample format settings is 16 bits (audio.sample.format), the
audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
even if the initial sample comes from a 24 bits soundfont.
In both cases (2) or (3), the real dynamic range is only 96 dB.
Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
- for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
*/
#define FLUID_PEAK_ATTENUATION 960.0f
void fluid_conversion_config(void);
#include "utils/fluid_conv_tables.h"
fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
fluid_real_t fluid_ct2hz(fluid_real_t cents);
@ -64,7 +32,6 @@ fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);
fluid_real_t fluid_tc2sec_release(fluid_real_t tc);
fluid_real_t fluid_act2hz(fluid_real_t c);
fluid_real_t fluid_hz2ct(fluid_real_t c);
fluid_real_t fluid_pan(fluid_real_t c, int left);
fluid_real_t fluid_balance(fluid_real_t balance, int left);
fluid_real_t fluid_concave(fluid_real_t val);

View File

@ -0,0 +1,41 @@
#ifndef _FLUID_CONV_TABLES_H
#define _FLUID_CONV_TABLES_H
/*
Attenuation range in centibels.
Attenuation range is the dynamic range of the volume envelope generator
from 0 to the end of attack segment.
fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
However the spec makes no distinction between 16 or 24 bit synths, so use
96 dB here.
Note about usefulness of 24 bits:
1)Even fluidsynth is a 24 bit synth, this format is only relevant if
the sample format coming from the soundfont is 24 bits and the audio sample format
choosen by the application (audio.sample.format) is not 16 bits.
2)When the sample soundfont is 16 bits, the internal 24 bits number have
16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
even if this sample is produced by the audio driver using an audio sample format
compatible for a 24 bit DAC.
3)When the audio sample format settings is 16 bits (audio.sample.format), the
audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
even if the initial sample comes from a 24 bits soundfont.
In both cases (2) or (3), the real dynamic range is only 96 dB.
Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
- for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
*/
#define FLUID_PEAK_ATTENUATION 960.0f
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
#endif

View File

@ -457,7 +457,7 @@ fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_n
else
{
/* path ends prematurely */
FLUID_LOG(FLUID_WARN, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name);
FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name);
return FLUID_FAILED;
}
@ -550,7 +550,7 @@ fluid_settings_register_str(fluid_settings_t *settings, const char *name, const
}
else
{
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name);
}
}
@ -612,7 +612,7 @@ fluid_settings_register_num(fluid_settings_t *settings, const char *name, double
else
{
/* type mismatch */
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name);
}
}
@ -674,7 +674,7 @@ fluid_settings_register_int(fluid_settings_t *settings, const char *name, int de
else
{
/* type mismatch */
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name);
}
}
@ -936,6 +936,7 @@ fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *
if((fluid_settings_get(settings, name, &node) != FLUID_OK)
|| (node->type != FLUID_STR_TYPE))
{
FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name);
goto error_recovery;
}
@ -1313,6 +1314,7 @@ fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val)
if((fluid_settings_get(settings, name, &node) != FLUID_OK)
|| (node->type != FLUID_NUM_TYPE))
{
FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name);
goto error_recovery;
}
@ -1320,7 +1322,7 @@ fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val)
if(val < setting->min || val > setting->max)
{
FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name);
goto error_recovery;
}
@ -1497,6 +1499,7 @@ fluid_settings_setint(fluid_settings_t *settings, const char *name, int val)
if((fluid_settings_get(settings, name, &node) != FLUID_OK)
|| (node->type != FLUID_INT_TYPE))
{
FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name);
goto error_recovery;
}
@ -1504,7 +1507,7 @@ fluid_settings_setint(fluid_settings_t *settings, const char *name, int val)
if(val < setting->min || val > setting->max)
{
FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name);
goto error_recovery;
}
@ -1733,7 +1736,6 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name,
const char *separator)
{
fluid_setting_node_t *node;
fluid_str_setting_t *setting;
fluid_list_t *p, *newlist = NULL;
size_t count, len;
char *str, *option;
@ -1756,10 +1758,8 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name,
return (NULL);
}
setting = &node->str;
/* Duplicate option list, count options and get total string length */
for(p = setting->options, count = 0, len = 0; p; p = p->next, count++)
for(p = node->str.options, count = 0, len = 0; p; p = p->next)
{
option = fluid_list_get(p);
@ -1767,6 +1767,7 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name,
{
newlist = fluid_list_append(newlist, option);
len += FLUID_STRLEN(option);
count++;
}
}

View File

@ -262,71 +262,6 @@ char *fluid_strtok(char **str, char *delim)
return token;
}
/**
* Check if a file is a MIDI file.
* @param filename Path to the file to check
* @return TRUE if it could be a MIDI file, FALSE otherwise
*
* The current implementation only checks for the "MThd" header in the file.
* It is useful only to distinguish between SoundFont and MIDI files.
*/
int
fluid_is_midifile(const char *filename)
{
FILE *fp = fopen(filename, "rb");
char id[4];
if(fp == NULL)
{
return 0;
}
if(fread((void *) id, 1, 4, fp) != 4)
{
fclose(fp);
return 0;
}
fclose(fp);
return FLUID_STRNCMP(id, "MThd", 4) == 0;
}
/**
* Check if a file is a SoundFont file.
* @param filename Path to the file to check
* @return TRUE if it could be a SoundFont, FALSE otherwise
*
* @note The current implementation only checks for the "RIFF" and "sfbk" headers in
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
*/
int
fluid_is_soundfont(const char *filename)
{
FILE *fp = fopen(filename, "rb");
char riff_id[4], sfbk_id[4];
if(fp == NULL)
{
return 0;
}
if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) ||
(fseek(fp, 4, SEEK_CUR) != 0) ||
(fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id)))
{
goto error_rec;
}
fclose(fp);
return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) &&
(FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0);
error_rec:
fclose(fp);
return 0;
}
/**
* Suspend the execution of the current thread for the specified amount of time.
* @param milliseconds to wait.
@ -1268,6 +1203,11 @@ fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt,
FLUID_SNPRINTF(buf, len, "%s", line);
buf[len - 1] = 0;
if(buf[0] != '\0')
{
add_history(buf);
}
free(line);
return 1;
}
@ -1318,9 +1258,10 @@ fluid_istream_gets(fluid_istream_t in, char *buf, int len)
}
else
{
#ifdef NETWORK_SUPPORT
n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0);
if(n == SOCKET_ERROR)
#endif
{
return -1;
}
@ -1393,10 +1334,13 @@ fluid_ostream_printf(fluid_ostream_t out, const char *format, ...)
return write(out, buf, FLUID_STRLEN(buf));
}
#ifdef NETWORK_SUPPORT
/* Socket */
retval = send(out & ~FLUID_SOCKET_FLAG, buf, FLUID_STRLEN(buf), 0);
return retval != SOCKET_ERROR ? retval : -1;
#else
return -1;
#endif
}
#endif
}

View File

@ -77,6 +77,14 @@
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
#define FLUID_LE16TOH(x) GINT16_FROM_LE(x)
#if FLUID_IS_BIG_ENDIAN
#define FLUID_FOURCC(_a, _b, _c, _d) \
(uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d))
#else
#define FLUID_FOURCC(_a, _b, _c, _d) \
(uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a))
#endif
#define fluid_return_if_fail(cond) \
if(cond) \

View File

@ -107,7 +107,7 @@
#endif
#if HAVE_IO_H
#include <io.h>
#include <io.h> // _open(), _close(), read(), write() on windows
#endif
#if HAVE_SIGNAL_H
@ -137,8 +137,6 @@ typedef guint64 uint64_t;
#include <ws2tcpip.h> /* Provides also socklen_t */
/* WIN32 special defines */
#define DSOUND_SUPPORT 1
#define WINMIDI_SUPPORT 1
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
@ -267,7 +265,7 @@ typedef FILE *fluid_file;
#define FLUID_STRNCPY(_dst,_src,_n) \
do { strncpy(_dst,_src,_n); \
(_dst)[(_n)-1]=0; \
(_dst)[(_n)-1]='\0'; \
}while(0)
#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
@ -322,7 +320,16 @@ do { strncpy(_dst,_src,_n); \
#define FLUID_FLUSH() fflush(stdout)
#endif
/* People who want to reduce the size of the may do this by entirely
* removing the logging system. This will cause all log messages to
* be discarded at compile time, allowing to save about 80 KiB for
* the compiled binary.
*/
#if 0
#define FLUID_LOG (void)sizeof
#else
#define FLUID_LOG fluid_log
#endif
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795

View File

@ -21,6 +21,8 @@ int main(void)
// no sfont loaded
TEST_ASSERT(fluid_synth_sfcount(synth) == 0);
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