pax_global_header00006660000000000000000000000064132227207600014513gustar00rootroot0000000000000052 comment=fe37923393d6a76f42ed044aa56dadc66ec909ce fluidsynth-1.1.9/000077500000000000000000000000001322272076000137145ustar00rootroot00000000000000fluidsynth-1.1.9/.appveyor-vcpkg.yml000066400000000000000000000013751322272076000175000ustar00rootroot00000000000000os: - Visual Studio 2015 build: verbosity: detailed configuration: - Release environment: # update the vcpkg cache even if build fails APPVEYOR_SAVE_CACHE_ON_ERROR: true matrix: - platform: x86 generator: Visual Studio 14 2015 - platform: x64 generator: Visual Studio 14 2015 Win64 cache: - c:\Tools\vcpkg\installed install: - vcpkg install glib:%platform%-windows build_script: - mkdir build - cd build - cmake -G "%generator%" -Denable-pkgconfig=0 -DCMAKE_TOOLCHAIN_FILE=c:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake .. - cmake --build . --config Release after_build: - 7z a fluidsynth.zip %APPVEYOR_BUILD_FOLDER%\build\src\Release\* artifacts: - path: build/fluidsynth.zip name: FluidSynth fluidsynth-1.1.9/.appveyor.yml000066400000000000000000000044221322272076000163640ustar00rootroot00000000000000os: - Visual Studio 2015 build: verbosity: detailed configuration: - Release environment: matrix: - platform: x86 generator: Visual Studio 14 2015 glib-url: http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib_2.28.8-1_win32.zip glib-dev-url: http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib-dev_2.28.8-1_win32.zip pkg-config-url: http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/pkg-config_0.26-1_win32.zip gettext-url: http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime_0.18.1.1-2_win32.zip proxy-libintl-dev-url: http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/proxy-libintl-dev_20100902_win32.zip - platform: x64 generator: Visual Studio 14 2015 Win64 glib-url: http://ftp.gnome.org/pub/gnome/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip glib-dev-url: http://ftp.gnome.org/pub/gnome/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip pkg-config-url: http://ftp.gnome.org/pub/gnome/binaries/win64/dependencies/pkg-config_0.23-2_win64.zip gettext-url: http://ftp.gnome.org/pub/gnome/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip proxy-libintl-dev-url: http://ftp.gnome.org/pub/gnome/binaries/win64/dependencies/proxy-libintl-dev_20100902_win64.zip install: - mkdir c:\deps - cd c:\deps - curl -fsS -o glib.zip %glib-url% - curl -fsS -o glib-dev.zip %glib-dev-url% - curl -fsS -o pkg-config.zip %pkg-config-url% - curl -fsS -o gettext.zip %gettext-url% - curl -fsS -o libintl-dev.zip %proxy-libintl-dev-url% - 7z x glib.zip > NUL - 7z x glib-dev.zip > NUL - 7z x pkg-config.zip > NUL - 7z x gettext.zip > NUL - 7z x libintl-dev.zip > NUL - SET PATH=C:\deps\bin;%PATH% build_script: # - call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 # - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 - cd C:\projects\fluidsynth - mkdir build - cd build - cmake -G "%generator%" .. - cmake --build . --config Release after_build: - 7z a fluidsynth.zip %APPVEYOR_BUILD_FOLDER%\build\src\Release\* c:\deps\bin\libglib*.dll c:\deps\bin\libgthread*.dll c:\deps\bin\*intl*.dll artifacts: - path: build/fluidsynth.zip name: FluidSynth fluidsynth-1.1.9/.travis.yml000066400000000000000000000112241322272076000160250ustar00rootroot00000000000000language: c #sudo: required dist: trusty env: - CMAKE_FLAGS="-Denable-profiling=1" - CMAKE_FLAGS="-Denable-floats=1 -Denable-profiling=1" - CMAKE_FLAGS="-Denable-trap-on-fpe=1" - CMAKE_FLAGS="-Denable-fpe-check=1" 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" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.9 - cmake-data - cmake - libglib2.0-0 - ladspa-sdk env: - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" # 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" # 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 - llvm-toolchain-precise-3.8 packages: - clang-3.8 - cmake-data - cmake - libglib2.0-0 - ladspa-sdk env: - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" # works on Trusty - os: linux addons: apt: sources: - llvm-toolchain-trusty-3.9 packages: - clang-3.9 - cmake-data - cmake - libglib2.0-0 - ladspa-sdk env: - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" # 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" - os: osx osx_image: xcode8 env: - MATRIX_EVAL="brew install gcc5 glib && CC=gcc-5 && CXX=g++-5" - os: osx osx_image: xcode8 env: - MATRIX_EVAL="brew install gcc6 glib && CC=gcc-6 && CXX=g++-6" - os: osx osx_image: xcode8 env: - MATRIX_EVAL="brew install gcc glib libsndfile jack dbus-glib pulseaudio portaudio && CC=gcc-7 && CXX=g++-7" before_install: - if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get update; else brew update; fi - eval "${MATRIX_EVAL}" before_script: - mkdir build && cd build script: - cmake ${CMAKE_FLAGS} "-Denable-portaudio=1" "-Denable-ladspa=1" "-DCMAKE_BUILD_TYPE=RelWithDebInfo" .. - make -j4 fluidsynth-1.1.9/AUTHORS000066400000000000000000000120641322272076000147670ustar00rootroot00000000000000[:Team:] Current development team Tom Moebert Former development team Josh Green Pedro Lopez-Cabanillas David Henningsson [:Idea:] * Samuel Bianchini, Peter Hanappe and Johnathan Lee [:Development:] Many people contributed to FluidSynth, sent suggestions or bug fixes. The project was started by Peter Hanappe who is the main author. Josh Green is the current maintainer. Below you'll find a summary of contributions. * Peter Hanappe. Initiated the project. files: stuck his nose in all files. * Josh Green is the former maintainer and contributed a lot of code directly or indirectly through the Swami and Smurf code base. The SoundFont loader is completely based on his code. He also wrote the alsa sequencer driver. He made many changes and bug fixes, but above all, he's one of the driving forces behind the synthesizer. He also created the current FluidSynth graphic logo with Blender (the blue waves with FluidSynth letters partially submerged). * Markus Nentwig (re-)designed the resonant filter, the chorus, the LADSPA subsystem, the MIDI router, optimized for SSE, made many changes and bug fixes and got the synthesizer to actually work. Most importantly, he used it on stage to make music. * S. Christian Collins did much testing of FluidSynth in regards to EMU10K1 compatibility and provided many synthesis fixes in that regard. * Stephane Letz from Grame wrote most of the MidiShare driver, all of the PortAudio driver, ported iiwusynth to MacOS X, and sent in many fixes. files: iiwu_midishare.c, iiwu_portaudio.c * Antoine Schmitt added the sequencer support, support for sample loading (RAM Sfont), developed the MacroMedia Director Xtra, and send in many many bug reports. Thanks to Antoine, the synthesizer finds its way to multi-media developers. files: in bindings/director/ and iiwu_seq.{c,h}, iiwu_event.{c,h}, iiwu_event_priv.h, iiwu_seqbind.{c,h}, iiwu_ramsfont.{c,h} * Bob Ham added the code for "bank select" MIDI messages and send code to define the synth's ALSA sequencer client name. files: iiwu_midi.c, iiwu_alsa.c, iiwusynth.c, iiwusynth.h. * Tim Goetze sent many patches and implemented the all_notes_off. He also sent his code for the new ALSA driver. files: iiwu_synth.c, iiwu_chan.c, iiwu_voice.c, iiwu_alsa.c * Norbert Schnell from Ircam's jMax Team wrote most of the jMax/FTS interface in a record time. He also pointed me to the technique of using a lookup table for the interpolation coefficients. file: iiwu_fts.c, iiwu_synth.c * The initial alsa driver was based on the jMax alsa driver by Francois Dechelle and his Real-time Team at Ircam (http://www.ircam.fr/jmax). The jMax code was based upon Ardour's alsa_device.cc by Paul Barton-Davis. file: iiwu_alsa.c * Code was borrowed from the glib library to the smurf files. The goal was to make iiwusynth independent from any library for maximum portability. * David Henningsson added code for fast rendering of MIDI files, rewrote the thread safety for 1.1.2, and fixed many bugs. * The midi device uses code from jMax's alsarawmidi.c file and from Smurf's midi_alsaraw.c by Josh Green. file: iiwu_alsa.c * The reverb algorithm was written by Jezar (http://www.dreampoint.co.uk). His code is public domain. The code was translated to C by Peter Hanappe. file: iiwu_synth.c * The original code for the chorus effect was written by Juergen Mueller and sundry contributors. * Bob Ham added LADCCA support. * Ebrahim Mayat made big efforts for compiling and running FluidSynth on MacOS X. He also wrote the README-OSX file. * Martin Uddén's midi package was used. His files are integrated into the iiwu_midi file. Martin Uddén file: iiwu_midi.c * Ken Ellinwood send in a patch to add bank offsets to SoundFonts. An adapted version was integrated in the source code. files: fluid_cmd.c, fluidsynth/synth.h, fluid_synth.c. * Some interpolation algorihms were used that were found in the music-dsp archives (http://www.smartelectronix.com/musicdsp). They were written by Joshua Scholar and others. file: iiwu_synth.c * Macros to {increment,decrement} the 64-bit fixed point phase were borrowed from Mozilla's macros to handle the Long-long type (64-bit signed integer type). Mozilla NSPR library, www.mozilla.org. file: iiwu_phase.h * KO Myung-Hun for OS/2 support with Dart audio driver. * Pedro Lopez-Cabanillas wrote the CoreMIDI driver for MacOSX, the CMake based build system, revised the doxygen documentation, sequencer examples, fixes. * Matt Giuca improved the midi player by letting it load midi files from RAM, and by making it handle EOT events. * Growing list of individuals who contributed bug fixes, corrections and minor features: Nicolas Boulicault for ALSA sequencer midi.portname setting. Werner Schweer Dave Philips Anthony Green Jake Commander Fernando Pablo Lopez-Lezcano Raoul Bonisch Sergey Pavlishin Eric Van Buggenhaut Ken Ellinwood Takashi Iwai Bob Ham Gerald Pye Rui Nuno Capela Frieder Bürzele Henri Manson Mihail Zenkov Paul Millar Nick Daly David Hilvert Bernat Arlandis i Mañó Sven Meier fluidsynth-1.1.9/CMakeLists.txt000066400000000000000000000503741322272076000164650ustar00rootroot00000000000000# FluidSynth - A Software Synthesizer # # Copyright (C) 2003-2011 Peter Hanappe and others. # # 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 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307, USA # CMake based build system. Pedro Lopez-Cabanillas cmake_minimum_required ( VERSION 3.0.2 ) project ( FluidSynth C ) set ( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake_admin ) # FluidSynth package name set ( PACKAGE "fluidsynth" ) # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 1 ) set ( FLUIDSYNTH_VERSION_MINOR 1 ) set ( FLUIDSYNTH_VERSION_MICRO 9 ) set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" ) set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) # libfluidsynth - Library version # *** NOTICE *** # Update library version upon each release (follow these steps in order) # if any source code changes: REVISION++ # if any interfaces added/removed/changed: REVISION=0 # if any interfaces removed/changed (compatibility broken): CURRENT++ # if any interfaces have been added: AGE++ # 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 1 ) set ( LIB_VERSION_AGE 7 ) set ( LIB_VERSION_REVISION 0 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) # Options disabled by default option ( enable-floats "enable type float instead of double for DSP samples" off ) option ( enable-profiling "profile the dsp code" off ) option ( enable-ladspa "enable LADSPA effect units" off ) option ( enable-portaudio "compile PortAudio support" off ) option ( enable-trap-on-fpe "enable SIGFPE trap on Floating Point Exceptions" off ) option ( enable-fpe-check "enable Floating Point Exception checks and debug messages" off ) option ( enable-debug "enable debugging (default=no)" off ) # Options enabled by default option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-libsndfile "compile libsndfile support (if it is available)" on ) option ( enable-aufile "compile support for sound file output" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-jack "compile JACK support (if it is available)" on ) option ( enable-midishare "compile MidiShare support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) option ( enable-dbus "compile DBUS support (if it is available)" on ) option ( BUILD_SHARED_LIBS "Build a shared object or DLL" on ) option ( enable-ipv6 "enable ipv6 support " on ) # Platform specific options if ( CMAKE_SYSTEM MATCHES "Linux" ) option ( enable-ladcca "compile LADCCA support if it is available (deprecated)" off ) option ( enable-lash "compile LASH support (if it is available)" on ) option ( enable-alsa "compile ALSA support (if it is available)" on ) endif ( CMAKE_SYSTEM MATCHES "Linux" ) if ( CMAKE_SYSTEM MATCHES "Darwin" ) option ( enable-coreaudio "compile CoreAudio support (if it is available)" on ) option ( enable-coremidi "compile CoreMIDI support (if it is available)" on ) option ( enable-framework "create a Mac OSX style FluidSynth.framework" on ) endif ( CMAKE_SYSTEM MATCHES "Darwin" ) if ( CMAKE_SYSTEM MATCHES "OS2" ) option ( enable-dart "compile DART support (if it is available)" on ) set ( enable-ipv6 off ) endif ( CMAKE_SYSTEM MATCHES "OS2" ) # Initialize the library directory name suffix. if (NOT MINGW) if ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) set ( _init_lib_suffix "64" ) else ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) set ( _init_lib_suffix "" ) endif ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) else () set ( _init_lib_suffix "" ) endif() set ( LIB_SUFFIX ${_init_lib_suffix} CACHE STRING "library directory name suffix (32/64/nothing)" ) mark_as_advanced ( LIB_SUFFIX ) # Default install directory names include ( DefaultDirs ) # Basic C library checks include ( CheckSTDC ) include ( CheckIncludeFile ) include ( CheckFunctionExists ) check_include_file ( string.h HAVE_STRING_H ) check_include_file ( stdlib.h HAVE_STDLIB_H ) check_include_file ( stdio.h HAVE_STDIO_H ) check_include_file ( math.h HAVE_MATH_H ) check_include_file ( errno.h HAVE_ERRNO_H ) check_include_file ( stdarg.h HAVE_STDARG_H ) check_include_file ( unistd.h HAVE_UNISTD_H ) check_include_file ( memory.h HAVE_MEMORY_H ) check_include_file ( sys/mman.h HAVE_SYS_MMAN_H ) check_include_file ( sys/types.h HAVE_SYS_TYPES_H ) check_include_file ( sys/time.h HAVE_SYS_TIME_H ) check_include_file ( sys/stat.h HAVE_SYS_STAT_H ) check_include_file ( sys/ioctl.h HAVE_SYS_IOCTL_H ) check_include_file ( fcntl.h HAVE_FCNTL_H ) check_include_file ( sys/socket.h HAVE_SYS_SOCKET_H ) check_include_file ( netinet/in.h HAVE_NETINET_IN_H ) check_include_file ( netinet/tcp.h HAVE_NETINET_TCP_H ) check_include_file ( arpa/inet.h HAVE_ARPA_INET_H ) check_include_file ( limits.h HAVE_LIMITS_H ) check_include_file ( pthread.h HAVE_PTHREAD_H ) check_include_file ( signal.h HAVE_SIGNAL_H ) check_include_file ( getopt.h HAVE_GETOPT_H ) include ( TestInline ) include ( TestVLA ) include ( TestBigEndian ) test_big_endian ( WORDS_BIGENDIAN ) unset ( LIBFLUID_CPPFLAGS CACHE ) unset ( LIBFLUID_LIBS CACHE ) unset ( FLUID_CPPFLAGS CACHE ) unset ( FLUID_LIBS CACHE ) # Options for the GNU C compiler only if ( CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" ) if ( NOT APPLE AND NOT OS2 ) set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed" ) set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" ) endif ( NOT APPLE AND NOT OS2 ) if ( OS2 ) set ( GNUCC_VISIBILITY_FLAG "" ) else ( OS2 ) set ( GNUCC_VISIBILITY_FLAG "-fvisibility=hidden" ) endif ( OS2 ) set ( GNUCC_WARNING_FLAGS "-Wall -W -Wpointer-arith -Wbad-function-cast -Wno-cast-qual -Wcast-align -Wstrict-prototypes -Wno-unused-parameter -Wdeclaration-after-statement" ) set ( CMAKE_C_FLAGS_DEBUG "-std=gnu89 -g ${GNUCC_VISIBILITY_FLAG} -DDEBUG ${GNUCC_WARNING_FLAGS}" ) set ( CMAKE_C_FLAGS_RELEASE "-std=gnu89 -O2 -fomit-frame-pointer -finline-functions ${GNUCC_VISIBILITY_FLAG} -DNDEBUG ${GNUCC_WARNING_FLAGS}" ) set ( CMAKE_C_FLAGS_RELWITHDEBINFO "-std=gnu89 -O2 -g -fomit-frame-pointer -finline-functions ${GNUCC_VISIBILITY_FLAG} -DNDEBUG ${GNUCC_WARNING_FLAGS}" ) endif ( CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" ) # Windows unset ( WINDOWS_SUPPORT CACHE ) unset ( WINDOWS_LIBS CACHE ) unset ( MINGW32 CACHE ) if ( WIN32 ) include ( CheckIncludeFiles ) 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" ) set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" ) set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" ) set ( CMAKE_DEBUG_POSTFIX "_debug" ) # MinGW compiler (a Windows GCC port) if ( MINGW ) set ( MINGW32 1 ) add_definitions ( -mms-bitfields ) endif ( MINGW ) else ( WIN32 ) # Check PThreads, but not in Windows find_package ( Threads REQUIRED ) set ( HAVE_LIBPTHREAD ${Threads_FOUND} ) set ( LIBFLUID_LIBS "m" ${CMAKE_THREAD_LIBS_INIT} ) endif ( WIN32 ) # IBM OS/2 unset ( DART_SUPPORT CACHE ) unset ( DART_LIBS CACHE ) unset ( DART_INCLUDE_DIRS CACHE ) if ( CMAKE_SYSTEM MATCHES "OS2" ) set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Zbin-files" ) set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Zbin-files" ) if ( enable-dart ) check_include_files ( "os2.h;os2me.h" HAVE_DART_H ) set ( DART_SUPPORT ${HAVE_DART_H} ) unset ( DART_INCLUDE_DIRS CACHE ) endif ( enable-dart ) endif ( CMAKE_SYSTEM MATCHES "OS2" ) # Solaris / SunOS if ( CMAKE_SYSTEM MATCHES "SunOS" ) set ( FLUID_LIBS "${FLUID_LIBS};nsl;socket" ) set ( LIBFLUID_LIBS "${LIBFLUID_LIBS};nsl;socket" ) endif ( CMAKE_SYSTEM MATCHES "SunOS" ) # Apple Mac OSX unset ( COREAUDIO_SUPPORT CACHE ) unset ( COREAUDIO_LIBS CACHE ) unset ( COREMIDI_SUPPORT CACHE ) unset ( COREMIDI_LIBS CACHE ) unset ( DARWIN CACHE ) unset ( MACOSX_FRAMEWORK CACHE ) if ( CMAKE_SYSTEM MATCHES "Darwin" ) set ( DARWIN 1 ) set ( CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}${LIB_SUFFIX} ) if ( enable-coreaudio ) check_include_file ( CoreAudio/AudioHardware.h COREAUDIO_FOUND ) if ( COREAUDIO_FOUND ) set ( COREAUDIO_SUPPORT ${COREAUDIO_FOUND} ) set ( COREAUDIO_LIBS "-Wl,-framework,CoreAudio,-framework,AudioUnit" ) endif ( COREAUDIO_FOUND ) endif ( enable-coreaudio ) if ( enable-coremidi ) check_include_file ( CoreMIDI/MIDIServices.h COREMIDI_FOUND ) if ( COREMIDI_FOUND ) set ( COREMIDI_SUPPORT ${COREMIDI_FOUND} ) set ( COREMIDI_LIBS "-Wl,-framework,CoreMIDI,-framework,CoreServices" ) endif ( COREMIDI_FOUND ) endif ( enable-coremidi ) if ( enable-framework ) set ( MACOSX_FRAMEWORK 1 ) endif ( enable-framework ) endif ( CMAKE_SYSTEM MATCHES "Darwin" ) unset ( HAVE_INETNTOP CACHE ) unset ( IPV6_SUPPORT CACHE ) CHECK_FUNCTION_EXISTS ( "inet_ntop" HAVE_INETNTOP ) if ( enable-ipv6 ) if ( HAVE_INETNTOP ) set ( IPV6_SUPPORT 1 ) endif ( HAVE_INETNTOP ) endif ( enable-ipv6 ) unset ( WITH_FLOAT CACHE ) if ( enable-floats ) set ( WITH_FLOAT 1 ) endif ( enable-floats ) unset ( WITH_PROFILING CACHE ) if ( enable-profiling ) set ( WITH_PROFILING 1 ) endif ( enable-profiling ) unset ( HAVE_LIBDL CACHE ) unset ( LADSPA_SUPPORT CACHE ) if ( enable-ladspa ) check_include_file ( ladspa.h LADSPA_SUPPORT ) if ( LADSPA_SUPPORT ) set ( LADSPA 1 ) if ( CMAKE_DL_LIBS ) set ( HAVE_LIBDL 1 ) set ( LIBFLUID_LIBS "${LIBFLUID_LIBS};${CMAKE_DL_LIBS}" ) endif ( CMAKE_DL_LIBS ) endif ( LADSPA_SUPPORT ) endif ( enable-ladspa ) unset ( ENABLE_TRAPONFPE CACHE ) unset ( TRAP_ON_FPE CACHE ) if ( enable-trap-on-fpe AND NOT APPLE AND NOT WIN32 ) set ( ENABLE_TRAPONFPE 1 ) set ( TRAP_ON_FPE 1 ) endif ( enable-trap-on-fpe AND NOT APPLE AND NOT WIN32 ) unset ( ENABLE_FPECHECK CACHE ) unset ( FPE_CHECK CACHE ) if ( enable-fpe-check ) set ( ENABLE_FPECHECK 1 ) set ( FPE_CHECK 1 ) endif ( enable-fpe-check ) if ( enable-debug ) set ( CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo" FORCE ) endif ( enable-debug ) if ( NOT CMAKE_BUILD_TYPE ) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo" FORCE ) endif ( NOT CMAKE_BUILD_TYPE ) unset ( ENABLE_DEBUG CACHE ) unset ( DEBUG CACHE ) if ( CMAKE_BUILD_TYPE MATCHES "Debug" ) set ( ENABLE_DEBUG 1 ) set ( DEBUG 1 ) endif ( CMAKE_BUILD_TYPE MATCHES "Debug" ) if(NOT enable-pkgconfig) FIND_LIBRARY( GLIB_LIB NAMES glib glib-2.0 PATH GLIB_LIBRARY_DIR ) FIND_LIBRARY( GTHREAD_LIB NAMES gthread gthread-2.0 PATH GTHREAD_LIBRARY_DIR ) FIND_PATH( GLIBH_DIR glib.h PATH GLIB_INCLUDE_DIR ) FIND_PATH( GLIBCONF_DIR glibconfig.h PATH GLIBCONF_INCLUDE_DIR ) IF( GLIB_LIB MATCHES "GLIB_LIB-NOTFOUND" OR GTHREAD_LIB MATCHES "GTHREAD_LIB-NOTFOUND" OR GLIBH_DIR MATCHES "GLIBH_DIR-NOTFOUND" OR GLIBCONF_DIR MATCHES "GLIBCONF_DIR-NOTFOUND") message( WARNING "Not sure if I found GLIB, continuing anyway.") ENDIF() SET( GLIB_INCLUDE_DIRS ${GLIBH_DIR} ${GLIBCONF_DIR} ) SET( GLIB_LIBRARIES ${GLIB_LIB} ${GTHREAD_LIB} ) message( STATUS "GLIB_INCLUDE_DIRS: " ${GLIB_INCLUDE_DIRS} ) message( STATUS "GLIB_LIBRARIES: " ${GLIB_LIBRARIES} ) else(NOT enable-pkgconfig) find_package ( PkgConfig REQUIRED ) # Mandatory libraries: glib and gthread pkg_check_modules ( GLIB REQUIRED glib-2.0>=2.6.5 gthread-2.0>=2.6.5 ) include ( UnsetPkgConfig ) # Optional features unset ( LIBSNDFILE_SUPPORT CACHE ) unset ( LIBSNDFILE_HASVORBIS CACHE ) if ( enable-libsndfile ) pkg_check_modules ( LIBSNDFILE sndfile>=1.0.0 ) set ( LIBSNDFILE_SUPPORT ${LIBSNDFILE_FOUND} ) if ( LIBSNDFILE_SUPPORT ) pkg_check_modules ( LIBSNDFILE_VORBIS sndfile>=1.0.18 ) set ( LIBSNDFILE_HASVORBIS ${LIBSNDFILE_VORBIS_FOUND} ) endif ( LIBSNDFILE_SUPPORT ) else ( enable-libsndfile ) unset_pkg_config ( LIBSNDFILE ) unset_pkg_config ( LIBSNDFILE_VORBIS ) endif ( enable-libsndfile ) unset ( PULSE_SUPPORT CACHE ) if ( enable-pulseaudio ) pkg_check_modules ( PULSE libpulse-simple>=0.9.8 ) set ( PULSE_SUPPORT ${PULSE_FOUND} ) else ( enable-pulseaudio ) unset_pkg_config ( PULSE ) endif ( enable-pulseaudio ) unset ( ALSA_SUPPORT CACHE ) if ( enable-alsa ) pkg_check_modules ( ALSA alsa>=0.9.1 ) set ( ALSA_SUPPORT ${ALSA_FOUND} ) else ( enable-alsa ) unset_pkg_config ( ALSA ) endif ( enable-alsa ) unset ( PORTAUDIO_SUPPORT CACHE ) if ( enable-portaudio ) pkg_check_modules ( PORTAUDIO portaudio-2.0>=19 ) set ( PORTAUDIO_SUPPORT ${PORTAUDIO_FOUND} ) else ( enable-portaudio ) unset_pkg_config ( PORTAUDIO ) endif ( enable-portaudio ) unset ( JACK_SUPPORT CACHE ) if ( enable-jack ) pkg_check_modules ( JACK jack ) set ( JACK_SUPPORT ${JACK_FOUND} ) else ( enable-jack ) unset_pkg_config ( JACK ) endif ( enable-jack ) unset ( LASH_SUPPORT CACHE ) if ( enable-lash ) pkg_check_modules ( LASH lash-1.0>=0.3 ) if ( LASH_FOUND ) set ( LASH_SUPPORT 1 ) add_definitions ( -DHAVE_LASH ) endif ( LASH_FOUND ) else ( enable-lash ) unset_pkg_config ( LASH ) remove_definitions( -DHAVE_LASH ) endif ( enable-lash ) unset ( LADCCA_SUPPORT CACHE ) if ( enable-ladcca ) pkg_check_modules ( LADCCA ladcca-1.0>=0.3 ) set ( LADCCA_SUPPORT ${LADCCA_FOUND} ) else ( enable-ladcca ) unset_pkg_config ( LADCCA ) endif ( enable-ladcca ) unset ( DBUS_SUPPORT CACHE ) if ( enable-dbus ) pkg_check_modules ( DBUS dbus-1>=1.0.0 ) set ( DBUS_SUPPORT ${DBUS_FOUND} ) else ( enable-dbus ) unset_pkg_config ( DBUS ) endif ( enable-dbus ) endif(NOT enable-pkgconfig) unset ( AUFILE_SUPPORT CACHE ) if ( enable-aufile ) set ( AUFILE_SUPPORT 1 ) endif ( enable-aufile ) find_package ( OSS QUIET ) set ( OSS_SUPPORT ${OSS_FOUND} ) unset ( MIDISHARE_SUPPORT CACHE ) if ( enable_midishare ) find_package ( MidiShare ) set ( MIDISHARE_SUPPORT ${MidiShare_FOUND} ) else ( enable_midishare ) unset ( MidiShare_LIBS CACHE ) endif ( enable_midishare ) unset ( WITH_READLINE CACHE ) if ( enable-readline ) find_package ( Readline ) set ( FOUND_READLINE ${HAVE_READLINE} ) if ( HAVE_READLINE ) set ( WITH_READLINE 1 ) set ( READLINE_LIBS ${READLINE_LIBRARIES} ) endif ( HAVE_READLINE ) else ( enable-readline ) unset ( READLINE_LIBS CACHE ) endif ( enable-readline ) # General configuration file configure_file ( ${CMAKE_SOURCE_DIR}/src/config.cmake ${CMAKE_BINARY_DIR}/config.h ) add_definitions ( -DHAVE_CONFIG_H ) # Extra configuration file for MS VisualC compiler if ( MSVC ) configure_file ( ${CMAKE_SOURCE_DIR}/src/config_win32.cmake ${CMAKE_BINARY_DIR}/config_win32.h ) endif ( MSVC ) # Process subdirectories add_subdirectory ( src ) add_subdirectory ( include ) add_subdirectory ( doc ) # pkg-config support set ( prefix "${CMAKE_INSTALL_PREFIX}" ) set ( exec_prefix "\${prefix}" ) set ( libdir "\${exec_prefix}/${LIB_INSTALL_DIR}${LIB_SUFFIX}" ) set ( includedir "\${prefix}/${INCLUDE_INSTALL_DIR}" ) configure_file ( fluidsynth.pc.in ${CMAKE_BINARY_DIR}/fluidsynth.pc IMMEDIATE @ONLY ) install ( FILES ${CMAKE_BINARY_DIR}/fluidsynth.pc DESTINATION ${LIB_INSTALL_DIR}${LIB_SUFFIX}/pkgconfig ) # Extra targets for Unix build environments if ( UNIX ) # RPM spec configure_file ( fluidsynth.spec.in ${CMAKE_BINARY_DIR}/fluidsynth.spec IMMEDIATE @ONLY ) # uninstall custom target configure_file ( "${CMAKE_SOURCE_DIR}/cmake_admin/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target ( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") # tarball custom target add_custom_target ( tarball COMMAND mkdir -p ${PACKAGE}-${VERSION} COMMAND cp -r bindings ${PACKAGE}-${VERSION} COMMAND cp -r cmake_admin ${PACKAGE}-${VERSION} COMMAND cp -r doc ${PACKAGE}-${VERSION} COMMAND cp -r include ${PACKAGE}-${VERSION} COMMAND cp -r src ${PACKAGE}-${VERSION} COMMAND cp AUTHORS ChangeLog CMakeLists.txt LICENSE ${PACKAGE}.* INSTALL NEWS README* THANKS TODO ${PACKAGE}-${VERSION} # COMMAND tar -cj --exclude .svn --exclude Makefile.am -f ${PACKAGE}-${VERSION}.tar.bz2 ${PACKAGE}-${VERSION} # COMMAND tar -cz --exclude .svn --exclude Makefile.am -f ${PACKAGE}-${VERSION}.tar.gz ${PACKAGE}-${VERSION} # COMMAND zip -qr ${PACKAGE}-${VERSION}.zip ${PACKAGE}-${VERSION} -x '*.svn*' -x '*Makefile.am' COMMAND tar -cj --exclude .svn -f ${PACKAGE}-${VERSION}.tar.bz2 ${PACKAGE}-${VERSION} COMMAND tar -cz --exclude .svn -f ${PACKAGE}-${VERSION}.tar.gz ${PACKAGE}-${VERSION} COMMAND zip -qr ${PACKAGE}-${VERSION}.zip ${PACKAGE}-${VERSION} -x '*.svn*' COMMAND rm -rf ${PACKAGE}-${VERSION} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) # tarball custom target add_custom_target ( tarball-autotools COMMAND mkdir -p ${PACKAGE}-${VERSION} COMMAND cp -r bindings ${PACKAGE}-${VERSION} COMMAND cp -r cmake_admin ${PACKAGE}-${VERSION} COMMAND cp -r doc ${PACKAGE}-${VERSION} COMMAND cp -r include ${PACKAGE}-${VERSION} COMMAND cp -r src ${PACKAGE}-${VERSION} COMMAND cp AUTHORS ChangeLog CMakeLists.txt ../LICENSE ${PACKAGE}.* INSTALL NEWS README* ../README.md THANKS TODO ${PACKAGE}-${VERSION} COMMAND cp acinclude.m4 autogen.sh configure.ac install-sh missing mkinstalldirs Makefile.am ${PACKAGE}-${VERSION} # Files below are added when running autogen.sh # COMMAND cp -r autom4te.cache ${PACKAGE}-${VERSION} COMMAND cp -r m4 ${PACKAGE}-${VERSION} COMMAND cp aclocal.m4 compile config.guess config.sub configure depcomp ltmain.sh Makefile.in ${PACKAGE}-${VERSION} COMMAND tar -cj --exclude .svn -f ${PACKAGE}-${VERSION}.tar.bz2 ${PACKAGE}-${VERSION} COMMAND tar -cz --exclude .svn -f ${PACKAGE}-${VERSION}.tar.gz ${PACKAGE}-${VERSION} COMMAND zip -qr ${PACKAGE}-${VERSION}.zip ${PACKAGE}-${VERSION} -x '*.svn*' COMMAND rm -rf ${PACKAGE}-${VERSION} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) endif ( UNIX ) include ( report ) # CPack support set ( CPACK_PACKAGE_DESCRIPTION_SUMMARY "FluidSynth real-time synthesizer" ) set ( CPACK_PACKAGE_VENDOR "fluidsynth.org" ) set ( CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md" ) set ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE" ) set ( CPACK_PACKAGE_VERSION_MAJOR ${FLUIDSYNTH_VERSION_MAJOR} ) set ( CPACK_PACKAGE_VERSION_MINOR ${FLUIDSYNTH_VERSION_MINOR} ) set ( CPACK_PACKAGE_VERSION_PATCH ${FLUIDSYNTH_VERSION_MICRO} ) set ( CPACK_PACKAGE_EXECUTABLES "fluidsynth" "FluidSynth CLI" ) # source packages set ( CPACK_SOURCE_GENERATOR TGZ;TBZ2;ZIP ) set ( CPACK_SOURCE_IGNORE_FILES "/.svn/;/build/;~$;.cproject;.project;/.settings/;${CPACK_SOURCE_IGNORE_FILES}" ) set ( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" ) set ( CPACK_SOURCE_STRIP_FILES OFF ) # binary packages include ( InstallRequiredSystemLibraries ) set ( CPACK_GENERATOR STGZ;TGZ;TBZ2;ZIP ) set ( CPACK_PACKAGE_NAME ${PACKAGE} ) set ( CPACK_STRIP_FILES ON ) include ( CPack ) fluidsynth-1.1.9/ChangeLog000066400000000000000000002164211322272076000154740ustar00rootroot00000000000000This file is no longer used. For detailed Changelog information, please refer to the version control system's commits. For an overview of differences between versions, see: http://sourceforge.net/apps/trac/fluidsynth/wiki/ChangeLog1_1_2 http://sourceforge.net/apps/trac/fluidsynth/wiki/ChangeLog1_1_1 etc. For developer related "What's new"-information, doc/fluidsynth-v11-devdoc contains valuable information. === OLD === 2009-05-01 Pedro Lopez-Cabanillas * configure.ac: fix for win32 build. 2009-05-01 Pedro Lopez-Cabanillas * doc/Doxyfile: added fluid_filerenderer.c to Doxygen documentation. * doc/fluidsynth-v11-devdoc.txt: license changed to CC-BY-SA 3.0 * doc/fluidsynth_arpeggio.c: new example added. * doc/fluidsynth_metronome.c: new example added. * include/fluidsynth.h, include/fluidsynth/audio.h, include/fluidsynth/settings.h: Doxygen documentation. * src/fluid_settings.c: Doxygen documentation. 2009-04-27 Josh Green * include/fluidsynth/audio.h: Moved new filerenderer documentation to source file. * src/config_win32.h.in: Added 'typedef int socklen_t;' to the correct place. * src/fluid_filerenderer.c: Removed 2 extra pasted duplicates of the file, moved Doxygen documentation from header file and added "API 1.1.0" designators. * src/config_win32.h: Removed from subversion, since it is generated from config_win32.h.in. 2009-04-26 Josh Green * configure.ac: Added glib 2.10 as a dependency, added notes in output for LASH, LADCCA and READLINE that they are GPL. * src/fluid_io.c: Moved code to fluid_sys.c and removed. * src/config_win32.h: Added "typedef int socklen_t" definition. * src/fluid_defsfont.h: Removed glib ripped code. * src/fluid_oss.c: Fixed warnings where return value of write() was being ignored. * src/fluid_sys.c: Re-organized, implemented portable fluid_curtime() and fluid_utime() using glib functions and removed old platform specific code, implemented fluid_thread functionality using glib and removed old platform specific code, fluid_istream_readline(), fluid_istream_gets() and fluid_ostream_printf() should now work on WIN32 also, added code for WIN32 for TCP sockets (not yet tested). * src/fluid_sys.h: Added fluid_gerror_message() macro to extract message safely from GError structures, replaced fluid_mutex macros with portable implementations using glib, removed new_fluid_client_socket() and delete_fluid_client_socket() which were never implemented or used. * src/fluidsynth.c: Added call to g_thread_init(). * src/fluidsynth_priv.h: Integer types now use glib integer types. 2009-04-11 Josh Green * FluidSynth release 1.0.9 "A Sound Future" * configure.ac: Bumped version, no library interfaces added, removed or changed. * doc/Makefile.am: Removed html and api folders from EXTRA_DIST. * src/fluid_synth.c (fluid_synth_program_change): Preset substitute warning now outputs MIDI channel. 2009-04-02 Pedro Lopez-Cabanillas * src/config_win32.h: fix compilation under MSVC 2008 and older 2009-03-15 Josh Green * ltconfig: Removed obsolete ltconfig script by suggestion of Sven Hoexter. * doc/fluidsynth.1: Some fixes from Sven Hoexter. * src/fluid_adriver.c: Re-order of default drivers to jack, alsa, pulse. * src/fluidsynth.c (fluid_synth_program_change): Added preset selection fallback logic: Melodic - Fallback to Bank0:prognum followed by Bank0:Program0, Percussion - Fallback to 128:0, code re-organization. 2009-03-08 Josh Green * src/fluid_jack.c: Added support for Jack MIDI. * src/fluid_mdriver.c: Registered Jack MIDI driver. * README-OSX: Update from Ebrahim Mayat. 2009-02-28 Pedro Lopez-Cabanillas * src/fluid_midi.c: Fix for ticket #22 (Wrong tempo changes) * src/fluid_midi.h: delta-time accumulator moved to fluid_midi_file struct. 2009-02-03 Josh Green * Applied patch from KO Myung-Hun for OS/2 support including Dart audio driver. 2009-01-29 Josh Green * src/Makefile.am: Added PortAudio driver conditional build. * src/fluid_adriver.c: Registered fluid_portaudio_driver_settings. * src/fluid_portaudio.c: Completely overhauled for Portaudio 19. This driver appears to have been unbuildable before. 2009-01-08 Pedro Lopez-Cabanillas * configure.ac: detection of CoreMIDI support. Ticket #18. * src/Makefile.am: conditional build of CoreMIDI driver. * src/fluid_coremidi.c: Basic CoreMIDI driver. * src/fluid_mdriver.c: added CoreMIDI driver. 2009-01-08 Josh Green * configure.ac: Followed GTK's lead for some unexplained magic for stupid libtool version parameters (fixes autogen.sh bomb on undefined macro LT_REVISION/LT_CURRENT/LT_AGE). Added AC_CONFIG_MACRO_DIR([m4]) as suggested by libtoolize. * Makefile.am: Added ACLOCAL_AMFLAGS=-I m4 as suggested by libtoolize. 2008-12-23 Josh Green * configure.ac: Added detection of PulseAudio driver. * src/Makefile.am: Added conditional build of PulseAudio driver. * src/fluid_adriver.c: Added PulseAudio driver and re-sorted drivers by use preference. * src/fluid_chan.c: Using MIDI enums for initializing channel CC values, added supported for RPN GM MIDI messages Bend Range, Fine Tune and Coarse Tune, added check for out of range NRPN parameters. * src/fluid_midi.h: Added RPN enum midi_rpn_event. * src/fluid_pulse.c: New PulseAudio driver. 2008-09-22 Pedro Lopez-Cabanillas * src/fluid_dsound.c: Fix for ticket #16 - dsound device can't be selected. 2008-09-07 Josh Green * src/fluid_alsa.c (new_fluid_alsa_seq_driver): Patch from Nicolas Boulicault to add ALSA sequencer midi.portname setting. * src/fluid_conv.h: S. Christian Collins' patch - changed FLUID_ATTEN_POWER_FACTOR from -531.509 to -200.0. * src/fluid_defsfont.c (fluid_defpreset_noteon): S. Christian Collins' patch - crash bug fix related to using certain modulators in a preset. * src/fluid_mdriver.c: Pedro Lopez-Cabanillas' patch which adds a midi.winmidi.device setting. * src/fluid_mod.c: S. Christian Collins' patch - Stop forcing velocity based filtering and a couple of calculation fixes to transform functions. * src/fluid_synth.c: Nicolas Boulicault's patch to add midi.portname setting. (fluid_synth_program_change): added fix to properly search for a percussion instrument * src/fluid_synth.h: Changed FLUID_NUM_PROGRAMS to 128 and set DRUM_INST_BANK to 128. * src/fluid_voice.c (fluid_voice_write): S. Christian Collins' patch - force velocity envelope value to be that of the previous stage when switching from decay to sustain and filter calculation now uses synthesizer baud rate rather than fixed at 44100. (fluid_voice_update_param): S. Christian Collins' patch - Use multiplier for GEN_ATTENUATION to be compatible with EMU10K1 cards. * src/fluid_winmidi.c: Pedro Lopez-Cabanillas' patch which adds a midi.winmidi.device option. * src/fluidsynth.c: Nicolas Boulicault's patch which adds midi.portname setting. Pedro Lopez-Cabanillas' patch which breaks out of argument processing loop for non getopt option argument handling when a non option is encountered and not using Readline. 2007-11-17 Josh Green * FluidSynth release 1.0.8 "Its about funky time!" * configure.ac: Bumped LT_REVISION and added call to AM_PROG_CC_C_O macro. * Makefile: Updated fluidsynth.prj to fluidsynth.anjuta * README-OSX: Update from Ebrahim Mayat for OS X Leopard * acinclude.m4: Fixed embedded main function in AM_PATH_READLINE macro. 2007-11-11 Josh Green * configure.ac: Added --enable-trap-on-fpe and --enable-fpe-check to assist with Floating Point Exception debugging. * src/fluid_chorus.c: Reverted the rest of the chorus "Effect level clip" patch, until something better is devised. * src/fluid_synth.c: Added support for trapping on Floating Point Exceptions on GLIBC systems, to aid developers in tracking down FPEs with gdb, removed buffer alignment hacks since they are no longer needed (not using SSE currently). * src/fluid_sys.c (fluid_time_config): Added check for a CPU freq calculation of 0.0, since this test is inadequate to begin with and was coming up as 0.0 on my laptop, causing a FPE. Will replace with real timer functions, in the future. * src/fluid_voice.c: Removed zap_almost_zero macro as it was buggy and had issues which went away when gcc optimization was turned off and in the case of !WITH_FLOAT was using abs() which is integer based and would cause FPEs. (fluid_voice_write): Removed a memory alignment hack and moved a call to fluid_fpe_check() to a better location. (fluid_voice_effects): Replaced zap_almost_zero with a call to fabs(), added fluid_fpe_check() call. * src/fluidsynth_priv.h: Removed FLUID_ALIGN16BYTE hack, as it is no longer needed. 2007-11-10 Josh Green * doc/fluidsynth.1: Updated man page with current command line options and other changes (minor). * include/fluidsynth/synth.h: Reverted "Effect level clip" patch as it seems to cause chorus count to have a much lessor effect. 2007-09-20 Josh Green * Doc updates to AUTHORS and latest README-OSX from Ebrahim Mayat. * src/config_win32.h.in: VERSION is now filled in at configure time. * src/fluid_alsa.c (fluid_alsa_audio_run_s16): Fixed bug which was causing weird crashes with QSynth when new_fluid_audio_driver2() when audio meters were enabled (user data parameter was being used as a fluid_synth_t instance). Synth instance is now no longer used in this case (it was only used for 16 bit dithering before). * src/fluid_oss.c: Fixed the same bug that was affecting ALSA driver. * src/fluid_rev.c: Reverted to old commented out code in regards to reverb level. * src/fluid_synth.c (fluid_synth_dither_s16): Now no longer uses fluid_synth_t instance, but accepts a pointer to an integer instead for keeping track of dithering buffer index (all that the synth instance was being used for). * src/fluid_synth.c (fluid_synth_one_block): Reverted patch which performs assignment of chorus and reverb levels in synthesis loop, until a better scheme is devised (unnecessary CPU consumption). * Added Visual Studio .sln and .vcproj files and some minor source changes to get FluidSynth to build with it. * Back-converted Visual Studio project to VC++ 6 project for users using that build platform (not tested). 2007-09-02 Josh Green * configure.ac: Removed SSE and longlong related switches (SSE support removed for now and longlong is now always used). * : Applied effect level clip patch from David Hilvert see http://fluidsynth.resonance.org/trac/ticket/2. * : Applied reverb damp scaling patch from David Hilvert see http://fluidsynth.resonance.org/trac/ticket/3. * src/fluid_dsp_float.c: No longer being #include'd and all interpolation functionality has been re-written as separate functions, interpolating around loops is now supported, effect (reverb/chorus/pan/filter) stuff moved to fluid_voice.c. * src/fluid_phase.h: 64 bit unsigned integers are now used for phase index/fraction sample pointers, modified macros accordingly. * src/fluid_voice.c: Removed SSE code, fluid_voice_init() renamed to fluid_dsp_float_init() and moved to fluid_dsp_float.c. Effect related functionality (reverb/chorus/pan/filter) moved from fluid_dsp_float.c to fluid_voice.c. Some code re-formatting and comment cleanup. Loop no longer requires padding surrounding it (fixes bug related to loop point right on the end of the sample). 2007-08-18 Josh Green * src/fluid_alsa.c: Added SND_SEQ_PORT_TYPE_MIDI_GENERIC back into the ALSA sequencer port registration as it broke the use of playmidi (thanks to Dave Serls for providing a patch and pointing this out). 2007-01-14 Josh Green * src/fluid_alsa.c: Fixed evil bugs in ALSA driver where return value of new fluid_alsa_handle_write_error() was not being checked correctly causing successfully handled ALSA errors (underruns for example) to terminate audio thread. * src/fluid_synth.c: Using an inline roundi function to replace roundf as per suggestion by Mihail Zenkov, 16 bit for dithering. 2006-12-10 Josh Green Lots of documentation updates. * doc/Doxyfile: No longer including functions by default, only those listed in the listed header files. * src/fluid_strtok.[ch]: Removed, since it was crap. Replaced with fluid_strtok() in fluid_sys.c which doesn't require an allocated tokenizing instance. * src/fluid_alsa.c: Audio processing is more optimized in the case where no user defined audio callback is used (removal of unneeded buffer copy), fluid_alsa_handle_write_error() added for centralized ALSA audio error handling, * src/fluid_aufile.c: Now also doing 16 bit dithering. * src/fluid_cmd.c: Removed use of old tokenizer instance. * src/fluid_coreaudio.c: User defined callback function is now honored. * src/fluid_defsfont.c: More leaks plugged (thanks to Paul Millar for the patch), removed sfont_free_data() since sfont_close() should be used instead (don't want to leak a file handle). * src/fluid_midi_router.c: Took out uses of fflush() since sending a line of text (with newline) should display it. * src/fluid_oss.c: Using fluid_synth_dither_s16() in place of old 16 bit conversion code. * src/fluid_settings.c: Replaced strtok stuff with new function, some other improvements. * src/fluid_synth.c (delete_fluid_synth): Turning off all voices so that SoundFont data will be freed correctly (thanks to patch from Paul Millar). * src/fluid_sys.c (fluid_strtok): New function to replace old tokenizing functions which required a token instance. * src/fluidsynth.c: Warning message printed if a non option is not a valid SoundFont or MIDI file (thanks to Nick Daly for the patch). 2006-11-22 Josh Green * src/fluid_alsa.c (new_fluid_alsa_audio_driver2): Removed some ALSA lib calls to set software parameters, which was likely causing the 100% CPU usage problem (not actually fixed in last update, not sure which one is the culprit). (fluid_alsa_seq_run): More changes in ALSA sequencer code, hopefully it is right this time! (delete_fluid_alsa_seq_driver): Memory leak fixed - wasn't freeing array of sequencer file descriptors. * src/fluid_chan.c: Memory leak fixes: Now deleting preset from channel when channel is destroyed. * src/fluid_cmd.c: Memory leak fix: strtok being deleted from command shell when shell is destroyed. * src/fluid_defsfont.c: Memory leak fixes: Freeing modulator lists in preset and instrument zones, freeing zone names, freeing instruments linked from preset zones, replaced use of "safe_malloc" with FLUID_MALLOC macro, deleting instrument list in SFData, deleting samples in SFData, freeing SFData structure. * src/fluid_settings.c: Memory leak fix: freeing options in option type settings. * src/fluid_synth.c: Memory leak fixes: Freeing FX buffers and right/left_buf. 2006-11-21 Josh Green * src/fluid_alsa.c (new_fluid_alsa_audio_driver2): Modified all ALSA calls to check return code error as "< 0" as per ALSA examples, sample rate is now compared with what was expected and warning message displays both values, if target sample rate wasn't set update the local period_size variable (was causing 100% CPU consumption by ALSA, from the resultant erroneous sw_params calls). (fluid_alsa_audio_run_float): Using case statement for error codes from snd_pcm_writen() for the sake of tidiness. (fluid_alsa_audio_run_s16): Using case statement for error codes from snd_pcm_writei() for the sake of tidiness, re-instated call of device callback function that was broken with the dither patch (don't want to break the API), now using new fluid_synth_dither_s16() to convert floating point sample data to 16 bit with dithering. (fluid_alsa_seq_run): Timeout in poll() call set to 100ms (from 1ms!), snd_seq_event_input_pending is used to check if events are available before calling snd_seq_event_input to prevent blocking, check of snd_seq_event_input error code moved to the right location (bug fix). * src/fluid_synth.h: Added dither_index parameter to fluid_synth_t structure to allow for per synth dithering continuity. * src/fluid_synth.c: Modified dithering to use new dither_index field for per synth dithering continuity, fixed off by 1 error with dithering index comparison, removed usage of roundf in dithering (is it sufficient to just integer truncate?). (fluid_synth_dither_s16): New function to perform dithering on buffers of floating point sample data. 2006-11-20 Josh Green * src/fluid_alsa.c: Applied dithering patch from Mihail Zenkov. * src/fluid_synth.c: Applied dithering patch from Mihail Zenkov. 2006-03-04 Josh Green * src/fluid_alsa.c (delete_fluid_alsa_audio_driver): Now calling snd_pcm_close() to close the ALSA audio driver handle. (fluid_alsa_seq_run): Check for -ENOSPC error was logicly inverted. (new_fluid_alsa_seq_driver): Sequencer is now opened in blocking mode. 2006-02-20 Josh Green * Fixed build error that occured when neither LASH or LADCCA are present. * Updated README-OSX from Ebrahim Mayat. 2006-02-18 Josh Green * FluidSynth release 1.0.7 "Increasing Fluidity.." * Removed spurious newlines from FLUID_LOG statements throughout. * AUTHORS: Some cleanup and additions. * src/fluid_lash.[ch]: Moved LADCCA related code from fluidsynth.c here and added new LASH support (both old LADCCA and LASH are supported exclusively). Used patches sent by Frieder Bürzele as a guide. * src/fluidsynth.c: Removed LADCCA code (now in fluid_lash.c), re-organized command line parsing and removed duplicate WIN32 switch statement, re-organized help output and added missing entries, added "-o help" switch for listing settings, welcome message now printed whenever FluidSynth is run and simplified, (print_usage): hard coded application name as "fluidsynth". * configure.ac: Changed --enable-SSE option to --enable-broken-SSE and --enable-SSE now just displays a fat warning about not using it. * src/fluid_jack.c: Warning is now displayed if synth sample rate doesn't match jackd. * src/fluid_alsa.c: Added detection for ALSA sequencer buffer overrun (-ENOSPC) and interrupted poll() call (-1??). * src/fluid_voice.c: Applied patch from Henri Manson which adds a fluid_ct2hz_real() function which does not have the filter cutoff limits that fluid_ct2hz() does, new function being used for calculations that may include non-audible frequencies. * src/fluid_dsound.c: Applied patch from Henri Manson which only creates the directsound window once. 2005-09-04 Josh Green * src/fluid_ramsfont.c (fluid_ramsfont_remove_izone): Applied crash bug fix from Antoine Schmitt. 2005-07-05 Josh Green * src/fluidsynth_priv.h: FLUID_ALIGN16BYTE is broken on AMD64 so now only enabled if SSE is being used. If SSE code becomes more useful in the future this should be fixed. 2005-06-29 Josh Green * Applied LASH patch that is included with ladcca-0.4.0. 2005-06-11 * Released FluidSynth 1.0.6 "Music to my ears" * README-OSX: Update from Ebrahim Mayat. * acinclude.m4: Midishare support now defaults to auto. * configure.ac: Added LT_CURRENT, LT_REVISION and LT_AGE in place of LIBFLUIDSYNTH_MAJ and LIBFLUIDSYNTH_MIN to make better use of libtool library versioning. Fixed use of AC_ARG_ENABLE (was setting variables to yes even when disable was specified), fixes --disable-SSE which was reported by Mikhail Yakshin, added warning when SSE is enabled to let users know that this feature isn't really desirable currently. * src/Makefile.am: Now using LT_VERSION_INFO to substitute the libtool version. * src/fluid_cmd.c (fluid_cmd_handler_handle): Modified to avoid GCC "type-punned" cast warning. * src/fluid_defsfont.c (fluid_preset_zone_import_sfont): Fixed assignment of modulator amtsrc flags (should be assigned to flags2 not flags1), thanks to Stephan Tassart for reporting this. (fluid_inst_zone_import_sfont): Same fixes as for above. * src/fluid_sys.c (fluid_log): Now using vsnprintf for formatting error messages to fix buffer overflow as reported by Axioplase. (fluid_debug): Same as above. 2005-06-11 * fluidsynth.prj: Added Anjuta project file. * src/fluid_conv.c: fluid_cb2amp conversion set back to real centibels and added a new fluid_atten2amp table conversion for non-standard EMU 8k/10k attenuation. * src/fluid_voice.c (fluid_voice_write): Updated volume calculations to use fluid_cb2amp for envelope and LFO, but use fluid_atten2amp for initial attenuation. (fluid_voice_noteoff): Re-coded volenv_val attack conversion and verified. 2005-06-10 * src/fluid_phase.h: Patch from Sean Bolton to fix big endian long long phase combined 64 bit value type fluid_phase_t * src/fluid_voice.c (fluid_voice_update_param): case GEN_OVERRIDEROOTKEY was incorrectly adding pitchadj fine tune amount instead of subtracting it. Also, fine tuning should be applied to root key override as well. 2005-06-07 * Applied Sean Bolton's DSSI patch (SB patch) which adds the ability to change polyphony at runtime and fixes a bug (see below). * README-OSX: Update from Ebrahim Mayat for OSX Panther. * include/fluidsynth/synth.h: Sean Bolton's DSSI patch adds two new functions fluid_synth_set_polyphony and fluid_synth_get_polyphony. * src/fluid_conv.c: Centibel to amplitude conversion now follows EMU 8k/10k which is contrary to SoundFont specification (TiMidity++ used as an example). * src/fluid_conv.h: FLUID_CB_POWER_FACTOR defined for the centibel->amp conversion table equation. * src/fluid_defsfont.c (load_pgen): Fixed 'use of cast expressions as lvalues is deprecated' warning by casting the value being assigned instead of the variable assigned to and removed code warrior specific code to work around this. (load_igen): Same as for load_pgen. * src/fluid_synth.c: SB patch - uses synth->polyphony instead of synth->nvoice when iterating over the synth's voices. (fluid_synth_update_polyphony): SB patch (new) - runtime update (fluid_synth_set_polyphony): SB patch (new) (fluid_synth_get_polyphony): SB patch (new) (fluid_synth_nwrite_float): SB patch - fixes bug where the use of arbitrary values of the 'len' parameter was broken. * src/fluid_voice.c (fluid_voice_write): modlfo_to_vol (modulation LFO to volume) was being calculated inverted (should be negative attenuation, gain, for a positive rise in LFO). (fluid_voice_noteoff): Updated centibel to amplitude conversion used when voice off during attack to use the new FLUID_CB_POWER_FACTOR. 2004-11-11 * README-OSX: Update from Ebrahim Mayat. 2004-08-18 * src/fluid_synth.c (fluid_synth_set_bank_offset): (fluid_synth_get_bank_offset): New API to set a bank offset in a SoundFont (proposition made by Ken Ellinwood). 2004-08-06 * src/fluid_synth.c (fluid_synth_noteon): fluid_synth_release_voice_on_same_note() is now called in the noteon() function instead of in fluid_synth_start(). This fixes the silent note problem! 2004-07-29 * src/fluid_chan.c (fluid_channel_cc): Applied Ken Ellinwood's fix for the bank select (MSB) message. * src/fluid_jack.c (fluid_jack_audio_driver_settings): Applied Rui Nuno Capela's patch 2004-05-14 * doc/fluidsynth.1 (option): Fixed typo noted by Gerald Pye. 2004-05-14 Peter Hanappe * src/fluid_dsound.c (fluid_dsound_enum_callback): Applied Sergey Pavlishin's patch. This path fix stack overflow during DirectSound audio driver initialization. 2004-05-07 Peter Hanappe * src/fluid_synth.c (fluid_synth_remove_sfont): Added new function 2004-05-05 Peter Hanappe * src/fluid_alsa.c (new_fluid_alsa_seq_driver): The alsa driver now opens several ports if the synthesizer is configured for more than 16 MIDI channels. * src/fluid_voice.c (fluid_voice_write): I removed the filter on/off optimization. The filter is always on and serves as an anti-aliasing filter. 2004-05-04 Peter Hanappe * src/fluid_synth.c (new_fluid_synth): The number of MIDI channels now has to be a multiple of 16. The synth checks that this is the case and changes the settings accordingly. I removed the sanity checks for the min/max value of the number of MIDI channels since this is already done by the settings object. 2004-03-30 Josh Green * src/fluid_voice.c (fluid_voice_write): Altered filter turn-off optimization to not turn filter off once it has been enabled. There is still a potential for a click when it gets turned on though, which needs to be dealt with. 2004-03-30 Peter Hanappe * src/fluid_dsp_core.c: I've split up the dsp core file in three files: fluid_dsp_simple.c, fluid_dsp_float.c, and fluid_dsp_sse.c. This improves the readability. 2004-03-29 Peter Hanappe * src/fluid_jack.c (new_fluid_jack_audio_driver2): Testing the number of ports before allocating them. (fluid_jack_audio_driver_settings): Registering the "audio.jack.autoconnect" setting. * src/fluid_midi.c (fluid_player_set_midi_tempo): Tempo changes handled correctly. Was broken after fix on [2004-03-22] (see below). * src/fluid_strtok.c (fluid_strtok_char_index): Removed printf's from fluid_strtok.c 2004-03-26 Peter Hanappe * bindings/README: Imported the fluidsynth_jni and fluidmax projects. 2004-03-25 Peter Hanappe * src/fluid_rev.c (new_fluid_revmodel): Added 'gain', similar as in Freeverb 3. Using same 'wetscale' as Freeverb 3, but fixing 'wet' to 3. fluid_revmodel_setlevel() does not change the value of 'wet': The 'wet' level can be controlled with the 'reverb send'. (fluid_revmodel_processreplace): The input is multiplied by 2 and by the gain. This corresponds to the channel mixing and scaling that Freeverb 3 does. 2004-03-24 Peter Hanappe * src/fluidsynth.c (main): Added the -f switch. Passing "-f file" on the command line tells fluidsynth to read parse the file and execute and commands. (main): User config and system config file are now loaded correctly * src/fluid_cmd.c (fluid_shell_run): the shell doesn't get stuck and loop on an emtpy string when the end of the stream is reached. * src/fluid_io.c (fluid_istream_gets): fluid_istream_gets() returns 0 if the end of the stream was reached and -1 on error. * src/fluid_cmd.c (fluid_source): Fixed bug in "file = open(filename, FLAGS);" (I shouldn't pass O_WRONLY when what I want is O_RDONLY!) 2004-03-23 Peter Hanappe * src/fluid_aufile.c (new_fluid_file_audio_driver): Added fluid_aufile.c. This file implements a audio driver that writes the audio output to a file. This driver is NOT real-time and is currently useful for testing purposes only (not even useful to play MIDI files). 2004-03-22 Peter Hanappe * src/fluid_synth.c (new_fluid_synth): Removed the synth->busy mutex. I don't think it is necessary; to be discussed. * src/fluid_midi.c (fluid_player_callback): Fixed the timing in the MIDI playback. The current MIDI tick in every timer callback was calculated as an increment to the previous number of ticks. This introduces a growing error due to rounding errors and timer variations. The current tick is now calculated according to the absolute time at the beginning of the file. (Beginners error ...) * doc/FluidSynth-LADSPA.pdf: Added Markus' LADSPA design document. * doc/xtrafluid.txt: Added Antoine's Xtra API documentation. * doc/midi_time.txt: Added a memo on midi timing. 2004-03-19 Peter Hanappe * src/fluid_midishare.c: Applied Stephane Letz patch: MidiShare is now connected to fluidsynth by default so that received MIDI events directly trigger the synth 2004-02-28 Peter Hanappe * src/fluid_synth.c: Added fluid_synth_program_select2() and fluid_synth_get_sfont_by_name() in fluid_synth.c. These functions are not in the public API, yet. 2004-02-25 Peter Hanappe * src/fluid_voice.c: Fixed bug in volume envelope (in fluid_voice_update_param(), case GEN_VOLENVDECAY): the minimum value was converted to linear amplitude instead of a normalized value of the cB (1-cB/1000). Because of that, the decay section went on for too long. 2004-12-xx Peter Hanappe * src/fluid_seq.c: Inserting events in the queueLater list was incomplete. It didn't check if the event was the last in the list, and the looping through the list didn't update the prev pointer. I added muteces to the sequencer. Events are dynamically allocated if no free events are available. The sequencer is protected by a mutex. 2003-11-14 Josh Green * src/fluidsynth.c: Removed CCA_Use_Jack and CCA_Use_Alsa flags since LADCCA no longer uses them. 2003-08-31 Josh Green * acinclude.m4: Renamed AC_SOUND macro to AC_OSS_AUDIO and removed the ALSA check from it since pkg-config is now being used to check for ALSA. Also fixed --enable-alsa-support and --enable-oss-support which were disabling support instead (reported by Bart Massey). * configure.ac: pkg-config is now being used to check for ALSA. ALSA and OSS now use automake conditionals to conditionally compile source files. * Makefile.am: Re-arranged SUBDIRS so build output looks nicer. * src/Makefile.am: ALSA and OSS are now conditionally compiled using automake conditionals. 2003-08-29 Josh Green * src/fluid_sys.c: Patch from Eric Van Buggenhaut to make i386 asm code not compile for all non-i386 archs rather than just DARWIN. * src/fluidsynth_priv.h: Patch from Sergey Pavlishin to fix FLUID_REALLOC macro. * src/fluid_cmd.c: Ken Ellinwood's patch to add -verbose to "channels" command, and print settings values with 3 decimal places. * src/fluid_defsfont.c (fluid_defsfont_sfont_get_preset): Ken Ellinwood's patch to initialize sfont field of preset. * src/fluid_ramsfont.c (fluid_ramsfont_sfont_get_preset): Ken Ellinwood's patch to initialize sfont field of preset. * src/fluid_midi.c (fluid_midi_file_read_event): Fixed a crash bug with zero length MIDI meta events that was pointed out by Sergey Pavlishin. (delete_fluid_midi_event): Fixed a stack overflow problem pointed out by Sergey Pavlishin that was caused by recursively deleting MIDI event linked list, now just using a while loop. 2003-08-25 Josh Green * src/fluidsynth.c: MIDI channels switch should be -K not -L as was listed in "Usage" output, also -K was setting audio.channels for non getopt case statement - changed to midi.channels. Added a new option "-l, --disable-ladcca" to disable LADCCA server connection. 2003-08-25 Josh Green Release version 1.0.3 * doc/fluidsynth.1: Applied typo patch from Eric Van Buggenhaut. * TODO: Restructuring TODO file (removing old stuff). * doc/Doxyfile: Disabled Tex doxygen generation and changed OUTPUT_DIRECTORY to api/. * doc/Makefile.am: Added an update-docs target and related for updating developer doc and doxygen reference HTML. Also added update-docs to dist-hook for updating before distribution packaging. * include/fluidsynth/synth.h: Some fixes to doxygen documentation. * fluidsynth.spec.in: New RPM spec file which is generated at configure time. * Makefile.am: Added fluidsynth.spec(.in) to EXTRA_DIST. 2003-08-19 Josh Green * src/fluid_alsa.c: Added some calls to snd_strerror() to print out details of ALSA routine failures. * src/fluid_defsfont.c: Put a message about SoundFont loading code being borrowed from Smurf SoundFont Editor. * src/fluid_rev.c: Valgrind found that some values were being used uninitialized because fluid_revmodel_update() was being called before all reverb parameters were set, now setting manually and then calling update routine. * src/fluid_voice.c: Increased FLUID_MAX_AUDIBLE_FILTER_FC to minimize clicks from filter toggling. Added a FLUID_MIN_VOLENVRELEASE constant to set the minimum volume envelope release to minimize clicks. 2003-07-22 Josh Green * src/fluid_midishare.c: Added include of header "config.h" as per Albert Graef's request. * src/fluid_voice.c (fluid_voice_optimize_sample): Moved a variable declaration to the beginning of function, it was causing problems with at least one user. 2003-06-28 Josh Green * src/fluid_defsfont.c: Moved call to fluid_voice_optimize_sample from fluid_inst_zone_import_sfont to fluid_defsfont_load. Also reduced minimum sample size before rejection from 48 to 8 (could be lower?). * src/fluid_voice.c (fluid_voice_optimize_sample): Added a check for sample->valid to ignore ROM samples which was causing a crash with Vintage Dreams and other SoundFont files with ROM samples. 2003-06-17 Josh Green Release version 1.0.2 Added Makefile.am files where lacking. * Makefile.am: Fixes to "make dist" target by adding macbuild, sf2 and winbuild to SUBDIRS also removed acconfig.h from EXTRA_DIST. * acinclude.m4: Removed AC_JACK, now using pkgconfig. * configure.ac: Updated to version 1.0.2, Jack test now using pkgconfig and built by default if found, coreaudio driver now built by default if found. * doc/Makefile.am: Added Doxyfile, example.c, example.sf2, fluidsynth.1 and fluidsynth-v10-devdoc.xml to EXTRA_DIST. * src/Makefile.am: fluid_jack.c now conditionally built, fluid_sse.h added to EXTRA_DIST. * src/fluid_jack.c: #if JACK_SUPPORT removed as its not needed. 2003-06-15 Josh Green * configure.ac: Fixed detection of CoreAudio by looking for CoreAudio/AudioHardware.h. * src/Makefile.am: Added COREAUDIO_CFLAGS and COREAUDIO_LIBS. * src/fluid_coreaudio.c: Added CoreAudio prefix to #include headers (fluid_core_audio_callback): Fixed declarition to match that of the typedef in CoreAudio header to stop warnings. * fluidsynth.c: Now including fluidsynth_priv.h to include the arch specific definitions in there (perhaps should be done in configure script though). * fluidsynth_priv.h: Added "#define WITHOUT_SERVER 1" to Darwin build. 2003-06-12 Josh Green * Makefile.am: Added autogen.sh to EXTRA_DIST * acinclude.m4: Added AM_PATH_READLINE macro for readline detection and prefix configuration. * configure.ac: Support for MinGW32 build, Darwin build fixes, configure CFLAGS input value now honored, fixes to CoreAudio support, and better readline detection and config. * src/Makefile.am: Now conditionally compiling CoreAudio and Windows sources, added config_*.h files to EXTRA_DIST, some stuff for MinGW32 build, READLINE_LIBS and READLINE_CFLAGS now used. * src/fluid_dsound.c: Fixed some warnings by adding "void" for empty parameter procedure declarations. * src/fluidsynth.c: Don't include config_win32.h if MinGW32. * src/fluidsynth_priv.h: Stuff for MinGW32 and Darwin builds. * doc/fluidsynth-v10-devdoc.xml: Applied a diff from Alexandre Prokoudine. 2003-06-09 Josh Green * src/fluid_alsa.c: Added calls to pthread_attr_setschedparam to properly create SCHED_FIFO threads. * src/fluid_oss.c: pthread_attr_setschedparam calls added. * src/fluid_midishare.c: Patch update from Stephane Letz. 2003-05-29 root * src/fluid_synth.c (fluid_synth_one_block): Added a mutex that provides a small degree of protection against noteons / noteoffs, when the audio thread is working. * src/fluid_synth.h (struct _fluid_synth_t): * src/fluid_voice.c (fluid_voice_optimize_sample): 2003-05-29 Markus Nentwig * include/fluidsynth/voice.h: added fluid_voice_gen_incr to api * src/fluidsynth.c: Added error message for command line parameter handling * src/fluid_voice.c (fluid_voice_optimize_sample): Removed loop peak detection at run time, because it caused dropouts. Now the sound font loader or application is responsible to call fluid_voice_optimize_sample (if it doesn't, the turnoff optimization is simply disabled). 1999-11-30 Antoine Schmitt * src/fluid_defsfont.c: inst_zone lokey is now properly inialized to 0 (it was not, leading to random lost noteons depending on memory initialization) 2003-04-03 Peter Hanappe * src/fluid_rev.c: reverb parameters are clipped to their valid range. * src/fluid_alsa.c: using fluid_alsa_audio_run_s16 as default function. This reduces the high CPU usage. * src/fluid_voice.c (fluid_voice_write): filter interpolation done over only 1 buffer to avoid filter instability * src/fluid_chan.c (fluid_channel_init): bank number set to 128 for the drum channel * src/fluid_midi.c (fluid_midi_file_read_event): Correctly reading pitchbend value 2003-02-27 Josh Green Updated automake files (automake 1.6). * configure.ac: New version autoconf variables which get substituted into include/iiwusynth/version.h.in. * include/iiwusynth/version.h.in: Version defines that are filled in by autoconf. * src/Makefile.am: Fixed SOURCES including removing headers that are now in include/iiwusynth/, added missing sources (iiwu_ramsfont.[ch], iiwu_sfont.h) and added iiwu_dsp_core.c to EXTRA_DIST. * doc/Makefile.am: Added iiwusynth.1 to EXTRA_DIST. * include/iiwusynth.h: Added version.h. * iiwusynth/Makefile.am: Added version.h to the installed headers. 2003-02-08 Markus Nentwig * src/iiwu_ladspa.c: Added a very small signal at Nyquist frequency. This fixes denormal number problems in some plugins. * src/iiwu_cmd.c (iiwu_shell_run): Now also invalid input lines are added to the command line history. So the user can just scroll up and fix them. * src/iiwu_ladspa.c: Cleaned up error messages * src/iiwu_dsp_core.c: Disabled SSE interpolation, because it is slower than the normal code * autogen.sh: Added a line, that checks for the presence of pkg-config in autogen.sh. Motivation: It took me some time to figure out what was wrong... It produces some error message instead of an obscure error later during ./configure, if pkg-config is not installed. 2003-02-07 Josh Green Applied another Bob Ham LADCCA patch. * src/iiwu_alsa.c: LADCCA patch: Now using a ladcca.enable setting. * src/iiwu_jack.c: LADCCA patch: ladcca.enable setting and jack ports are no longer auto connected unless audio.jack.autoconnect is set. * src/iiwusynth.c: LADCCA patch: ladcca.enable and command line options -j and --connect-jack-outputs to enable Jack autoconnect. 2003-02-05 Josh Green Applied Bob Ham's LADCCA and pkgconfig patches. * Makefile.am: pkgconfig patch. * configure.ac: Renamed from configure.in as per new autoconf standards. LADCCA configure switch and detection. FluidSynth.pc pkgconfig file output. * src/Makefile.am: LADCCA patch. * src/iiwu_alsa.c [HAVE_LADCCA]: LADCCA patch: reports ALSA sequencer client ID. * src/iiwu_jack.c [HAVE_LADCCA]: LADCCA patch: reports JACK client name. * src/iiwusynth.c [HAVE_LADCCA]: LADCCA patch: connects to LADCCA server, creates client thread, saves/restores SoundFont file state. Used iiwu_sfont_get_name macro to get SoundFont file names contrary to the patch. Should these macros be public? Included unistd.h for usleep call (within HAVE_LADCCA). 2003-01-23 Josh Green * src/iiwu_jack.c: Fixed a segfault bug caused by freeing jack port names, when really only the port array should be freed, jack reference docs are confusing on this matter! * src/iiwu_voice.c (iiwu_voice_check_sample_sanity): Min loop size and padding now set via constants IIWU_MIN_LOOP_SIZE and IIWU_MIN_LOOP_PAD defined at top of iiwu_voice.c, and the values were lowered to exceed SF spec requirements rather then just meet. (iiwu_voice_write): Now using a constant IIWU_MAX_AUDIBLE_FILTER_FC defined at the top of iiwu_voice.c to control the filter cutoff optimization. Also added IIWU_MIN_AUDIBLE_FILTER_Q so filter will only turn off if both cutoff and q are determined to be inaudible. Filter optimization is much less noticeable when modulating. 2003-01-14 Markus Nentwig * src/iiwu_ladspa.c: Adapted new command handler * src/iiwu_midi_router.c (midi_dump_prerouter): Added forgotten 'flush' for event dump 'fprintf's 2003-01-01 Markus Nentwig * src/iiwu_oss.c (new_iiwu_oss_audio_driver): Changed to callback function * src/iiwu_alsa.c (new_iiwu_alsa_midi_driver): Changed to callback function * src/iiwu_midishare.c (iiwu_midishare_midi_driver_receive): Partly done the same * src/iiwu_winmidi.c (new_iiwu_winmidi_driver): To be done... * src/iiwu_midi_router.c: Added * src/iiwu_ladspa.c: Adapted to new settings system * src/iiwu_adriver.c (iiwu_audio_driver_settings): Uses getint instead of getnum for audio.period-size and audio.periods settings. * src/iiwu_voice.c (iiwu_voice_write): Ignore the valid flag for samples. Otherwise no sound is produced. * src/iiwu_chan.c (iiwu_channel_cc): Fixed bank select (7-bit instead of 8 bit) 2002-12-23 Peter Hanappe * src/iiwu_io.c (iiwu_istream_readline): new file (iiwu_io.c and iiwu_io.c) to handle IO in the shell. * src/iiwusynth.c (main): options to start TCP server. * src/iiwu_cmd.c (new_iiwu_server): New structure and functions (new_iiwu_shell): New structure and functions to improve command interface. (new_iiwu_cmd_handler): New structure and functions to improve command interface. * src/iiwu_sys.c (new_iiwu_server_socket): New structure and functions (new_iiwu_thread): New structure and functions 2002-12-14 Peter Hanappe * src/iiwu_chan.c (iiwu_channel_cc): Handling NRPN messages (NRPN system). * src/iiwu_voice.c (iiwu_voice_update_param): Does more extensive range checking because the NPRN system may produce out-of-range values (NRPN system). (iiwu_voice_set_param): New function to change generator values (NRPN system). * src/iiwusynth_priv.h (iiwu_clip): New macro * src/iiwu_synth.c (iiwu_synth_set_gen): New function to change generator values (NRPN system). * src/iiwu_gen.c (iiwu_gen_map_nrpn): New function to map the NRPN data input to the parameter range (NRPN system). * src/iiwu_midi.c (iiwu_midi_file_read_event): Fixed metadata buffer bug (alloc size 1 too small). 2002-12-10 Peter Hanappe * src/iiwu_dsound.c (iiwu_win32_destroy_window): Filled in the empty lines... * src/iiwusynth.h: Changes in the definition for iiwu_synth_sfload and iiwu_synth_sfunload, New functions: iiwu_synth_sfreload, iiwu_synth_get_sfont_by_id, and iiwu_list_insert_at. New 'id' field in iiwu_font_t. 2002-12-08 Markus Nentwig * src/Makefile.am: added iiwu_hash.c and iiwu_strtok.c to libiiwusynth_la_SOURCES * src/iiwu_settings.c (iiwu_settings_init): Removed multi_channel from the settings (replaced with audio_channels > 1) * src/iiwu_settings.c (iiwu_settings_init): added audio_groups setting. This is the number of individual channels generated from the synth, and always equal to audio_channels, as long as the LADSPA Fx unit is disabled. Otherwise it can be used (for example) to separate even and odd MIDI channels, apply different Fx and mix together to one stereo output. src/iiwu_ladspa.c: Extended Fx unit to multigroup input, fx sends and multiple audio output channels 2002-12-04 Peter Hanappe * src/iiwu_midi.c (iiwu_midi_file_read_event): the metadata buffer is now dynamically allocated. What! Dynamic memory management already existed in the sixties! * src/iiwu_cmd.c (iiwu_handle_reset): New shell command. Sends system reset. * src/iiwu_cmd.c (iiwu_expand_path): New function to handle filenames starting with '~'. * src/iiwu_cmd.c: Added commands for working with tunings. Added 'source' command. * src/iiwu_chan.h (struct _iiwu_channel_t): added tuning * src/iiwusynth.h: new tuning functions * src/iiwu_synth.c (iiwu_synth_reset_tuning): new tuning functions * src/iiwu_voice.c: Added tuning 2002-12-03 Peter Hanappe * doc/iiwusynth.1: new man page * src/iiwu_midi.c (iiwu_player_load): the player now handles a playlist. * src/iiwusynth.h: 'iiwu_player_add' replaces 'iiwu_player_load' * src/iiwusynth.c (main): iiwusynth can now play midifiles. 2002-12-02 Peter Hanappe * src/iiwu_sys.c (new_iiwu_timer): New argument 'auto_destroy' to specify whether the timer should delete it's structure when the timer is finished. * src/iiwu_synth.c (iiwu_synth_sfunload): If the soundfont can not be unloaded immediately, a timer thread is spinned of to unload it later. On MacOS 9, the unload is tried at a subsequent 'load' or 'unload' request. * src/iiwusynth.h (struct _iiwu_sample_t): Added 'refcount' field to test when a soundfont can be unloaded. * src/iiwu_synth.c (iiwu_synth_nwrite_float): New function allowing multi-channel audio output. (iiwu_synth_init): Fixed 'amount' for pan. Now set to 500. * src/iiwu_cmd.c (iiwu_synth_cmdshell): Added little prompt. * src/iiwusynth.c (print_welcome): iiwusynth prints out a welcome message as an well-behaved, interactive application should. * src/iiwu_synth.c (iiwu_synth_all_sounds_off): New function to implement the 'All Sound Off' MIDI messages (CC 120). (iiwu_synth_system_reset): This function now also resets the default controller values on the MIDI channels, and clears the reverb and chorus delay lines. (iiwu_synth_count_midi_channels): New function to retreive the number of available midi channels. (iiwu_synth_count_audio_channels): New function to retreive the number of available midi channels. (iiwu_synth_count_effects_channels): New function to retreive the number of available effects channels. (iiwu_synth_get_cpu_load): New function to retreive an estimation of the CPU load. * src/iiwusynth.h: Added fields to handle multi-channel audio and a variable number of midi-channels. The 'flags' has been expanded/replaced with several variables. * src/iiwu_chan.c (iiwu_channel_cc): Implemented the 'All Control Off' MIDI message (CC 121). * src/iiwu_chorus.c (iiwu_chorus_update): iiwu_chorus_update (called after the iiwu_chorus_set_xxx function) no longer returns an error of out-of-range values. It clips the value the the [min-max] range. 2002-11-22 Markus Nentwig * src/iiwu_voice.c (iiwu_voice_write): Fixed compilation problem without --enable-SSE (Pentium II and Mac) 2002-11-17 Markus Nentwig * src/iiwu_voice.c (iiwu_voice_write): Fixed nonlooped samples-bug. * TODO (TODO): Updated * src/iiwu_cmd.c (iiwu_handle_reverbsetlevel): Changed command line command 'rev_setwet' to 'rev_setlevel'. Replaced the word 'wet' by 'level' in most places. Added a command line option --dump, which provides 'machine-readable' output from stdout to hook up a user interface. * src/iiwusynth.h: Moved the default values for gain, chorus and reverb here. Might be useful as an example... * src/iiwu_voice.c (iiwu_voice_calculate_runtime_synthesis_parameters): Added 'scale tuning' modulator, centered around C3. * src/iiwusynth.h: Added API functions to read the reverb state Moved iiwu_synth_system_reset to the API 2002-11-08 Markus Nentwig * src/iiwu_voice.c (iiwu_voice_write): Fixed Volume envelope delay bug * src/iiwu_voice.c (FILTER_TRANSITION_SAMPLES): Doubled filter fading time * src/iiwu_mod.c (iiwu_mod_get_value): Changed convex unipolar negative definition * src/iiwu_voice.c (iiwu_voice_off): Cleaned up a bit, uses now calls to iiwu_voice_off, when a voice is finished. * src/iiwu_midi.c (iiwu_midi_parser_parse): Reimplemented New parser should be able to cope with realtime, system common and resynchronize. 2002-10-31 Markus Nentwig * src/iiwu_alsa.c (iiwu_alsa_midi_run): Increased MIDI timeout from 1 to 100 ms * src/iiwu_dsp_core.c: Merged identical filter coefficients b0 and b2 into b02 Implemented smooth filter transitions * src/iiwu_sys.c (iiwu_check_fpe): Added verbose FPE reporting and systematic FPE checks. * src/iiwu_rev.c: Added a constant DC offset to avoid slowdown caused by denormal numbers * src/iiwu_synth.c (delete_iiwu_synth): Fixed segv during shutdown * src/iiwu_dsp_core.c: Fixed buffer bug (aligned-unaligned) * src/iiwu_synth.c (iiwu_synth_damp_voices): Commented out unused code * src/iiwu_dsp_core.c: Optimized, added SSE code, which is this time actually faster than the default code. Well. Part of it. * src/iiwu_voice.c: Minor clean-up * configure.in: Added switch --enable-longlong * configure.in: Added switch --enable-SSE * src/iiwu_phase.h: Added 64 bit operations, documented * src/iiwu_sse.h: Check to avoid #including the file more than once 2002-10-29 Markus Nentwig * src/iiwu_voice.c: Added experimental SSE support for Pentium III. Comment out #define SSE from iiwu_voice.c to get back to the standard version. 2002-10-26 Markus Nentwig * src/iiwu_seq.c: Fixed a couple of warnings * src/iiwu_voice.c (new_iiwu_voice): Removed iiwu_voice_init. * src/iiwu_dsp_core.c: New 7th order interpolation. 2002-10-24 Markus Nentwig * src/iiwu_voice.c(iiwu_voice_determine_amplitude_that_reaches_noise_floor_for_sample): Added checking for invalid sample. * src/iiwu_voice.c (iiwu_voice_write): Moved the DSP core functions into iiwu_dsp_core.c. Optimized, cleaned up, documented. Amplitude scaling short => floating point is now done as the last operation in the DSP loop (voice->amp does not include the scaling factor anymore). * src/iiwu_synth.c (iiwu_synth_one_block): Saved a couple of multiplications per sample by moving the master gain into iiwu_voice_write * src/iiwu_synth.c (iiwu_synth_free_voice_by_kill): Modified the algorithm * src/iiwu_synth.c (iiwu_synth_alloc_voice): Noteon algorithm will now turn off retriggered running voices ('sustain pedal problem') 2002-10-18 Markus Nentwig * src/iiwu_alsa.c (new_iiwu_alsa_midi_driver): Disabled high-priority scheduling for the MIDI thread to get rid of audio dropouts. * src/iiwu_synth.c (iiwu_synth_free_voice_by_kill): Modified voice killing algorithm, so that recently started voices are not killed * src/iiwu_voice.c (iiwu_voice_run_dsp): Changed some variable names. Extensive loop point checking, when loop points are modulated. * src/iiwusynth.h: Added functions to read the state of the chorus. * src/iiwu_chorus.c: Rewrote chorus setup logic (if a parameter is out-of-range, all other parameter changes are discarded). * src/iiwu_voice.c: Added caching for loop peak detection: The amplitude of the loop is only detected once for each sample. Exception only, if the resulting loop differs from the original loop settings of the sample (in this case, the peak detection is still run for each noteon event). * src/iiwusynth.h (struct _iiwu_sample_t): Added 'amplitude_that_reaches_noise_floor_is_valid' and 'amplitude_that_reaches_noise_floor' * src/iiwu_voice.c(iiwu_voice_calculate_runtime_synthesis_parameters): Renamed 'iiwu_voice_optimize' 2002-07-21 Peter Hanappe * src/Makefile.am (libiiwusynth_la_SOURCES): Followed Bob Ham's suggestion for the Makefile.am to fix the problems with automake 1.6 1999-11-30 Tim Goetze * src/iiwu_synth.c (iiwu_synth_alloc_voice): New algorithm for voice allocation, when all voice processes are in use 1999-11-30 Markus Nentwig * src/iiwu_synth.c (iiwu_synth_alloc_voice): Applied above patch, 2002-07-08 Markus Nentwig * src/iiwu_synth.c (iiwu_synth_noteoff): Changed noteoff strategy: Noteoff now turns off all voice processes with the same channel / key, regardless of the voice ID (avoids stuck notes). 2002-07-13 Peter Hanappe * src/Makefile.am (EXTRA_libiiwusynth_la_SOURCES): Applied Takashi Iwai's patch. The configure stuff in iiwusynth-0.2 cannot be rebuilt with the latest automake 1.6. You cannot use substitution for *_SOURCES in Makefile.am. This fixes this problem. 1999-11-30 Markus Nentwig * src/iiwusynth.h: Added documentation, removed GEN_CHANGED (it was unused). * src/iiwu_mod.c (iiwu_dump_modulator): Cleaned up * src/iiwu_cmd.c (iiwu_handle_help): Restructured command line help system 2002-06-14 Markus Nentwig * src/iiwu_chorus.c (iiwu_chorus_processmix): Turning off chorus now, when parameters are wrong (avoid FPE) * src/iiwu_voice.c (iiwu_voice_write): Optimized turnoff condition for voice 2002-06-11 Markus Nentwig * src/iiwu_voice.c (iiwu_voice_add_mod): Fixed bug that prevented non-default modulators from being added. (iiwu_voice_config): Added peak detection for the sample loop, and a condition turning off the voice, if loop peak volume and amplitude envelope combined fall below the noise floor. 2002-06-06 Peter Hanappe * acinclude.m4: Fixed problems with enable/disable jack and midishare 2002-06-06 Tim Goetze * src/iiwu_synth.c (iiwu_synth_all_notes_off): Added handling of all-notes-off midi message 2002-06-03 Markus Nentwig * src/iiwu_chorus.c: Fixed bug in initial phase calculation 2002-06-02 Peter Hanappe * src/iiwu_jack.c: updated for new JACK types. 2002-06-02 Bob Ham * acinclude.m4: Changed acinclude.m4 for configure to ignore jack. 2002-06-02 Markus Nentwig * autogen.sh: Added libtoolize -f to prevent error message 'libtool: ltconfig version does not match ltmain.sh version ...' * src/iiwusynth.h: Changed iiwu_voice_add_mod_t to iiwu_voice_add_mod * src/iiwu_synth.c: Added NULL termination to list returned by iiwu_synth_get_voicelist * src/iiwusynth.h: Added iiwu_synth_set_chorus (API function) * src/iiwu_synth.c: Added iiwu_synth_set_(reverb|chorus)_on (API functions) * src/iiwu_cmd.c: Added control commands for chorus (see help) 2002-05-26 Tim Goetze * src/iiwu_voice.c (iiwu_voice_noteoff): Fixed conversion between volenv-values from attack segment to later envelope segments 2002-05-22 Markus Nentwig * src/iiwu_voice.c (iiwu_voice_query_ID): Added, API function (iiwu_voice_query_playing): Added, API function (iiwu_voice_write): Fixed problem with filter caused 05-18 * src/iiwusynth.h: Moved iiwu_voice_update_param into the API 2002-05-19 Markus Nentwig * src/iiwusynth.h (iiwu_synth_get_voicelist): Added. * src/iiwu_voice.c (iiwu_voice_noteoff): Added a conversion for linear to cB amplitude, when a note is turned off during the attack phase of the volume envelope * src/iiwu_gen.h: Moved the generator definition to API. Changed the fields to 'double'. * src/iiwu_mod.c: Moved the modulator definitions to API. Changed the data type of amount to 'double'. * src/iiwu_voice.c (iiwu_voice_write): The condition, that quits a voice, when the amplitude falls below a threshold now uses only the volume envelope instead of the voice amplitude. Previously, turning a volume pedal briefly to 0 would quit all voices playing. * src/iiwu_rev.c (iiwu_revmodel_processreplace): Removed 'dry' path from reverb unit Motivation: This saves a couple of multiplications, the dry signal goes through the ordinary output anyway. * src/iiwusynth.h (iiwu_synth_kill_by_exclusive_class): added to API * src/iiwu_synth.c (iiwu_synth_kill_by_exclusive_class): Extended the exclusive class function to work with stereo samples (iiwu_synth_set_reverb): Renamed iwu_synth_set_reverb to iiwu_synth_set_reverb_preset iiwu_synth_set_reverb is now an API function, that allows to set all reverb parameters. 2002-05-18 Markus Nentwig * src/iiwu_chorus.c: Implemented variable delay line with bandlimited interpolation. Documentation, error handling. Removed unneeded and broken features * src/iiwusynth_priv.h: Moved typedef struct iiwu_mod_t iiwu_mod_t into iiwusynth.h * src/iiwusynth.h: Moved iiwu_voice_add_mod from iiwu_voice.h into iiwusynth.h (now API function). * src/iiwu_voice.c (iiwu_voice_update_param): Inserted chorus send into DSP loop (iiwu_voice_write): Added flag 'voice->update_filter'. Now Q can be modulated. 2002-05-12 Markus Nentwig * src/iiwu_synth.c (iiwu_synth_pitch_wheel_sens): added * src/iiwu_chan.c (iiwu_channel_pitch_wheel_sens): added * src/iiwu_cmd.c (iiwu_handle_reverbsetwidth): changed 'wet' to * 'width' 2002-05-11 Markus Nentwig * src/iiwu_conv.c (iiwu_tc2sec): Added more conversion functions with range check for different ranges: (iiwu_tc2sec_attack): (iiwu_tc2sec_hold): (iiwu_tc2sec_release): * src/iiwu_voice.c (iiwu_voice_add_mod): implemented modulator src 0 (constant mod offset) * src/iiwu_voice.c (iiwu_voice_update_param): sample-and envelope related voice parameters are now handled together with other voice parameters. Implemented generators: GEN_KEYTOVOLENVDECAY GEN_KEYTOVOLENVHOLD GEN_KEYTOMODENVDECAY GEN_KEYTOMODENVHOLD 2002-05-10 Peter Hanappe * src/iiwu_synth.c (iiwu_synth_start_voice): added iiwu_synth_start_voice() to handle exclusive classes. 2002-05-09 Peter Hanappe * src/iiwu_conv.h: removed velocity to cB conversion. No longer used. * src/iiwu_synth.c (iiwu_synth_write_float): removed limiter * src/iiwu_synth.h (IIWU_NUM_CHANNELS): set the number of channels to 64. * src/iiwu_synth.c (iiwu_synth_get_internal_bufsize): added * src/iiwu_ladspa.h: lower-cased ladspa files * src/iiwusynth.h: prefixed log levels with IIWU_... Updated all references. * src/iiwu_cmd.c (iiwu_handle_reverb): renamed 'rev_enable' to 'reverb' in correspondance with the long command line arguments * src/iiwusynth.c (main): checking if files on command line are valid * src/iiwuplay.c (main): checking if files on command line are valid * src/iiwusynth.h: New log level for verbose messages: IIWU_INFO 2002-04-30 Markus Nentwig * src: Added iiwu_LADSPA.c, iiwu_LADSPA.h (support for LADSPA effect plugins). * src/iiwusynth.c (main): Changed default gain to 0.2. * src/iiwu_voice.c: Restructured the voice initialization as follows: (iiwu_voice_init): sample position, IIR filter history, envelopes etc. are reset. (iiwu_voice_optimize): The generators (nominal value) have been set by the sound font. Now each modulator is calculated once to obtain the 'final' initial value for each generator, which consists of nominal value and modulator-contributed part. (iiwu_voice_update_param): Calculates all voice parameters, which depend on one particular generator. This is called once for each voice parameter during voice_optimize and further each time, when a modulator changes a generator. (iiwu_voice_update_param): Added a voice parameter filter_gain to avoid recalculating the filter gain each time the center frequency changes (it depends only on Q) (iiwu_voice_write): Voice is now turned off, when the amplitude falls below -100 dB, even during the sustain phase (happens, when holding a piano key for a very long time) * src/iiwu_voice.c (iiwu_voice_noteoff): Moved voice->chan = NO_CHANNEL into iiwu_voice_off. Previously a released note was not modulated anymore, for example pitch bend stopped working as soon as the key was released. * src/iiwu_voice.h: Changed _ON macro to figure out the state of a key from the position in the envelope, instead of using a cleared channel number as indicator. * src/iiwu_synth.c: Implemented all default modulators Added LADSPA support. Added digital clipping. Moved master gain factor ahead of LADSPA Fx. * src/iiwu_mod.c: 'Hardcoded' GM default modulator vel => filter. Replaced 128 with 127 in (127-x) * src/iiwu_midi.c: Fixed sysex for realtime MIDI. Fixed pitch bend bug. * src/iiwu_gen.c (iiwu_gen_set_default_values): Using float instead of int for default values. Added references to specifications (doc). Changed 'init array' function name to 'set_default' . * src/iiwu_defsfont.c (iiwu_preset_zone_import_sfont): Import of modulators (iiwu_inst_zone_import_sfont): Import of modulators (iiwu_defpreset_noteon): Added modulators, fixed generator problem (local zone overwrites global zone, previously it added) * src/iiwu_conv.c: Using now oncave / convex equation from SF specs. Removed ct2hz functions and tables. (iiwu_ct2hz): Limit checking (iiwu_cb2amp): Removed 'magic number' (iiwu_tc2sec): Avoided == for iiwu_real_t * src/iiwu_cmd.c: Increased number of tokens. Using WORKLINELENGTH constant. Changed max. gain to 5. Added LADSPA commands. Renamed misleading rev_bypass command to rev_enable * src/iiwu_chan.c: Centered pitch wheel. Added 'expression' modulator (CC 11). * configure.in: Added LADSPA support 2002-04-03 Peter Hanappe * src/iiwu_voice.c (iiwu_voice_run_dsp): Integrated Markus Nentwig's new filter design 2002-03-12 Peter Hanappe * src/iiwusynth.h: the preset iteration in a soundfont now takes a pointer to a preset structure * src/iiwu_sys.c (iiwu_profile_data): added support for profiling * src/iiwu_voice.c (iiwu_voice_write): turns off voice if amplitude < -100 dB in release phase. Set filter gain back to old value (0.25f * ...) * src/iiwuplay.c (main): added gain, interactive, and reverb options * src/iiwusynth.c (main): added gain and reverb options * src/iiwu_synth.c (iiwu_synth_write_s16): added brickwall limiter for s16 samples 2002-01-29 Stephane Letz * src/iiwu_midishare.c : Compilation on MacOSX, use a task for typeNote management * src/iiwu_sys.c : Compilation on MacOSX * src/iiwu_sys.h : Compilation on MacOSX * src/iiwu_sfont.c : Use the flag MACINTOSH instead of MACOS * config_macos.h : Cleanup * config_macosx.h : New file, compilation on MacOSX 2002-01-21 Stephane Letz * src/iiwu_midi.c (delete_iiwu_midi_handler): Desallocation of heap allocated strings * src/iiwusynth_priv.h : Definition of strdup if not available (Macintosh) 2002-01-16 Peter Hanappe * src/iiwu_alsa.c (new_iiwu_alsa_seq_driver): Applied and adjusted Bob Ham's patch: support for configurable ALSA sequencer client name. * src/iiwu_chan.c (iiwu_channel_cc): Applied Bob Ham's patch: added bank select midi message. 2001-12-31 Peter Hanappe * src/iiwu_synth.c (iiwu_synth_damp_voices): Sustain messages are now handled. Updated iiwu_channel and iiwu_voice. (delete_iiwu_synth): SoundFonts are deleted. 2001-12-21 Stephane Letz * src/iiwu_midishare.c (new_iiwu_midishare_midi_driver, delete_iiwu_midishare_midi_driver): Updated to be compiled either in driver or application mode with the flag MIDISHARE_DRIVER. 2001-12-20 Stephane Letz * src/iiwu_portaudio.c (iiwu_portaudio_run , new_iiwu_portaudio_driver): Adaptation for new audio drivers * src/iiwu_synth.c (audio driver definition): Adaptation for PortAudio driver * src/iiwu_sys.c (header): Adaptation for compilation on MacOS9 * src/iiwu_sys.h (header): Adaptation for compilation on MacOS9 2001-12-16 Peter Hanappe * src/iiwuplay.c (main): The .iiwusynth file is loaded *before* the soundfonts on the command lines are loaded * src/iiwusynth.c (main): idem. 2001-12-16 Peter Hanappe * src/iiwu_midi.c (iiwu_player_callback): Fixed error in midi timing after a tempo change * src/iiwu_jack.c (new_iiwu_jack_audio_driver): Added first version of JACK driver 2001-12-14 Peter Hanappe * src/iiwu_synth.c (iiwu_synth_noteoff): noteon/notoff events can print a clear message, useful for debugging. * src/iiwu_sys.c (struct _iiwu_timer_t ): timer moved from iiwu_midi.c to iiwu_sys.c * src/iiwusynth.h: New organization of settings; using bit flags; added verbose option * src/iiwusynth.c (main): Added the verbose option * src/iiwuplay.c (main): Added the verbose option 2001-10-05 Stephane Letz * src/iiwu_portaudio.c (new_iiwu_portaudio_driver): imported new driver for the PortAudio library. 2001-10-04 Stephane Letz * src/iiwu_synth.c (new_iiwu_synth): Fixed bug in synth initialisation 2001-10-02 Peter Hanappe * src/iiwu_cmd.c (iiwu_get_userconf): returns default user configuration (iiwu_get_sysconf): returns default system configuration (iiwu_synth_cmdline): Fixed bug with argument offset. Empty lines are skipped correctly. * src/iiwusynth.c (main): loads the user or system config * src/iiwuplay.c (main): loads the user or system config * src/iiwu_synth.c (iiwu_sp_write): Using new envelope model for modulation envelope 2001-09-29 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write): redesigned the envelopes. 2001-09-20 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write): redesigned the dsp loop. it's faster and it sounds better (!) 2001-09-19 Peter Hanappe * src/iiwu_sfont.c (iiwu_sample_import_sfont): better checking for minimum sample size, loop start and loop end offsets. 2001-09-17 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write): improved calculation of filter coefficients (new_iiwu_synth): using settings structure 2001-09-09 Peter Hanappe * src/iiwu_synth.h (iiwu_phase_decr): fixed bug * src/iiwu_synth.c (iiwu_synth_noteoff): noteoff now turns off the oldest note only (instead of all notes with the given channel and key) 2001-07-10 Peter Hanappe * src/iiwu_midi.h: removed midi driver join function. updated all structures, implementations and callers. 2001-07-04 Peter Hanappe * src/iiwuplay.c (print_help): corrected errors in the help and usage display. 2001-06-29 Peter Hanappe * src/iiwu_synth.c (iiwu_synth_one_block): new function. fills the buffer with fresh samples. (iiwu_synth_write_lr): now calls iiwu_synth_one_block. the synthesizer uses fixed synthesis buffer size, independent of the requested buffer length passed to iiwu_synth_write_lr. (iiwu_revmodel_processreplace): new uses fixed IIWU_BUFSIZE value for buffer length. (iiwu_revmodel_processmix): uses fixed IIWU_BUFSIZE value 2001-06-22 Peter Hanappe * src/iiwusynth.c (iiwu_handle_fonts): new shell command to list the loaded fonts. (iiwu_handle_mstat): new shell command to list the statistics of the midi driver. 2001-06-19 Peter Hanappe * src/iiwusynth.c (main): Several command line options are available to select the midi and audio driver and device. Using the getopt function on posix machines. 2001-06-16 Peter Hanappe * src/iiwu_synth.h: new iiwu_revmodel_presets_t structure to store reverb presets (concert hall, room, ...) * src/iiwu_synth.c (iiwu_synth_write_lr): now using 1 reverb for all synthesis processes. the synthesis processes now receive a left and right buffer, a reverb buffer, a chorus buffer, and a monobuffer for their temporarry storage. reverb now always on. (new_iiwu_sp): no longer allocating a reverb module nor a monobuf. only one reverb model and monobuffer allocated by the synth object (read: much less memory usage). * src/iiwu_midi.c (iiwu_player_callback): fixed timing errors. midi should play correctly now. 2001-06-09 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write_lr): now using a 64-bits fixed-point number to calculate the phase of the wavetable. because of rounding erros, the float value I used before gave terrible tuning problems. I updated all the intepolation macros. * src/iiwusynth_priv.h: included the iiwu_phase_t data type. This type represents a 64-bits fixed-point number. It's used to hold the phase in the wavetable. 2001-06-08 Peter Hanappe * src/iiwu_midi.c (new_iiwu_midi_handler): Better support for runtime selection of the MIDI driver (using the iiwu_mdriver_definition_t structure) * src/iiwu_auport.c (new_iiwu_auport): Better support for runtime selection of the audio driver (using the iiwu_adriver_definition_t structure) 2001-06-07 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write_lr): rewrote the dsp function to accept a seperate left and right channel buffer. (iiwu_sp_write_lr): using cubic hermite interpolation by default. (iiwu_synth_write_lr): added a dsp function to accept a seperate left and right channel buffer. 2001-05-26 Peter Hanappe * src/iiwu_midi.c (iiwu_midi_parser_parse): Fixed a bug in the midi parser (running status should not be split in a status and channel part for system messages). (iiwu_midi_send_event): pitch bend events are now handled 2001-05-25 Peter Hanappe * src/iiwu_midi.c (iiwu_midi_file_getc): Fixed bug when pushed back byte equals zero (mf->c >= 0) * src/iiwu_midi.c (iiwu_midi_file_getc): Fixed bug when pushed back byte equals zero (mf->c >= 0) 2001-05-24 Peter Hanappe * src/iiwusynth.c: added the stupidly simple interpreter * src/iiwu_synth.c: removed all param strcutures. * src/iiwu_synth.c (iiwu_channel_get_banknum): new function 2001-05-23 Peter Hanappe * src/iiwu_synth.c (iiwu_sp_write): Fixed devide by zero in filter * src/smurf.c (gerr): applied Josh's patch: using va_list now (as it should). 2001-05-22 Peter Hanappe * src/iiwu_midi.c: the midi handler is now devided in a dummy iiwu_midi_handler_t and a "low level" driver. This allows for multiple midi drivers to be compiled in. * src/iiwusynth.h: renamed iiwu_midi_driver_t to iiwu_midi_handler_t * src/iiwu_auport.c (new_iiwu_auport): new "driver" argument to select between alsa, oss, midishare, directx, ... * configure.in: preparing for the first pre-release, version 0.0.1 fluidsynth-1.1.9/INSTALL000066400000000000000000000200471322272076000147500ustar00rootroot00000000000000Select build system =================== fluidsynth currently provides two different build systems, one based on autotools, the other based on cmake. The autotools build system is deprected, unmaintained and will be removed in a future release. The recommend way to build is using cmake, please see the README.cmake file. If you still want to use the unsupported autotools build system, see below. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. fluidsynth-1.1.9/LICENSE000066400000000000000000000635351322272076000147350ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} 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 Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it! fluidsynth-1.1.9/Makefile.am000066400000000000000000000005701322272076000157520ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in ACLOCAL_AMFLAGS=-I m4 SUBDIRS = src doc include cmake_admin EXTRA_DIST = TODO acinclude.m4 autogen.sh fluidsynth.pc.in \ fluidsynth.spec.in fluidsynth.spec fluidsynth.anjuta README-OSX \ README.cmake CMakeLists.txt DISTCLEANFILES = fluidsynth.pc pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = fluidsynth.pc fluidsynth-1.1.9/NEWS000066400000000000000000000001061322272076000144100ustar00rootroot00000000000000Check the web site at http://www.fluidsynth.org for the latest news. fluidsynth-1.1.9/README-OSX000066400000000000000000000054311322272076000152460ustar00rootroot00000000000000fluidsynth-1.1.2 on Mac OS X ----------------------------------------------------------------------- fluidsynth-1.1.2 can be installed in three ways on your Apple computer: A. Compilation and installation by hand using autotools ------------------------------------------------------- Requirements: - "XcodeTools.mpkg","DevSDK.pkg", "CoreAudioSDK.pkg" packages (The Leopard/Snow Leopard Install DVD) - Fink installation - libgnugetopt, readline5, libflac8-dev, libsndfile1-dev and glib2-dev from fink: e.g. "fink install libgnugetopt" - MIDI Patchbay 1.0.3 - Optional: JackOSX.0.85*.pkg.zip 1. Run configure && make: $ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure && make 2. make install as superuser B. Compilation and installation by hand using cmake --------------------------------------------------- Requirements: - "XcodeTools.mpkg","DevSDK.pkg", "CoreAudioSDK.pkg" packages (The Leopard/Snow Leopard Install DVD) - Fink installation - libgnugetopt, readline5, libflac8-dev, libsndfile1-dev, glib2-dev, dbus1.3-dev and cmake from fink: e.g. "fink install libgnugetopt" - MIDI Patchbay 1.0.3 - Optional: JackOSX.0.85*.pkg.zip 1. Run cmake from the newly-created "build" folder: $ mkdir build ; cd build ; cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local ; make where "/usr/local" could be replaced by the PREFIX of your choice. 2. make install as superuser C. Compilation and installation of the fink fluidsynth package -------------------------------------------------------------- Note: The fink fluidsynth package is currently at version 1.1.1. The update will become available some time after the source tarball for version 1.1.2 becomes officially available on http://fluidsynth.sourceforge.net Requirements: - "XcodeTools.mpkg","DevSDK.pkg", "CoreAudioSDK.pkg" packages (The Leopard/Snow Leopard Install DVD) - Fink installation - MIDI Patchbay 1.0.3 In Terminal.app simply type $ fink install fluidsynth and the fink fluidsynth package automatically installs its dependencies. C. Running fluidsynth ______________________ $ fluidsynth -a SOUND_DRIVER -j -m coremidi where the SOUND_DRIVER is either "jack" or "coreaudio" (in order to run the coreaudio sound driver replace the "-a jack -j" options with "-a coreaudio") In order to run another instance of fluidsynth, open another terminal window: $ fluidsynth -a SOUND_DRIVER [-j] -m coremidi -p name_of_instance Connect MIDI I/O devices using MIDI Patchbay Ebrahim Mayat 12th August 2010 fluidsynth-1.1.9/README.cmake000066400000000000000000000146021322272076000156560ustar00rootroot00000000000000What is CMake? ============== CMake is a cross platform build system, that can be used to replace the old auto-tools, providing a nice building environment and advanced features. Some of these features are: * Out of sources build: CMake allows you to build your software into a directory different to the source tree. You can safely delete the build directory and all its contents once you are done. * Multiple generators: classic makefiles can be generated for Unix and MinGW, but also Visual Studio, XCode and Eclipse CDT projects among other types. * Graphic front-ends for configuration and build options. More information and documentation is available at the CMake project site: http://www.cmake.org CMake is free software. You can get the sources and pre-compiled packages for Linux and other systems at: http://www.cmake.org/cmake/resources/software.html How to use it? ============== 1. You need CMake 2.6.3 or later to build FluidSynth 2. Unpack the FluidSynth sources somewhere, or checkout the repository, and create a build directory. For instance, using a command line shell: $ tar -xvzf Downloads/fluidsynth-x.y.z.tar.gz $ cd fluidsynth-x.y.z $ mkdir build 2. Execute CMake from the build directory, providing the source directory location and optionally, the build options. There are several ways. * From a command line shell: $ pwd fluidsynth-x.y.z $ cd build $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -Denable-ladspa=1 Valid values for boolean (enable-xxxx) options: 1, 0, yes, no, on, off. * There are also several alternative CMake front-ends, if you don't want to use the command line interface: * ncurses based program, for Linux and Unix: ccmake * GUI, Qt4 based program, multiplatform: cmake-gui * GUI, Windows native program: CMakeSetup.exe 3. Execute the build command. If you used the Makefiles generator (the default in Linux and other Unix systems) then execute make, gmake or mingw32-make. If you generated a project file, use your IDE to build it. You may find more information in the project Wiki: http://sourceforge.net/apps/trac/fluidsynth/wiki/BuildingWithCMake Compiling with make =================== There are many targets available. To see a complete list of them, type: $ make help The build process usually hides the compiler command lines, to show them: $ make VERBOSE=1 There is a "clean" target, but not a "distclean" one. You should use a build directory different to the source tree. In this case, the "distclean" target would be equivalent to simply removing the build directory. To compile the developer documentation, install Doxygen (http://www.doxygen.org) and use this command from the root build directory: $ make doxygen If something fails ================== If there is an error message while executing CMake, this probably means that a required package is missing in your system. You should install the missing component and run CMake again. If there is an error executing the build process, after running a flawless CMake configuration process, this means that there may be an error in the source code, or in the build system, or something incompatible in 3rd party libraries. The CMake build system for FluidSynth is experimental. It will take a while until it becomes stable and fully tested. You can help providing feedback, please send a report containing your problems to the FluidSynth development mailing list: http://lists.nongnu.org/mailman/listinfo/fluid-dev For developers - how to add a new feature to the CMake build system =================================================================== Let's explain this issue with an example. We are adding D-Bus support to FluidSynth as an optional feature, conditionally adding source files that require this feature. The first step is to add a macro "option()" to the main CMakeLists.txt file, the one that is located at the fluidsynth root directory. file CMakeLists.txt, line 64: option ( enable-dbus "compile DBUS support (if it is available)" on ) Now, let's check if the dbus-1 library and headers are installed, using pkg-config: file CMakeLists.txt, lines 371-377: unset ( DBUS_SUPPORT CACHE ) if ( enable-dbus ) pkg_check_modules ( DBUS dbus-1>=1.0.0 ) set ( DBUS_SUPPORT ${DBUS_FOUND} ) else ( enable-dbus ) unset_pkg_config ( DBUS ) endif ( enable-dbus ) The first line clears the value of the CMake variable DBUS_SUPPORT. If the value of the option "enable-dbus" is true, then the macro pkg_check_modules() is used to test a package named "dbus-1" with version 1.0.0 or later. This macro automatically defines the variables DBUS_LIBRARIES, DBUS_INCLUDEDIR, DBUS_FOUND and others. The value of the last one is assigned to our variable DBUS_SUPPORT for later use. There is a report to summarize the performed checks and the enabled features after the configuration steps, so let's add a line in this report regarding the D-Bus support. file cmake_admin/report.cmake, lines 14-18: if ( DBUS_SUPPORT ) message ( "D-Bus: yes" ) else ( DBUS_SUPPORT ) message ( "D-Bus: no" ) endif ( DBUS_SUPPORT ) The variable DBUS_SUPPORT is available for the CMake files, but we want to make it available to the compilers as well, to conditionally build code using "#ifdef DBUS_SUPPORT". This can be done adding a line to the config.cmake file: file src/config.cmake, lines 22-23: /* Define if D-Bus support is enabled */ #cmakedefine DBUS_SUPPORT @DBUS_SUPPORT@ The file config.cmake will be processed at configure time, producing a header file "config.h" in the build directory with this content, if the dbus support has been enabled and found: /* Define if D-Bus support is enabled */ #define DBUS_SUPPORT 1 Finally, we can add the new source files to the build system for the compiler target with the macro add_library(), and the libraries for the linker target with the macros link_directories() and target_link_libraries(). file src/CMakeLists.txt, lines 57-60 if ( DBUS_SUPPORT ) set ( fluid_dbus_SOURCES fluid_rtkit.c fluid_rtkit.h ) include_directories ( ${DBUS_INCLUDEDIR} ${DBUS_INCLUDE_DIRS} ) endif ( DBUS_SUPPORT ) file src/CMakeLists.txt, lines 163-197 link_directories ( ... ${DBUS_LIBDIR} ${DBUS_LIBRARY_DIRS} ) add_library ( libfluidsynth ... ${fluid_dbus_SOURCES} ... ) file src/CMakeLists.txt, lines 163-197 target_link_libraries ( libfluidsynth ... ${DBUS_LIBRARIES} ... ) fluidsynth-1.1.9/README.md000066400000000000000000000105631322272076000152000ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/FluidSynth/fluidsynth.svg?branch=master)](https://travis-ci.org/FluidSynth/fluidsynth) Introduction ============ FluidSynth is a software real-time synthesizer based on the Soundfont 2 specifications. FluidSynth reads and handles MIDI events from the MIDI input device. It is the software analogue of a MIDI synthesizer. FluidSynth can also play midifiles using a Soundfont. Information on the web ====================== The place to look if you are looking for the latest information on FluidSynth is the web site at http://www.fluidsynth.org/. Why did we do it ================ The synthesizer grew out of a project, started by Samuel Bianchini and Peter Hanappe, and later joined by Johnathan Lee, that aimed at developing a networked multi-user game. Sound (and music) was considered a very important part of the game. In addition, users had to be able to extend the game with their own sounds and images. Johnathan Lee proposed to use the Soundfont standard combined with an intelligent use of midifiles. The arguments were: - Wave table synthesis is low on CPU usage, it is intuitive and it can produce rich sounds - Hardware acceleration is possible if the user owns a Soundfont compatible soundcard (important for games!) - MIDI files are small and Soundfont2 files can be made small thru the intelligent use of loops and wavetables. Together, they are easier to downloaded than MP3 or audio files. - Graphical editors are available for both file format: various Soundfont editors are available on PC and on Linux (Smurf!), and MIDI sequencers are available on all platforms. It seemed like a good combination to use for an (online) game. In order to make Soundfonts available on all platforms (Linux, Mac, and Windows) and for all sound cards, we needed a software Soundfont synthesizer. That is why we developed FluidSynth. Design decisions ================ The synthesizer was designed to be as self-contained as possible for several reasons: - It had to be multi-platform (Linux, MacOS, Win32). It was therefore important that the code didn't rely on any platform specific library. - It had to be easy to integrate the synthesizer modules in various environements, as a plugin or as a dynamically loadable object. I wanted to make the synthesizer available as a plugin (jMax, LADSPA, Xmms, WinAmp, Director, ...); develop language bindings (Python, Java, Perl, ...); and integrate it into (game) frameworks (Crystal Space, SDL, ...). For these reasons I've decided it would be easiest if the project stayed very focussed on it's goal (a Soundfont synthesizer), stayed small (ideally one file) and didn't dependent on external code. Links ===== Home Page - http://www.fluidsynth.org Documentation - Introduction to SoundFonts, by Josh Green, http://smurf.sourceforge.net/sfont_intro.php - Soundfont2 Documentation, http://www.synthfont.com/SFSPEC21.PDF (if it moved, do a search on sfspec21.pdf). - Soundfont.com FAQ, http://www.soundfont.com/faqs.html - The MIDI Manufacturers Association has a standard called "Downloadable Sounds (DLS)" that closely ressembles the Soundfont Specifications, http://www.midi.org/about-midi/dls/abtdls.htm Software SoundFont Synthesizers: - LiveSynth Pro DXi and Crescendo from LiveUpdate (Win), http://www.livesynth.com/lspro.html - Unity DS-1 from Bitheadz (Win & Mac), http://www.bitheadz.com/ - QuickTime 5 from Apple (Win & Mac), http://www.apple.com/quicktime/ - Logic from eMagic, http://www.emagic.de Soundfont Editors - Smurf Soundfont Editor by Josh Green (Linux), http://smurf.sourceforge.net - Vienna SoundFont Editor by Creative Technology Ltd. (Win) - Alive Soundfont Editor by Soundfaction (Win), http://www.soundfaction.com/alive/index.htm - Polyphone, http://polyphone-soundfonts.com/en/ **Note:** We cannot recommend using Audio Compositor for creating or editing Soundfonts, as it generates files that violate the Soundfont2 spec (specifically the order of generators as defined in section 8.1.2) and are therefore unusable with fluidsynth! Conversion Tools - CDxtract from CDxtract (Win), http://www.cdxtract.com - ReCycle from Propellerhead Software (Win & Mac), http://www.propellerheads.se/products/recycle/ - Translator from Rubber Chicken Software (Win & Mac), http://www.chickensys.com/translator Soundfont Databases - HammerSound, http://www.hammersound.net fluidsynth-1.1.9/THANKS000066400000000000000000000030761322272076000146350ustar00rootroot00000000000000For the list of authors that contributed to the code, please read the file AUTHORS. We would like to thank the Fondation Daniel Langlois for their funding. Their help made this project to get of the ground. Without it would simply not exist. Many thanks! (http://www.fondation-langlois.org) In alphabetic order: Paul Barton-Davis Samuel Bianchini Raoul Bonisch Rui Nuno Capela Jake Commander Francois Dechelle Ken Ellinwood Tim Goetze Anthony Green Josh Green Bob Ham Peter Hanappe Jezar Fernando Pablo Lopez-Lezcano Johnathan Lee Stephane Letz Ebrahim Mayat Sven Meier Juergen Mueller Markus Nentwig David Olofson Sergey Pavlishin Dave Phillips Daniel Pressnitzer Gerald Pye Norbert Schnell Joshua Scholar Antoine Schmitt Werner Schweer Stephan Tassart Martin Uddén fluidsynth-1.1.9/TODO000066400000000000000000000057051322272076000144130ustar00rootroot00000000000000New features ------------ - 24 bit sample support - Non-realtime MIDI file rendering - Sample streaming, load/unload sample on demand - Synth sample rate change after initial creation - handle tuning sysex messages - Audio level metering - Active voice count monitoring Synthesis --------- - Improve voice stealing algorithm - Dynamic voice killing (based on CPU usage) - Batch voice activation (stereo synch. as per SoundFont spec) - Pitch control on stereo samples not managed as should - soft clipping, compressor, limitor, or automatic gain control Drivers ------- - libao audio output driver - MacOS X MidiCore component - Windows DirectMusic component - ASIO driver - DirectSound 3D and EAX Bugs to mash ------------ - Fix warnings on 64 bit platforms and type-puned pointer warning - Add byte swapping support (on synthesis or sample load?) - Investigate why MIDI rendering causes burst of notes at start Validation ---------- - Validation tests: create soundfont with basic wave forms [sine, square, triangle]; make test midi file; compare with SBLive output; "regression" test - Validate chorus (often sounds rather crappy) - Analyse performance Documentation ------------- - Write documention on tuning - fluid_synth_program_select2() with name of soundfont instead of font_id - fluid_synth_set_gen2() - Add usage scenarios in the documentation - User and system configuration file Binaries -------- - FluidSynth * Linux * Win * MacOS X - fluid~ (Pd/Linux, MaxMSP/MacOSX, MaxMSP/Windows) - fluidsynth~ (MaxMSP/MacOSX, MaxMSP/Windows) - FluidXtra Misc ---- - Remove dependency of settings on audio driver and other (see fluid_settings_init()) - Add "unselect" command to shell to set a MIDI channel to not sound. - When specifying -i -s (no console and TCP server) log to TCP clients with easier parsable messages ("warning:", "error:", etc) - add function to get initial soundfont generator value - Pause and resume the synthesizer/audio thread (run synthesizer as a daemon) - set loop on/off on a sample (set_gen GEN_SAMPLEMODE?) FluidSynth Next Generation -------------------------------------------- Top of the list - Use FIFOs to send events to the audio thread - Redo sfloader api using "interface" api - 3D audio output Design - turn ladspa fx unit and router in indepedent objects, remove their dependencies from the synth object MIDI player - Add API to manipulate and query MIDI file list - generalize use of fluid_event_t, remove fluid_midi_event_t Shell & command handler - Add "note" command that plays a note with a duration (sequencer) - MIDI file player commands (load/play/stop) - Relax dependency of the command handler on the synthesizer - Allow settings to be loaded before the synthesizer is created SoundFont Specs: MIDI Specs - Omni and poly modes - sample dump - MIDI thru - Scalable Polyphony MIDI (SP-MIDI) Unsorted - include readline in project - additional aux buses - rewrite midi file using new sequencer - direct access to audio buffer fluidsynth-1.1.9/acinclude.m4000066400000000000000000000064521322272076000161140ustar00rootroot00000000000000dnl Some additional autoconf macros AC_DEFUN([AC_MIDISHARE], [ AC_ARG_ENABLE(midishare, AS_HELP_STRING([--enable-midishare], [Compile MIDISHARE support (default=auto)]), midishare=$enableval, midishare=yes) MIDISHARE_SUPPORT=0 if test "x$midishare" != "xno"; then AC_CHECK_HEADERS(MidiShare.h) if test "${ac_cv_header_MidiShare_h}" = "yes"; then MIDISHARE_SUPPORT=1 midishare_found=yes AC_CHECK_LIB([MidiShare], [MidiOpen],, [midishare_found=no]) if test "x$midishare_found" = "xyes" ; then MIDISHARE_SUPPORT=1 AC_DEFINE(MIDISHARE_SUPPORT, 1, [Define to enable MidiShare driver]) fi if test "x$midishare_found" = "xno" ; then AC_MSG_WARN([ *** Could not find the required MidiShare library]) fi dnl midishare_found = yes test else AC_MSG_WARN([ *** Could not find MidiShare.h, disabling MidiShare driver]) fi dnl midishare.h header test fi dnl enable_midishare != no? ]) AC_DEFUN([AC_OSS_AUDIO], [ AC_ARG_ENABLE(oss-support, [ --disable-oss-support Do not compile OSS support (default=auto)], enable_oss_support=$enableval, enable_oss_support="yes") OSS_SUPPORT=0 if test "x$enable_oss_support" != "xno"; then AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/soundcard.h machine/soundcard.h) if test "${ac_cv_header_fcntl_h}" = "yes" && \ test "${ac_cv_header_sys_ioctl_h}" = "yes"; then if test "${ac_cv_header_sys_soundcard_h}" = "yes" || \ test "${ac_cv_header_machine_soundcard_h}" = "yes"; then OSS_SUPPORT=1 AC_DEFINE(OSS_SUPPORT, 1, [Define to enable OSS driver]) else AC_MSG_WARN([ *** Could not find soundcard.h, disabling OSS driver]) fi dnl soundcard.h header test else AC_MSG_WARN([ *** Could not find fcntl.h and/or ioctl.h which are required for sound and midi support]) fi dnl fcntl.h & ioctl.h header test fi dnl enable_oss_support != no? ]) dnl Configure Paths for readline (Josh Green 2003-06-10) dnl dnl AM_PATH_READLINE([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) dnl Test for readline, and define READLINE_CFLAGS and dnl READLINE_LIBS as appropriate. dnl enables arguments --with-readline-prefix= AC_DEFUN([AM_PATH_READLINE], [dnl Save the original CFLAGS, and LIBS save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" readline_found=yes dnl dnl Setup configure options dnl AC_ARG_WITH(readline-prefix, [ --with-readline-prefix=PATH Path where readline is (optional)], [readline_prefix="$withval"], [readline_prefix=""]) AC_MSG_CHECKING(for readline) dnl Add readline to the LIBS path READLINE_LIBS="-lreadline" if test "${readline_prefix}" != "" ; then READLINE_LIBS="-L${readline_prefix}/lib $READLINE_LIBS" READLINE_CFLAGS="-I${readline_prefix}/include" else READLINE_CFLAGS="" fi LIBS="$READLINE_LIBS $LIBS" CFLAGS="$READLINE_CFLAGS $CFLAGS" AC_TRY_COMPILE([ #include #include ], [ #ifndef readline return (1); #else return (0); #endif ], [AC_MSG_RESULT(found.)], [AC_MSG_RESULT(not present.) readline_found=no] ) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" if test "x$readline_found" = "xyes" ; then ifelse([$1], , :, [$1]) else READLINE_CFLAGS="" READLINE_LIBS="" ifelse([$2], , :, [$2]) fi dnl That should be it. Now just export out symbols: AC_SUBST(READLINE_CFLAGS) AC_SUBST(READLINE_LIBS) ]) fluidsynth-1.1.9/autogen.sh000077500000000000000000000006271322272076000157220ustar00rootroot00000000000000#!/bin/sh # Some poor souls have linux distributions, that don't install pkg-config by default. #pkg-config --version does actually nothing, but it will fail and give 'sort of' an error message... pkg-config --version > /dev/null \ && aclocal \ && libtoolize -f \ && autoheader \ && autoconf \ && automake -a fluidsynth-1.1.9/bindings/000077500000000000000000000000001322272076000155115ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/README000066400000000000000000000005161322272076000163730ustar00rootroot00000000000000 This directory contains the projects that bind FluidSynth to a language or host application. The following projects are currently available: fluidmax: Provides the fluidsynth~ external object for Max/MSP. Written by Norbert Schnell. fluidsynth_jni: Provides a native interface for the Java language. Written by Peter Hanappe. fluidsynth-1.1.9/bindings/fluidmax/000077500000000000000000000000001322272076000173225ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidmax/README.TXT000066400000000000000000000071551322272076000206700ustar00rootroot00000000000000****************************************************************** * * This is fluidsynth~, * a derivative for Max/MSP of the Fluid Synth soundfont synthesizer. * * Fluid Synth is written by Peter Hanappe et al. * See http://www.fluidsynth.org/. * * Max/MSP integration by Norbert Schnell, IRCAM - Centre Pompidou * Norbert.Schnell@ircam.fr * * Take a look here: * http://www.ircam.fr/equipes/temps-reel/maxmsp/fluidsynth.html * _____________________________________________________________________ LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See file COPYING.LIB in the CVS root directory for further information on licensing terms. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. _____________________________________________________________________ SYSTEM REQUIREMENTS This version of fluidsynth~ was initially created and tested using Max/MSP 4.3 on Mac 10.3 and should work on all following versions of Max/MSP and Mac OS. _____________________________________________________________________ RELASE NOTES for version 01/2009 (release 13) - adapted to current FluidSynth code base (after years without activity) - no new features or relevant changes! _____________________________________________________________________ RELASE NOTES up to version 10/2005 (release 10, 11 and 12) - added micro-tuning methods (to be added to help patch) - fixed bug restricting argument of tuning-octave message to int (11) - fixed voice stealing (12) _____________________________________________________________________ RELASE NOTES up to version 09/2004 (release 7 to 9) - bug fix: now polyphony and # of midi channel arguments take effect (release 9) - added message resample permitting to chose resampling interpolation method (release 8) - added names (= file name without path and postfix) for soundfonts (7) - fixed bug of crash for "info channels" in case of channel(s) without defined preset - dropped Mac OS 9 version - extended help patch _____________________________________________________________________ RELASE NOTES of version 04/2004 (release 6) - fixed several bugs of the initial release (especially related to the translation of paths - introduced names for soundfonts (without path and .sf2 postfix) - introduced a message "info" in order to query the current state of tuning-octave _____________________________________________________________________ SVN DIRECTORY svn repository: https://resonance.org/svn/fluidsynth/trunk/fluidsynth Max/MSP implementation directory: bindings/fluidmax The fluidsynth directory in the fluidmax directory contains only a version.h which is just added for the case that the version.h file in the fluidsynth checkout is not (auto-)"maked" or generated otherwise. For now the file fluid_sys.c is not included to the CW project. Instead there is a file called fluidmax_fakefuns.c implementing some necessary functions. _____________________________________________________________________ KNOWN BUGS: _____________________________________________________________________ TODO - implement log function properly fluidsynth-1.1.9/bindings/fluidmax/build/000077500000000000000000000000001322272076000204215ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidmax/build/osx-macho/000077500000000000000000000000001322272076000223175ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidmax/build/osx-macho/fluidmax.xcodeproj/000077500000000000000000000000001322272076000261245ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidmax/build/osx-macho/fluidmax.xcodeproj/project.pbxproj000066400000000000000000000411261322272076000312040ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; objects = { /* Begin PBXBuildFile section */ 3B1863D00B2DACF200083EB3 /* fluidmax_fakefuns.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863CE0B2DACF200083EB3 /* fluidmax_fakefuns.c */; }; 3B1863D10B2DACF200083EB3 /* fluidmax.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863CF0B2DACF200083EB3 /* fluidmax.c */; }; 3B1863E20B2DAD7800083EB3 /* fluid_chan.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D30B2DAD7800083EB3 /* fluid_chan.c */; }; 3B1863E30B2DAD7800083EB3 /* fluid_chorus.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D40B2DAD7800083EB3 /* fluid_chorus.c */; }; 3B1863E40B2DAD7800083EB3 /* fluid_conv.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D50B2DAD7800083EB3 /* fluid_conv.c */; }; 3B1863E50B2DAD7800083EB3 /* fluid_defsfont.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D60B2DAD7800083EB3 /* fluid_defsfont.c */; }; 3B1863E60B2DAD7800083EB3 /* fluid_gen.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D70B2DAD7800083EB3 /* fluid_gen.c */; }; 3B1863E70B2DAD7800083EB3 /* fluid_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D80B2DAD7800083EB3 /* fluid_hash.c */; }; 3B1863E80B2DAD7800083EB3 /* fluid_list.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863D90B2DAD7800083EB3 /* fluid_list.c */; }; 3B1863E90B2DAD7800083EB3 /* fluid_mod.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863DA0B2DAD7800083EB3 /* fluid_mod.c */; }; 3B1863EA0B2DAD7800083EB3 /* fluid_ramsfont.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863DB0B2DAD7800083EB3 /* fluid_ramsfont.c */; }; 3B1863EB0B2DAD7800083EB3 /* fluid_rev.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863DC0B2DAD7800083EB3 /* fluid_rev.c */; }; 3B1863EC0B2DAD7800083EB3 /* fluid_settings.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863DD0B2DAD7800083EB3 /* fluid_settings.c */; }; 3B1863EE0B2DAD7800083EB3 /* fluid_synth.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863DF0B2DAD7800083EB3 /* fluid_synth.c */; }; 3B1863EF0B2DAD7800083EB3 /* fluid_tuning.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863E00B2DAD7800083EB3 /* fluid_tuning.c */; }; 3B1863F00B2DAD7800083EB3 /* fluid_voice.c in Sources */ = {isa = PBXBuildFile; fileRef = 3B1863E10B2DAD7800083EB3 /* fluid_voice.c */; }; 3BC6778E0ADB9F2E0064088F /* MaxAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BC6778C0ADB9F2E0064088F /* MaxAPI.framework */; }; 3BC6778F0ADB9F2E0064088F /* MaxAudioAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BC6778D0ADB9F2E0064088F /* MaxAudioAPI.framework */; }; 5CA4036E0F1F689D0054E6B0 /* fluid_dsp_float.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CA4036D0F1F689D0054E6B0 /* fluid_dsp_float.c */; }; 8D01CCCE0486CAD60068D4B7 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08EA7FFBFE8413EDC02AAC07 /* Carbon.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 08EA7FFBFE8413EDC02AAC07 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; 3B1863CE0B2DACF200083EB3 /* fluidmax_fakefuns.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluidmax_fakefuns.c; path = bindings/fluidmax/fluidmax_fakefuns.c; sourceTree = ""; }; 3B1863CF0B2DACF200083EB3 /* fluidmax.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluidmax.c; path = bindings/fluidmax/fluidmax.c; sourceTree = ""; }; 3B1863D30B2DAD7800083EB3 /* fluid_chan.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_chan.c; path = ../../../../src/fluid_chan.c; sourceTree = SOURCE_ROOT; }; 3B1863D40B2DAD7800083EB3 /* fluid_chorus.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_chorus.c; path = ../../../../src/fluid_chorus.c; sourceTree = SOURCE_ROOT; }; 3B1863D50B2DAD7800083EB3 /* fluid_conv.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_conv.c; path = ../../../../src/fluid_conv.c; sourceTree = SOURCE_ROOT; }; 3B1863D60B2DAD7800083EB3 /* fluid_defsfont.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_defsfont.c; path = ../../../../src/fluid_defsfont.c; sourceTree = SOURCE_ROOT; }; 3B1863D70B2DAD7800083EB3 /* fluid_gen.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_gen.c; path = ../../../../src/fluid_gen.c; sourceTree = SOURCE_ROOT; }; 3B1863D80B2DAD7800083EB3 /* fluid_hash.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_hash.c; path = ../../../../src/fluid_hash.c; sourceTree = SOURCE_ROOT; }; 3B1863D90B2DAD7800083EB3 /* fluid_list.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_list.c; path = ../../../../src/fluid_list.c; sourceTree = SOURCE_ROOT; }; 3B1863DA0B2DAD7800083EB3 /* fluid_mod.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_mod.c; path = ../../../../src/fluid_mod.c; sourceTree = SOURCE_ROOT; }; 3B1863DB0B2DAD7800083EB3 /* fluid_ramsfont.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_ramsfont.c; path = ../../../../src/fluid_ramsfont.c; sourceTree = SOURCE_ROOT; }; 3B1863DC0B2DAD7800083EB3 /* fluid_rev.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_rev.c; path = ../../../../src/fluid_rev.c; sourceTree = SOURCE_ROOT; }; 3B1863DD0B2DAD7800083EB3 /* fluid_settings.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_settings.c; path = ../../../../src/fluid_settings.c; sourceTree = SOURCE_ROOT; }; 3B1863DF0B2DAD7800083EB3 /* fluid_synth.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_synth.c; path = ../../../../src/fluid_synth.c; sourceTree = SOURCE_ROOT; }; 3B1863E00B2DAD7800083EB3 /* fluid_tuning.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_tuning.c; path = ../../../../src/fluid_tuning.c; sourceTree = SOURCE_ROOT; }; 3B1863E10B2DAD7800083EB3 /* fluid_voice.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fluid_voice.c; path = ../../../../src/fluid_voice.c; sourceTree = SOURCE_ROOT; }; 3BC6778C0ADB9F2E0064088F /* MaxAPI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MaxAPI.framework; path = /Library/Frameworks/MaxAPI.framework; sourceTree = ""; }; 3BC6778D0ADB9F2E0064088F /* MaxAudioAPI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MaxAudioAPI.framework; path = /Library/Frameworks/MaxAudioAPI.framework; sourceTree = ""; }; 5CA4036D0F1F689D0054E6B0 /* fluid_dsp_float.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fluid_dsp_float.c; sourceTree = ""; }; 8D01CCD20486CAD60068D4B7 /* fluidsynth~.mxo */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "fluidsynth~.mxo"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8D01CCCD0486CAD60068D4B7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8D01CCCE0486CAD60068D4B7 /* Carbon.framework in Frameworks */, 3BC6778E0ADB9F2E0064088F /* MaxAPI.framework in Frameworks */, 3BC6778F0ADB9F2E0064088F /* MaxAudioAPI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 089C166AFE841209C02AAC07 /* yin */ = { isa = PBXGroup; children = ( 08FB77ADFE841716C02AAC07 /* Sources */, 089C1671FE841209C02AAC07 /* Frameworks */, 19C28FB4FE9D528D11CA2CBB /* Products */, ); name = yin; sourceTree = ""; }; 089C1671FE841209C02AAC07 /* Frameworks */ = { isa = PBXGroup; children = ( 3BC6778C0ADB9F2E0064088F /* MaxAPI.framework */, 3BC6778D0ADB9F2E0064088F /* MaxAudioAPI.framework */, 08EA7FFBFE8413EDC02AAC07 /* Carbon.framework */, ); name = Frameworks; sourceTree = ""; }; 08FB77ADFE841716C02AAC07 /* Sources */ = { isa = PBXGroup; children = ( 3B1863D20B2DACFC00083EB3 /* fluidsynth */, 3B1863CE0B2DACF200083EB3 /* fluidmax_fakefuns.c */, 3B1863CF0B2DACF200083EB3 /* fluidmax.c */, ); name = Sources; path = ../../../..; sourceTree = SOURCE_ROOT; }; 19C28FB4FE9D528D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8D01CCD20486CAD60068D4B7 /* fluidsynth~.mxo */, ); name = Products; sourceTree = ""; }; 3B1863D20B2DACFC00083EB3 /* fluidsynth */ = { isa = PBXGroup; children = ( 3B1863D30B2DAD7800083EB3 /* fluid_chan.c */, 3B1863D40B2DAD7800083EB3 /* fluid_chorus.c */, 3B1863D50B2DAD7800083EB3 /* fluid_conv.c */, 3B1863D60B2DAD7800083EB3 /* fluid_defsfont.c */, 3B1863D70B2DAD7800083EB3 /* fluid_gen.c */, 3B1863D80B2DAD7800083EB3 /* fluid_hash.c */, 3B1863D90B2DAD7800083EB3 /* fluid_list.c */, 3B1863DA0B2DAD7800083EB3 /* fluid_mod.c */, 3B1863DB0B2DAD7800083EB3 /* fluid_ramsfont.c */, 3B1863DC0B2DAD7800083EB3 /* fluid_rev.c */, 3B1863DD0B2DAD7800083EB3 /* fluid_settings.c */, 3B1863DF0B2DAD7800083EB3 /* fluid_synth.c */, 3B1863E00B2DAD7800083EB3 /* fluid_tuning.c */, 3B1863E10B2DAD7800083EB3 /* fluid_voice.c */, 5CA4036D0F1F689D0054E6B0 /* fluid_dsp_float.c */, ); name = fluidsynth; path = src; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8D01CCC60486CAD60068D4B7 /* fluidsynth~ */ = { isa = PBXNativeTarget; buildConfigurationList = 4FADC23308B4156C00ABE55E /* Build configuration list for PBXNativeTarget "fluidsynth~" */; buildPhases = ( 8D01CCCB0486CAD60068D4B7 /* Sources */, 8D01CCCD0486CAD60068D4B7 /* Frameworks */, 3BC677940ADB9F6C0064088F /* ShellScript */, ); buildRules = ( ); dependencies = ( ); name = "fluidsynth~"; productInstallPath = "$(HOME)/Library/Bundles"; productName = yin; productReference = 8D01CCD20486CAD60068D4B7 /* fluidsynth~.mxo */; productType = "com.apple.product-type.bundle"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; buildConfigurationList = 4FADC23708B4156C00ABE55E /* Build configuration list for PBXProject "fluidmax" */; compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = 089C166AFE841209C02AAC07 /* yin */; projectDirPath = ""; projectRoot = ""; targets = ( 8D01CCC60486CAD60068D4B7 /* fluidsynth~ */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ 3BC677940ADB9F6C0064088F /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "NAME=$WRAPPER_NAME\nDEST=$PROJECT_DIR/$EXTERNALS_DIR\nORIG=$TARGET_BUILD_DIR/$NAME\nPROD=$DEST/$NAME\n\n# set bundle bit for mxo's (on some installations, they show up as directories)\n/Developer/Tools/SetFile -a B $ORIG\n\nif test -d \"$PROD\"; then\n\trm -f -r \"$PROD\"\nfi\ncp -R $ORIG $DEST\n\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8D01CCCB0486CAD60068D4B7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 3B1863D00B2DACF200083EB3 /* fluidmax_fakefuns.c in Sources */, 3B1863D10B2DACF200083EB3 /* fluidmax.c in Sources */, 3B1863E20B2DAD7800083EB3 /* fluid_chan.c in Sources */, 3B1863E30B2DAD7800083EB3 /* fluid_chorus.c in Sources */, 3B1863E40B2DAD7800083EB3 /* fluid_conv.c in Sources */, 3B1863E50B2DAD7800083EB3 /* fluid_defsfont.c in Sources */, 3B1863E60B2DAD7800083EB3 /* fluid_gen.c in Sources */, 3B1863E70B2DAD7800083EB3 /* fluid_hash.c in Sources */, 3B1863E80B2DAD7800083EB3 /* fluid_list.c in Sources */, 3B1863E90B2DAD7800083EB3 /* fluid_mod.c in Sources */, 3B1863EA0B2DAD7800083EB3 /* fluid_ramsfont.c in Sources */, 3B1863EB0B2DAD7800083EB3 /* fluid_rev.c in Sources */, 3B1863EC0B2DAD7800083EB3 /* fluid_settings.c in Sources */, 3B1863EE0B2DAD7800083EB3 /* fluid_synth.c in Sources */, 3B1863EF0B2DAD7800083EB3 /* fluid_tuning.c in Sources */, 3B1863F00B2DAD7800083EB3 /* fluid_voice.c in Sources */, 5CA4036E0F1F689D0054E6B0 /* fluid_dsp_float.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 4FADC23408B4156C00ABE55E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( /System/Library/Frameworks, /Library/Frameworks, ); GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "/Library/Frameworks/MaxAPI.framework/Versions/A/Headers/macho-prefix.h"; GCC_PREPROCESSOR_DEFINITIONS = FLUIDMAX; HEADER_SEARCH_PATHS = ( ../.., ../../../../src, ../../../../include/, /Library/Frameworks/MaxAPI.framework/Versions/A/Headers, /Library/Frameworks/MaxAudioAPI.framework/Versions/A/Headers, ); INFOPLIST_FILE = maxternal.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = "fluidsynth~"; WRAPPER_EXTENSION = mxo; }; name = Debug; }; 4FADC23508B4156C00ABE55E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); FRAMEWORK_SEARCH_PATHS = ( /System/Library/Frameworks, /Library/Frameworks, ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "/Library/Frameworks/MaxAPI.framework/Versions/A/Headers/macho-prefix.h"; GCC_PREPROCESSOR_DEFINITIONS = FLUIDMAX; HEADER_SEARCH_PATHS = ( ../.., ../../../../src, ../../../../include/, /Library/Frameworks/MaxAPI.framework/Versions/A/Headers, /Library/Frameworks/MaxAudioAPI.framework/Versions/A/Headers, ); INFOPLIST_FILE = maxternal.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = "fluidsynth~"; WRAPPER_EXTENSION = mxo; }; name = Release; }; 4FADC23808B4156C00ABE55E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { EXTERNALS_DIR = ../..; FRAMEWORK_SEARCH_PATHS = ( /System/Library/Frameworks, /Library/Frameworks, ); GCC_PREPROCESSOR_DEFINITIONS = ( HAVE_STRING_H, HAVE_STDLIB_H, HAVE_STDIO_H, HAVE_MATH_H, HAVE_STDARG_H, WORDS_BIGENDIAN, ); GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Debug; }; 4FADC23908B4156C00ABE55E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); EXTERNALS_DIR = ../..; FRAMEWORK_SEARCH_PATHS = ( /System/Library/Frameworks, /Library/Frameworks, ); GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 4FADC23308B4156C00ABE55E /* Build configuration list for PBXNativeTarget "fluidsynth~" */ = { isa = XCConfigurationList; buildConfigurations = ( 4FADC23408B4156C00ABE55E /* Debug */, 4FADC23508B4156C00ABE55E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 4FADC23708B4156C00ABE55E /* Build configuration list for PBXProject "fluidmax" */ = { isa = XCConfigurationList; buildConfigurations = ( 4FADC23808B4156C00ABE55E /* Debug */, 4FADC23908B4156C00ABE55E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 089C1669FE841209C02AAC07 /* Project object */; } fluidsynth-1.1.9/bindings/fluidmax/build/osx-macho/maxternal.plist000066400000000000000000000004751322272076000253750ustar00rootroot00000000000000 CFBundleExecutable $(PRODUCT_NAME) CFBundlePackageType iLaX fluidsynth-1.1.9/bindings/fluidmax/build/winxp-vs8/000077500000000000000000000000001322272076000223045ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidmax/build/winxp-vs8/fluidsynth.def000066400000000000000000000000761322272076000251600ustar00rootroot00000000000000;fluidsynth.def LIBRARY fluidsynth~.mxe EXPORTS main fluidsynth-1.1.9/bindings/fluidmax/build/winxp-vs8/fluidsynth.sln000066400000000000000000000015571322272076000252230ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fluidsynth", "fluidsynth.vcproj", "{73069FC3-D989-47DB-B15F-625703EED4DA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {73069FC3-D989-47DB-B15F-625703EED4DA}.Debug|Win32.ActiveCfg = Debug|Win32 {73069FC3-D989-47DB-B15F-625703EED4DA}.Debug|Win32.Build.0 = Debug|Win32 {73069FC3-D989-47DB-B15F-625703EED4DA}.Release|Win32.ActiveCfg = Release|Win32 {73069FC3-D989-47DB-B15F-625703EED4DA}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal fluidsynth-1.1.9/bindings/fluidmax/build/winxp-vs8/fluidsynth.suo000066400000000000000000001230001322272076000252210ustar00rootroot00000000000000ࡱ>   +&!"#$F()*,.-8;456@9:<=>? ABCDHJI0KLMNOPQRoot Entry5LProjInfoExOutliningState1 TaskListUserTasks$5 :Z !"#$^'()*+,-/01`6789;<=>?@A+CDEFGHIJKLMNOPQRSTUVWXY[\]_abcdefghijklmnopqrstuvwxyz{|}~AbLݺ} c:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\fluidsynth.defS]C  LIBCMTD.libfc:\Program Files\Fichiers cVsToolboxService"XmlPackageOptions$DebuggerWatches  DebuggerBreakpoints(  ommuns\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\fluC:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\nC:\Program Files\Microsoft Visual Studio 8\VC\crt\src\|DebuggerExceptions& DebuggerFindSource& DebuggerFindSymbol&DebuggerMemoryWindows,TC:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\|C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\atl\|C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include\ͫ4 $Aͫ49C1ͫ4ͫ4 ͫ4MultiStartupProj=;4{73069FC3-D989-47DB-B15F-625703EED4DA}.dwStartupOpt=;StartupProject=&{73069FC3-D989-47DB-B15F-625703EED4DA};?{73069FC3-D989-ExternalFilesProjectContents:BDocumentWindowPositions0 3GDocumentWindowUserData. lSolutionConfiguration, 47DB-B15F-625703EED4DA}.Release|Win32.fBatchBld=;={73069FC3-D989-47DB-B15F-625703EED4DA}.Debug|Win32.fBatchBld=;4{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3Q C:\Program Files\Fichiers communs\projects\fluidsynth\bindingObjMgrContentsV8" %ClassViewContents$ProjExplorerState$&UnloadedProjects"&s\fluidmax\build\winxp-vs8\fluidsynth.vcprojC:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\fluidsynth.vcprojSource FilesFichiers communs\pC:\Program Files\Fichiers communs\projects\fluidsynth\srcHiddenSlnFolders"OutliningStateDir$. BookmarkState2(TaskListShortcuts$3\fluidsynth_priv.hfC:\maxmspsdk\c74support\max-includes\ext_s$Bookmarks V001.01X 7 `idsynth.defcC:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\LIBCMTD.libgc:\Program Files\Fichiers communs\projects\fluidOutliningState14"4OutliningState5 OutliningState7 %$GOutliningState2 "synth\bindings\fluidmax\build\winxp-vs8\fluidsynth~.mxec:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax.cXX%:tfluidmax_load(t_object *o, Symbol *s, short ac, Atom *at):lB$JC:\Program Files\Fichiers communs\projects\fluidsynth\src\config_win32.h<open> C:\maxmspsdk\c74support\max-includes\ext_proto.h<open> C:\maxmspsdk\c74support\max-includes\ext_support.h<open> C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_dll.c<open> C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_event_priv.h<open> C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.h<open> c:\program files\fichiers communs\projects\fluidsynth\src\fluid_voice.h<open> C:\Program Files\Fichiers communs\projects\fluidsynth\include\fluidsynth.h<open> C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluidsynth_priv.h<open> C:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\ftmax.h<open> d6Gͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ458ͫ4'ͫ4 ͫ4 ͫ4}.dwStartupOpt=; ActiveCfg= Release|Win32;; ActiveCfgluid_voice.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22} \n[n\nz{73069FC3-D989-47DB-B15F-625703EED4DA}|fluidsynth.vcproj|c:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax_fakefuns.c||{8B382828-6202-11D1-8870-0000F87579D2}1234 \n[n\nT{73069FC3-D989-47DB-B15F-625703EED4DA}|fluidsynth.vcproj|c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_tuning.c||{8B382828-6202-11D1-8870-0000F87579D2}12 \n[n\nH{A2Fupport.hc:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_defsfont.cC:\Program Files\Fichiers communs\projects\fluidsynth\src\config_win32.hc:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax_fakefuns.cC:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_event_priv.h c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.cC:\Program Files\Fichiers communs\projects\fluidsynth\include\fluidsynth.h c:\Program Files\F'ichiers communs\projects\fluidsynth\src\fluid_tuning.cC:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.h c:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax.cc:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\build\winxp-vs8\fluidsynth.defC:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\ftmax.h c:\program files\fichiers communs\projects\fluidsynth\src\fluid_voice.hC:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_dll.c bC:\maxmspsdk\c74support\max-includes\ext_proto.hc:\Program Files\Fichiers commuc:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax_fakefuns.c "&(,      !"#$%&(,-./012~   1 D   S   H 8  ; $ @& 7 K9 M ?O V BX a 13:(;75GHJN)Q@LhikFm|}9:E74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\src\config_win32.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}1234560FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\include\fluidsynth.`5ai9jn]p?U9=>: &26 )H*BUVYNZ_`cQdJ7Kj<R, 1'6)-2/3359:</>CDFZ[lmqr1 5>+.cH'(*U,124 8\]_Nalmo3quvx:z~3V!3"@#O@PTW\_brsu5w{|~K%GT@'   .4d5:;=/?OPR-TXY[opx {@@W^] 3 G#%457+9>?A+CGHJa .(P(?)?  LE!34;K=KLN3Qcdf.iz}((G-/h18:= ?D$FT0VY7[_6ae6fj@ko9ptCuy5z~<:A9@7>9B7@NSR!"GHJ5KZ[]K^cdf;guvx0y}~MB3:c:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax.c'%.12<<VW\\;:0-A<4 G[HI1JM6OPT r_st8u9V|;~; 9 ,9.3C5U8Wn?pEC@;;6;8q=s~J::9BBA)*139:;:=9c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_tuning.cZ@304>>?I3JNHOSIT\@]eJfl/OutliningState4 VOutliningState15"&OutliningState13"OutliningState12"bC:\maxmspsdk\c74support\max-includes\ext_proto.h%v&D,F%$R!UXY Y[tJvC:\Program Files\Fichiers communs\projects\fluidsynth\include\fluidsynth.h#,# !",#%&()*.Ode egC  E! N ' ( C:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\ftmax.hz "JL N OutliningState11"!JOutliningState10"POutliningState3 OutliningState9 /@(!(.&%*jmrsu)y'%))+,.ILc no, Symbol *s, short ac, Atom *at):lB$J)_synth.cb5c|}66VA78;9=>E 8} -E0((  447aYZ\_gl)otJv>>cX]3HM%QG6    %  C  E! N ' ( * ?+ b c e Ef s t v Iw   )   = d \n[n\nX{73069FC3-D989-47DB-B15F-625703EED4DA}|fluidsynth.vcproj|c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_defsfont.c||{8B382828-6202-11D1-8870-0000F87579D2}123456} \n[n\nh{73069FC3-D989-47DB-B15F-625703EED4DA}|fluidsynth.vcproj|c:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax.c||{8B382828-6202-11D1-8870-0000F87579D2}123456 \n[n\n{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\maxmspsdk\c74support\max-includes\ext_support.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}12 \n[n\n{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\maxmspsdk\c74support\max-includes\ext_proto.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}123456 \n[n\nL{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\include\fluidsynth.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}12} \n[n\nV{A2FE74E1-B743-11D0-AE  Q   N   >   Z  1 <  1 <  5 3 9 < <  5  9 <  8  9  9 ! * / (0 T+ < C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_event_priv.h4S,157<ACDG @IOC:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_dll.cP%%)(/04a5B#C\!]` `C:\Program Files\Fichiers communs\projects\fluidsynth\src\config_win32.hA}C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.hg122334<BCKNSTV%d+epTq( NXC:\Program Files\Fichiers commuOutliningState8 #TOutliningState6 0OutliningState16"'OutliningState17"ns\projects\fluidsynth\src\fluidsynth_priv.hL' !$%(),-014589<=@ADEHILMPQTUXY\]`ade hilmq}   / .  P 9" "%$%3' ,.-:c:\program files\fichiers communs\projects\fluidsynth\src\fluid_voice.hmȿ&')*1!3=>@ADFH!K#L!WXYmy]z",!9!$%I  fC:\maxmspsdk\c74support\max-includes\ext_support.hns\projects\fluidsynth\src\fluid_defsfont.c} )!1629U:Z\1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\ftmax.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22} \n[n\nP{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_event_priv.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}123456 \n[n\nB{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_dll.c||{8B382828-6202-11D1-8870-0000F87579D2}1234 \n[n\nF{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22} \n[n\nN{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|C:\Program Files\Fichiers communs\projects\fluidsynth\src\fluidsynth_priv.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22} \n[n\nR{73069FC3-D989-47DB-B15F-625703EED4DA}|fluidsynth.vcproj|c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.c||{8B382828-6202-11D1-8870-0000F87579D2}1234 \n[n\nF{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|c:\program files\fichiers communs\projects\fluidsynth\src\f= @ TA N O Q uns\projects\fluidsynth\src\fluid_synth.c||{8B382828-6202-11D1-8870-0000F87579D2}1234 \n[n\nF{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|<MiscFiles>|c:\program files\fichiers communs\projects\fluidsynth\src\fh^Lno       *5,g+ir/t//;,>z,},I , , , 6 c:\Program Files\Fichiers communs\projects\fluidsynth\src\fluid_synth.cb5c|}66VA78;9=>E 8+GFGIX [ ,9 f ,i 5 3 6 V Y v  Q  I y     ' ,* 3 E6 ; ,> M P Z ] g h Nj l x ,z , +@BN Pprojects\fluidsynth\src\fluid+@BN Pprojects\fluidsynth\src\fluidc:\Program Files\Fichiers communs\projects\fluidsynth\bindings\fluidmax\fluidmax.c%:fluidmax_tuning_octave(t_object * #(K'(D  (<013:(;75GHJN)Q@LhikFm|}9:.(P(?)?  LE!34;K=KLN3Qcdf.iz}((GNSR!"GHJ5KZ[]K^cdf;guvx0y}~MB3:/@(!(.&%*jmrsu)y'%))+,.ILc n} -E0((  447aYZ\_gl)otJv>>cX]3HM%QG6    %  C  E! N ' ( * ?+ b c e Ef s t v Iw   )   =   Q   N   >   Z  1 <  1 <  5 3 9 < <  5  9 <  8  9  9 ! * / (0 T+ < = @ TA N O Q 5S W X Z 6\ ` a c 4e i j l 8n r 5s w @y O  " 1  <  =  Q + )- ? @@ D CE I DJ N DO S ET X AY ] B^ b Kd fluidsynth-1.1.9/bindings/fluidmax/build/winxp-vs8/fluidsynth.vcproj000066400000000000000000000204171322272076000257260ustar00rootroot00000000000000 fluidsynth-1.1.9/bindings/fluidmax/config_maxmsp.h000066400000000000000000000003401322272076000223220ustar00rootroot00000000000000#define HAVE_STRING_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STDIO_H 1 #define HAVE_MATH_H 1 #define HAVE_STDARG_H 1 #if defined(__POWERPC__) #define WORDS_BIGENDIAN 1 #endif #define STDIN_FILENO 0 #define STDOUT_FILENO 1 fluidsynth-1.1.9/bindings/fluidmax/fluidmax.c000066400000000000000000001334261322272076000213100ustar00rootroot00000000000000/*************************************************************************************** * * fluidsynth~ * * Fluid Synth soundfont synthesizer for Max/MSP. * * Fluid Synth is written by Peter Hanappe et al. * see http://www.fluidsynth.org/ * * Max/MSP integration by Norbert Schnell ATR IRCAM - Centre Pompidou * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * See file COPYING.LIB for further informations on licensing terms. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. * */ /************************************************************************ * * versions: * (14): adapted to latest code base * (13): ??? * (12): fixed voice stealing * (11): fixed arguments of fluidmax_tuning_octave() (third has to be float) * (10): added micro-tuning methods * (9): bug fix: now polyphony and # of midi channel arguments take effect * (8): added message resample permitting to chose resampling interpolation method * (7): added names for soundfonts (== file name without path and postfix) * (6): added message 'info' * (5): fixed bogus path translation at file loading * */ #define FLUIDMAX_VERSION "01/2009 (14)" #include "ftmax.h" #include "fluidsynth.h" #include "fluid_synth.h" #include "fluid_sfont.h" #include "fluid_chan.h" #include "fluidsynth/version.h" typedef struct { ftmax_dsp_object_t obj; fluid_synth_t *synth; fluid_settings_t *settings; int reverb; int chorus; int mute; void *outlet; } fluidmax_t; static t_messlist *fluidmax_class; static ftmax_symbol_t sym_on = NULL; static ftmax_symbol_t sym_off = NULL; static ftmax_symbol_t sym_undefined = NULL; static ftmax_symbol_t sym_gain = NULL; static ftmax_symbol_t sym_channels = NULL; static ftmax_symbol_t sym_channel = NULL; static ftmax_symbol_t sym_soundfonts = NULL; static ftmax_symbol_t sym_soundfont = NULL; static ftmax_symbol_t sym_presets = NULL; static ftmax_symbol_t sym_preset = NULL; static ftmax_symbol_t sym_reverb = NULL; static ftmax_symbol_t sym_chorus = NULL; static ftmax_symbol_t sym_polyphony = NULL; static ftmax_symbol_t sym_nearest = NULL; static ftmax_symbol_t sym_linear = NULL; static ftmax_symbol_t sym_cubic = NULL; static ftmax_symbol_t sym_sinc = NULL; /*************************************************************** * * generators * */ typedef struct { int index; const char *name; const char *unit; } fluidmax_gen_descr_t; static fluidmax_gen_descr_t fluidmax_gen_info[] = { {0, "startAddrsOffset", "samples"}, {1, "endAddrsOffset", "samples"}, {2, "startloopAddrsOffset", "samples"}, {3, "endloopAddrsOffset", "samples"}, {4, "startAddrsCoarseOffset", "32k samples"}, {5, "modLfoToPitch", "cent fs"}, {6, "vibLfoToPitch", "cent fs"}, {7, "modEnvToPitch", "cent fs"}, {8, "initialFilterFc", "cent 8.176 Hz"}, {9, "initialFilterQ", "cB"}, {10, "modLfoToFilterFc", "cent fs"}, {11, "modEnvToFilterFc", "cent fs "}, {12, "endAddrsCoarseOffset", "32k samples"}, {13, "modLfoToVolume", "cB fs"}, {14, "unused1", ""}, {15, "chorusEffectsSend", "0.1%"}, {16, "reverbEffectsSend", "0.1% "}, {17, "pan", "0.1%"}, {18, "unused2", ""}, {19, "unused3", ""}, {20, "unused4", ""}, {21, "delayModLFO", "timecent"}, {22, "freqModLFO", "cent 8.176 "}, {23, "delayVibLFO", "timecent "}, {24, "freqVibLFO", "cent 8.176"}, {25, "delayModEnv", "timecent"}, {26, "attackModEnv", "timecent "}, {27, "holdModEnv", "timecent"}, {28, "decayModEnv", "timecent"}, {29, "sustainModEnv", "-0.1%"}, {30, "releaseModEnv", "timecent"}, {31, "keynumToModEnvHold", "tcent/key"}, {32, "keynumToModEnvDecay", "tcent/key"}, {33, "delayVolEnv", "timecent"}, {34, "attackVolEnv", "timecent"}, {35, "holdVolEnv", "timecent"}, {36, "decayVolEnv", "timecent"}, {37, "sustainVolEnv", "cB"}, {38, "releaseVolEnv", "timecent "}, {39, "keynumToVolEnvHold", "tcent/key"}, {40, "keynumToVolEnvDecay", "tcent/key "}, {41, "instrument", ""}, {42, "reserved1", ""}, {43, "keyRange MIDI", ""}, {44, "velRange MIDI", ""}, {45, "startloopAddrsCoarseOffset", "samples"}, {46, "keynum MIDI", ""}, {47, "velocity MIDI", ""}, {48, "initialAttenuation", "cB"}, {49, "reserved2", ""}, {50, "endloopAddrsCoarseOffset", "samples"}, {51, "coarseTune", "semitone"}, {52, "fineTune", "cent"}, {53, "sampleId", ""}, {54, "sampleModes", "Bit Flags"}, {55, "reserved3", ""}, {56, "scaleTuning", "cent/key"}, {57, "exclusiveClass", "arbitrary#"}, {58, "unused5", ""}, {59, "unused6", ""}, {60, "unused7", ""}, {61, "unused8", ""}, {62, "unused9", ""}, {63, "unused10", ""} }; /*************************************************************** * * dsp * */ static t_int * fluidmax_perform(t_int *w) { fluidmax_t *self = (fluidmax_t *)(w[1]); t_float *left = (t_float *)(w[2]); t_float *right = (t_float *)(w[3]); int n_tick = (int)(w[4]); if(self->mute == 0) fluid_synth_write_float(self->synth, n_tick, left, 0, 1, right, 0, 1); else { int i; for(i=0; is_n; dsp_add(fluidmax_perform, 4, self, sp[0]->s_vec, sp[1]->s_vec, n_tick); } /*************************************************************** * * load utlilities * */ static char * fluidmax_translate_fullpath(char *maxpath, char *fullpath) { int i; strcpy(fullpath, "/Volumes/"); for(i=0; maxpath[i] != ':'; i++) fullpath[i + 9] = maxpath[i]; /* skip ':' */ i++; strcpy(fullpath + i + 8, maxpath + i); return fullpath; } static ftmax_symbol_t fluidmax_get_stripped_name(const char *fullpath) { char stripped[1024]; int i; for(i=strlen(fullpath)-1; i>=0; i--) { if(fullpath[i] == '/') break; } if(i != 0) i++; strcpy(stripped, fullpath + i); for(i=0; stripped[i] != '\0'; i++) { if((stripped[i] == '.') && (stripped[i + 1] == 's' || stripped[i + 1] == 'S') && (stripped[i + 2] == 'f' || stripped[i + 2] == 'F') && (stripped[i + 3] == '2')) { stripped[i] = '\0'; break; } } return ftmax_new_symbol(stripped); } static ftmax_symbol_t fluidmax_sfont_get_name(fluid_sfont_t *sfont) { return fluidmax_get_stripped_name(fluid_sfont_get_name(sfont)); } static fluid_sfont_t * fluidmax_sfont_get_by_name(fluidmax_t *self, ftmax_symbol_t name) { int n = fluid_synth_sfcount(self->synth); int i; for(i=0; isynth, i); if(fluidmax_sfont_get_name(sfont) == name) return sfont; } return NULL; } static void fluidmax_do_load(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_symbol(at)) { const char *filename = ftmax_symbol_name(ftmax_get_symbol(at)); ftmax_symbol_t name = fluidmax_get_stripped_name(filename); fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, name); if(sf == NULL) { int id = fluid_synth_sfload(self->synth, filename, 0); if(id >= 0) { post("fluidsynth~: loaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); fluid_synth_program_reset(self->synth); outlet_bang(self->outlet); } else error("fluidsynth~: cannot load soundfont from file '%s'", filename); } else { error("fluidsynth~: soundfont named '%s' is already loaded", ftmax_symbol_name(name)); return; } } } static void fluidmax_load_with_dialog(t_object *o, t_symbol *s, short argc, t_atom *argv) { char filename[256]; char maxpath[1024]; char fullpath[1024]; long type; short path; open_promptset("open SoundFont 2 file"); if(open_dialog(filename, &path, &type, 0, 0)) return; if(path_topotentialname(path, filename, maxpath, 0) == 0) { ftmax_atom_t a; ftmax_set_symbol(&a, ftmax_new_symbol(fluidmax_translate_fullpath(maxpath, fullpath))); fluidmax_do_load(o, NULL, 1, &a); } } /*************************************************************** * * user methods * */ static void fluidmax_load(t_object *o, Symbol *s, short ac, Atom *at) { if(ac == 0) defer(o, (method)fluidmax_load_with_dialog, NULL, 0, 0); else { if(ftmax_is_symbol(at)) { ftmax_symbol_t name = ftmax_get_symbol(at); char *string = (char *)ftmax_symbol_name(name); if(string[0] == '/') defer(o, (method)fluidmax_do_load, NULL, ac, at); else { char maxpath[1024]; char fullpath[1024]; short path; long type; ftmax_atom_t a; if(locatefile_extended(string, &path, &type, 0, 0) || path_topotentialname(path, string, maxpath, 0) != 0) { error("fluidsynth~: cannot find file '%s'", string); return; } ftmax_set_symbol(&a, ftmax_new_symbol(fluidmax_translate_fullpath(maxpath, fullpath))); defer(o, (method)fluidmax_do_load, NULL, 1, &a); } } } } static void fluidmax_unload(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int id = ftmax_get_number_int(at); fluid_sfont_t *sf = fluid_synth_get_sfont_by_id(self->synth, id); if(sf != NULL) { ftmax_symbol_t name = fluidmax_sfont_get_name(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) { post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); return; } } error("fluidsynth~: cannot unload soundfont %d", id); } else if (ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == ftmax_new_symbol("all")) { fluid_sfont_t *sf = fluid_synth_get_sfont(self->synth, 0); fluid_synth_system_reset(self->synth); while(sf != NULL) { ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); else error("fluidsynth~: cannot unload soundfont '%s' (id %d)", ftmax_symbol_name(name), id); sf = fluid_synth_get_sfont(self->synth, 0); } } else { fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, sym); if(sf != NULL) { unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) { post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(sym), id); return; } } error("fluidsynth~: cannot unload soundfont '%s'", ftmax_symbol_name(sym)); } } } } static void fluidmax_reload(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int id = ftmax_get_number_int(at); fluid_sfont_t *sf = fluid_synth_get_sfont_by_id(self->synth, id); if(sf != NULL) { if (fluid_synth_sfreload(self->synth, id) >= 0) { post("fluidsynth~: reloaded soundfont '%s' (id %d)", id); return; } error("fluidsynth~: cannot reload soundfont %d", id); } } else if (ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == ftmax_new_symbol("all")) { int n = fluid_synth_sfcount(self->synth); int i; fluid_synth_system_reset(self->synth); for(i=0; isynth, i); ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); if (fluid_synth_sfreload(self->synth, id) >= 0) post("fluidsynth~: reloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); else error("fluidsynth~: cannot reload soundfont '%s' (id %d)", ftmax_symbol_name(name), id); } } else { fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, sym); if(sf != NULL) { unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfreload(self->synth, id) >= 0) { post("fluidsynth~: reloaded soundfont '%s' (id %d)", ftmax_symbol_name(sym), id); return; } } error("fluidsynth~: cannot reload soundfont '%s'", ftmax_symbol_name(sym)); } } } } static void fluidmax_note(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int velocity = 64; int channel = 1; switch(ac) { default: case 3: if(ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } case 2: if(ftmax_is_number(at + 1)) velocity = ftmax_get_number_int(at + 1); case 1: fluid_synth_noteon(self->synth, channel - 1, ftmax_get_number_int(at), velocity); case 0: break; } } } static void fluidmax_list(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_note(o, NULL, ac, at); } static void fluidmax_control_change(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int value = 64; int channel = 1; switch(ac) { default: case 3: if(ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } case 2: if(ftmax_is_number(at + 1)) value = ftmax_get_number_int(at + 1); case 1: fluid_synth_cc(self->synth, channel - 1, ftmax_get_number_int(at), value); case 0: break; } } } static void fluidmax_mod(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 1 && ftmax_is_number(at) && ftmax_is_number(at + 1)) { int param = ftmax_get_number_int(at); float value = ftmax_get_number_float(at + 1); int channel = 1; if(ac > 2 && ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } fluid_synth_set_gen(self->synth, channel - 1, param, value); } } static void fluidmax_pitch_bend(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; double bend = 0.0; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } bend = ftmax_get_number_float(at); if(bend < 0.0) bend = 0.0; else if(bend > 127.0) bend = 127.0; fluid_synth_pitch_bend(self->synth, channel - 1, (int)(bend * 128.0)); } } static void fluidmax_pitch_bend_wheel(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; if(ac > 1 && ftmax_is_number(at + 1)) channel = ftmax_get_number_int(at + 1); fluid_synth_pitch_wheel_sens(self->synth, channel - 1, ftmax_get_number_int(at)); } } static void fluidmax_program_change(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } fluid_synth_program_change(self->synth, channel - 1, ftmax_get_number_int(at)); } } static void fluidmax_bank_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; unsigned int sf_id; unsigned int bank_num; unsigned int prog_num; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); } fluid_synth_bank_select(self->synth, channel - 1, ftmax_get_number_int(at)); fluid_synth_get_program(self->synth, channel - 1, &sf_id, &bank_num, &prog_num); fluid_synth_program_change(self->synth, channel - 1, prog_num); } } static void fluidmax_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; unsigned int bank = 0; unsigned int preset = 0; int channel = 1; switch(ac) { default: case 4: if(ftmax_is_number(at + 3)) channel = ftmax_get_number_int(at + 3); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); case 3: if(ftmax_is_number(at + 2)) preset = ftmax_get_number_int(at + 2); case 2: if(ftmax_is_number(at + 1)) bank = ftmax_get_number_int(at + 1); case 1: if(ftmax_is_number(at)) fluid_synth_program_select(self->synth, channel - 1, ftmax_get_number_int(at), bank, preset); else if(ftmax_is_symbol(at)) { ftmax_symbol_t name = ftmax_get_symbol(at); fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, name); if(sf != NULL) fluid_synth_program_select(self->synth, channel - 1, fluid_sfont_get_id(sf), bank, preset); else error("fluidsynth~ select: cannot find soundfont named '%s'", ftmax_symbol_name(name)); } case 0: break; } } static void fluidmax_reverb(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac == 0) { fluid_synth_set_reverb_on(self->synth, 1); fluid_synth_reset_reverb(self->synth); self->reverb = 1; } else if(ftmax_is_number(at)) { double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); fluid_synth_set_reverb_on(self->synth, 1); self->reverb = 1; switch(ac) { default: case 4: if(ftmax_is_number(at + 3)) width = ftmax_get_number_float(at + 3); case 3: if(ftmax_is_number(at + 2)) damping = ftmax_get_number_float(at + 2); case 2: if(ftmax_is_number(at + 1)) room = ftmax_get_number_float(at + 1); case 1: fluid_synth_set_reverb(self->synth, room, damping, width, ftmax_get_number_float(at)); case 0: break; } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_on) { fluid_synth_set_reverb_on(self->synth, 1); self->reverb = 1; } else if(sym == sym_off) { fluid_synth_set_reverb_on(self->synth, 0); self->reverb = 0; } } } static void fluidmax_chorus(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac == 0) { fluid_synth_set_chorus_on(self->synth, 1); fluid_synth_reset_chorus(self->synth); self->chorus = 1; } else if(ftmax_is_number(at)) { double speed = fluid_synth_get_chorus_speed_Hz(self->synth); double depth = fluid_synth_get_chorus_depth_ms(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); fluid_synth_set_chorus_on(self->synth, 1); self->chorus = 1; switch(ac) { default: case 5: if(ftmax_is_number(at + 4)) nr = ftmax_get_number_int(at + 4); case 4: if(ftmax_is_number(at + 3)) type = ftmax_get_number_int(at + 3); case 3: if(ftmax_is_number(at + 2)) depth = ftmax_get_number_float(at + 2); case 2: if(ftmax_is_number(at + 1)) speed = ftmax_get_number_float(at + 1); case 1: fluid_synth_set_chorus(self->synth, nr, ftmax_get_number_float(at), speed, depth, type); case 0: break; } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_on) { fluid_synth_set_chorus_on(self->synth, 1); self->chorus = 1; } else if(sym == sym_off) { fluid_synth_set_chorus_on(self->synth, 0); self->chorus = 0; } } } static void fluidmax_set_gain(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { float gain = ftmax_get_number_float(at); fluid_synth_set_gain(self->synth, gain); } } static void fluidmax_set_resampling_method(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int ip = ftmax_get_int(at); if(ip == 0) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_NONE); else if(ip < 3) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_LINEAR); else if(ip < 6) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_4THORDER); else fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_7THORDER); } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_nearest) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_NONE); else if(sym == sym_linear) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_LINEAR); else if(sym == sym_cubic) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_4THORDER); else if(sym == sym_sinc) fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_7THORDER); else error("fluidsynth~: undefined resampling method: %s", ftmax_symbol_name(sym)); } } } static void fluidmax_panic(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; fluid_synth_system_reset(self->synth); } static void fluidmax_reset(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int n = fluid_synth_count_midi_channels(self->synth); int i; for (i=0; isynth->channel[i]); } static void fluidmax_mute(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int mute = 1; if(ac > 0 && ftmax_is_number(at)) mute = (ftmax_get_number_int(at) != 0); fluid_synth_system_reset(self->synth); self->mute = mute; } static void fluidmax_unmute(t_object *o) { ftmax_atom_t a; ftmax_set_int(&a, 0); fluidmax_mute(o, NULL, 1, &a); } /* int fluid_synth_count_audio_channels (fluid_synth_t *synth) int fluid_synth_count_audio_groups (fluid_synth_t *synth) int fluid_synth_count_effects_channels (fluid_synth_t *synth) */ static void fluidmax_tuning_octave(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; ftmax_symbol_t name; int tuning_bank = 0; int tuning_prog = 0; double pitch[12]; int i, n; if(ac > 0 && ftmax_is_symbol(at)) { name = ftmax_get_symbol(at); at++; ac--; } n = ac - 2; if(n > 12) n = 12; if(ac > 0 && ftmax_is_number(at)) tuning_bank = ftmax_get_number_int(at) % 128; if(ac > 1 && ftmax_is_number(at + 1)) tuning_prog = ftmax_get_number_int(at) % 128; for(i=0; isynth, tuning_bank, tuning_prog, ftmax_symbol_name(name), pitch); } static void fluidmax_tuning_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int tuning_bank = 0; int tuning_prog = 0; int channel = 1; if(ac > 0 && ftmax_is_number(at)) tuning_bank = ftmax_get_number_int(at) % 128; if(ac > 1 && ftmax_is_number(at + 1)) tuning_prog = ftmax_get_number_int(at + 1) % 128; if(ac > 2 && ftmax_is_number(at + 2)) channel = ftmax_get_number_int(at + 2); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); fluid_synth_select_tuning(self->synth, channel - 1, tuning_bank, tuning_prog); } static void fluidmax_tuning_reset(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int channel = 0; if(ac > 0 && ftmax_is_number(at)) channel = ftmax_get_number_int(at); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); fluid_synth_reset_tuning(self->synth, channel - 1); } /* more tuning ?? fluid_synth_create_key_tuning (fluid_synth_t *synth, int tuning_bank, int tuning_prog, char *name, double *pitch) fluid_synth_tune_notes (fluid_synth_t *synth, int tuning_bank, int tuning_prog, int len, int *keys, double *pitch, int apply) fluid_synth_tuning_iteration_start (fluid_synth_t *synth) fluid_synth_tuning_iteration_next (fluid_synth_t *synth, int *bank, int *prog) fluid_synth_tuning_dump (fluid_synth_t *synth, int bank, int prog, char *name, int len, double *pitch) */ static void fluidmax_version(t_object *o) { post("fluidsynth~, version %s (based on FluidSynth %s)", FLUIDMAX_VERSION, FLUIDSYNTH_VERSION); post(" FluidSynth is written by Peter Hanappe et al. (see fluidsynth.org)"); post(" Max/MSP integration by Norbert Schnell IMTR IRCAM - Centre Pompidou"); } extern fluid_gen_info_t fluid_gen_info[]; static void fluidmax_print(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_soundfonts) { int n = fluid_synth_sfcount(self->synth); int i; if(n > 0) post("fluidsynth~ soundfonts:"); else post("fluidsynth~: no soundfonts loaded"); for(i=0; isynth, i); ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); post(" %d: '%s' (id %d)", i, ftmax_symbol_name(name), id); } } else if(sym == sym_presets) { int n = fluid_synth_sfcount(self->synth); if(n > 0) { if(ac > 1) { fluid_sfont_t *sf = NULL; ftmax_symbol_t name; if(ftmax_is_symbol(at + 1)) { name = ftmax_get_symbol(at + 1); sf = fluidmax_sfont_get_by_name(self, name); } else if(ftmax_is_int(at + 1)) { int id = ftmax_get_int(at + 1); sf = fluid_synth_get_sfont_by_id(self->synth, id); name = fluidmax_sfont_get_name(sf); } if(sf != NULL) { fluid_preset_t preset; fluid_sfont_iteration_start(sf); post("fluidsynth~ presets of soundfont '%s':", ftmax_symbol_name(name)); while(fluid_sfont_iteration_next(sf, &preset) > 0) { char *preset_str = fluid_preset_get_name(&preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); int bank_num = fluid_preset_get_banknum(&preset); int prog_num = fluid_preset_get_num(&preset); post(" '%s': bank %d, program %d", ftmax_symbol_name(preset_name), bank_num, prog_num); } } } else { int i; post("fluidsynth~ presets:"); for(i=0; i<128; i++) { int j; for(j=0; j<128; j++) { fluid_preset_t *preset = NULL; fluid_sfont_t *sf = NULL; int k; for(k=0; ksynth, k); preset = fluid_sfont_get_preset(sf, i, j); if(preset != NULL) break; } if(preset != NULL) { ftmax_symbol_t sf_name = fluidmax_sfont_get_name(sf); char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); post(" '%s': soundfont '%s', bank %d, program %d", ftmax_symbol_name(preset_name), ftmax_symbol_name(sf_name), i, j); } } } } } else error("fluidsynth~: no soundfonts loaded"); } else if(sym == sym_channels) { int n = fluid_synth_count_midi_channels(self->synth); int i; post("fluidsynth~ channels:"); for(i=0; isynth, i); if(preset != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); unsigned int sf_id; unsigned int bank_num; unsigned int prog_num; fluid_sfont_t *sf; fluid_synth_get_program(self->synth, i, &sf_id, &bank_num, &prog_num); sf = fluid_synth_get_sfont_by_id(self->synth, sf_id); post(" %d: soundfont '%s', bank %d, program %d: '%s'", i + 1, ftmax_symbol_name(fluidmax_sfont_get_name(sf)), bank_num, prog_num, ftmax_symbol_name(preset_name)); } else post(" channel %d: no preset", i + 1); } } else if(sym == ftmax_new_symbol("generators")) { int channel = 1; int n = GEN_LAST; int i; if(ac > 1 && ftmax_is_number(at + 1)) channel = ftmax_get_number_int(at + 1); if(channel < 1) channel = 1; else if(channel > fluid_synth_count_midi_channels(self->synth)) channel = fluid_synth_count_midi_channels(self->synth); post("fluidsynth~ generators of channel %d:", channel); for(i=0; isynth, channel - 1, i); double min = fluid_gen_info[i].min; double max = fluid_gen_info[i].max; post(" %d '%s': %s %g [%g ... %g] (%s)", i, name, (incr >= 0)? "": "-" , fabs(incr), min, max, unit); } } else if(sym == sym_gain) { double gain = fluid_synth_get_gain(self->synth); post("gain: %g", gain); } else if(sym == sym_reverb) { double level = fluid_synth_get_reverb_level(self->synth); double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); if(self->reverb != 0) { post("fluidsynth~ current reverb parameters:"); post(" level: %f", level); post(" room size: %f", room); post(" damping: %f", damping); post(" width: %f", width); } else post("fluidsynth~: reverb off"); } else if(sym == sym_chorus) { if(self->chorus != 0) { double level = fluid_synth_get_chorus_level(self->synth); double speed = fluid_synth_get_chorus_speed_Hz(self->synth); double depth = fluid_synth_get_chorus_depth_ms(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); post("fluidsynth~ current chorus parameters:"); post(" level: %f", level); post(" speed: %f Hz", speed); post(" depth: %f msec", depth); post(" type: %d (%s)", type, type? "triangle": "sine"); post(" %d units", nr); } else post("fluidsynth~: chorus off"); } } } } static void fluidmax_info(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_soundfonts) { int n = fluid_synth_sfcount(self->synth); int i; for(i=0; isynth, i); unsigned int id = fluid_sfont_get_id(sf); ftmax_atom_t a[2]; ftmax_set_int(a, i); ftmax_set_symbol(a + 1, fluidmax_sfont_get_name(sf)); ftmax_set_int(a + 2, id); outlet_anything(self->outlet, sym_soundfont, 3, a); } } else if(sym == sym_presets) { int n = fluid_synth_sfcount(self->synth); if(n > 0) { if(ac > 1) { fluid_sfont_t *sf = NULL; ftmax_symbol_t sf_name; if(ftmax_is_symbol(at + 1)) { sf_name = ftmax_get_symbol(at + 1); sf = fluidmax_sfont_get_by_name(self, sf_name); } else if(ftmax_is_int(at + 1)) { int id = ftmax_get_int(at + 1); sf = fluid_synth_get_sfont_by_id(self->synth, id); sf_name = fluidmax_sfont_get_name(sf); } if(sf != NULL) { fluid_preset_t preset; fluid_sfont_iteration_start(sf); while(fluid_sfont_iteration_next(sf, &preset) > 0) { char *preset_str = fluid_preset_get_name(&preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); int bank_num = fluid_preset_get_banknum(&preset); int prog_num = fluid_preset_get_num(&preset); ftmax_atom_t a[4]; ftmax_set_symbol(a , preset_name); ftmax_set_symbol(a + 1, sf_name); ftmax_set_int(a + 2, bank_num); ftmax_set_int(a + 3, prog_num); outlet_anything(self->outlet, sym_preset, 4, a); } } } else { int i; for(i=0; i<128; i++) { int j; for(j=0; j<128; j++) { fluid_preset_t *preset = NULL; fluid_sfont_t *sf = NULL; int k; for(k=0; ksynth, k); preset = fluid_sfont_get_preset(sf, i, j); if(preset != NULL) break; } if(preset != NULL) { ftmax_symbol_t sf_name = fluidmax_sfont_get_name(sf); char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); ftmax_atom_t a[4]; ftmax_set_symbol(a , preset_name); ftmax_set_symbol(a + 1, sf_name); ftmax_set_int(a + 2, i); ftmax_set_int(a + 3, j); outlet_anything(self->outlet, sym_preset, 4, a); } } } } } else error("fluidsynth~ info: no soundfonts loaded"); } else if(sym == sym_channels) { int n = fluid_synth_count_midi_channels(self->synth); int i; for(i=0; isynth, i); if(preset != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); unsigned int sf_id, bank_num, prog_num; fluid_sfont_t *sf; ftmax_atom_t a[5]; fluid_synth_get_program(self->synth, i, &sf_id, &bank_num, &prog_num); sf = fluid_synth_get_sfont_by_id(self->synth, sf_id); ftmax_set_int(a, i + 1); ftmax_set_symbol(a + 1, fluidmax_sfont_get_name(sf)); ftmax_set_int(a + 2, bank_num); ftmax_set_int(a + 3, prog_num); ftmax_set_symbol(a + 4, preset_name); outlet_anything(self->outlet, sym_channel, 5, a); } else { ftmax_atom_t a[2]; ftmax_set_int(a, i + 1); ftmax_set_symbol(a + 1, sym_undefined); outlet_anything(self->outlet, sym_channel, 2, a); } } } else if(sym == sym_gain) { ftmax_atom_t a; double gain = fluid_synth_get_gain(self->synth); ftmax_set_float(&a, gain); outlet_anything(self->outlet, sym_channel, 1, &a); } else if(sym == sym_reverb) { if(self->reverb != 0) { double level = fluid_synth_get_reverb_level(self->synth); double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); ftmax_atom_t a[4]; ftmax_set_float(a, level); ftmax_set_float(a + 1, room); ftmax_set_float(a + 2, damping); ftmax_set_float(a + 3, width); outlet_anything(self->outlet, sym_reverb, 4, a); } else { ftmax_atom_t a; ftmax_set_symbol(&a, sym_off); outlet_anything(self->outlet, sym_reverb, 1, &a); } } else if(sym == sym_chorus) { if(self->chorus != 0) { double level = fluid_synth_get_chorus_level(self->synth); double speed = fluid_synth_get_chorus_speed_Hz(self->synth); double depth = fluid_synth_get_chorus_depth_ms(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); ftmax_atom_t a[5]; ftmax_set_float(a, level); ftmax_set_float(a + 1, speed); ftmax_set_float(a + 2, depth); ftmax_set_int(a + 3, type); ftmax_set_int(a + 4, nr); outlet_anything(self->outlet, sym_chorus, 5, a); } else { ftmax_atom_t a; ftmax_set_symbol(&a, sym_off); outlet_anything(self->outlet, sym_chorus, 1, &a); } } else if(sym == sym_polyphony) { int polyphony = fluid_synth_get_polyphony(self->synth); ftmax_atom_t a; ftmax_set_int(&a, polyphony); outlet_anything(self->outlet, sym_polyphony, 1, &a); } } } } /*************************************************************** * * class * */ static void * fluidmax_new(Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)newobject(fluidmax_class); int polyphony = 256; int midi_channels = 16; self->outlet = outlet_new(self, "anything"); dsp_setup((t_pxobject *)self, 0); outlet_new(self, "signal"); outlet_new(self, "signal"); self->synth = NULL; self->settings = new_fluid_settings(); self->reverb = 0; self->chorus = 0; self->mute = 0; if(ac > 0 && ftmax_is_number(at)) { polyphony = ftmax_get_number_int(at); ac--; at++; } if(ac > 0 && ftmax_is_number(at)) { midi_channels = ftmax_get_number_int(at); ac--; at++; } if(ac > 0 && ftmax_is_symbol(at)) { fluidmax_load((t_object *)self, NULL, 1, at); } if(self->settings != NULL) { fluid_settings_setint(self->settings, "synth.midi-channels", midi_channels); fluid_settings_setint(self->settings, "synth.polyphony", polyphony); fluid_settings_setnum(self->settings, "synth.gain", 0.600000); fluid_settings_setnum(self->settings, "synth.sample-rate", sys_getsr()); self->synth = new_fluid_synth(self->settings); if(self->synth != NULL) { fluid_synth_set_reverb_on(self->synth, 0); fluid_synth_set_chorus_on(self->synth, 0); if(ac > 0 && ftmax_is_symbol(at)) fluidmax_load((t_object *)self, NULL, ac, at); return self; } delete_fluid_settings(self->settings); } error("fluidsynth~: cannot create FluidSynth core"); return NULL; } static void fluidmax_free(t_pxobject *o) { fluidmax_t *self = (fluidmax_t *)o; if(self->settings != NULL ) delete_fluid_settings(self->settings); if(self->synth != NULL ) delete_fluid_synth(self->synth); dsp_free(o); } int main(void) { setup(&fluidmax_class, (method)fluidmax_new, (method)fluidmax_free, (short)sizeof(fluidmax_t), 0, A_GIMME, 0); dsp_initclass(); addmess((method)fluidmax_dsp, "dsp", A_CANT, 0); addmess((method)fluidmax_version, "version", 0); addmess((method)fluidmax_print, "print", A_GIMME, 0); addmess((method)fluidmax_load, "load", A_GIMME, 0); addmess((method)fluidmax_unload, "unload", A_GIMME, 0); addmess((method)fluidmax_reload, "reload", A_GIMME, 0); addmess((method)fluidmax_info, "info", A_GIMME, 0); addmess((method)fluidmax_panic, "panic", A_GIMME, 0); addmess((method)fluidmax_reset, "reset", A_GIMME, 0); addmess((method)fluidmax_mute, "mute", A_GIMME, 0); addmess((method)fluidmax_unmute, "unmute", 0); /*addmess((method)fluidmax_tuning_keys, "tuning-keys", A_GIMME, 0);*/ addmess((method)fluidmax_tuning_octave, "tuning-octave", A_GIMME, 0); addmess((method)fluidmax_tuning_select, "tuning-select", A_GIMME, 0); addmess((method)fluidmax_tuning_reset, "tuning-reset", A_GIMME, 0); addmess((method)fluidmax_reverb, "reverb", A_GIMME, 0); addmess((method)fluidmax_chorus, "chorus", A_GIMME, 0); addmess((method)fluidmax_set_gain, "gain", A_GIMME, 0); addmess((method)fluidmax_set_resampling_method, "resample", A_GIMME, 0); addmess((method)fluidmax_note, "note", A_GIMME, 0); addmess((method)fluidmax_list, "list", A_GIMME, 0); addmess((method)fluidmax_control_change, "control", A_GIMME, 0); addmess((method)fluidmax_mod, "mod", A_GIMME, 0); addmess((method)fluidmax_pitch_bend, "bend", A_GIMME, 0); addmess((method)fluidmax_pitch_bend_wheel, "wheel", A_GIMME, 0); addmess((method)fluidmax_program_change, "program", A_GIMME, 0); addmess((method)fluidmax_bank_select, "bank", A_GIMME, 0); addmess((method)fluidmax_select, "select", A_GIMME, 0); sym_on = ftmax_new_symbol("on"); sym_off = ftmax_new_symbol("off"); sym_undefined = ftmax_new_symbol("undefined"); sym_gain = ftmax_new_symbol("gain"); sym_channels = ftmax_new_symbol("channels"); sym_channel = ftmax_new_symbol("channel"); sym_soundfonts = ftmax_new_symbol("soundfonts"); sym_soundfont = ftmax_new_symbol("soundfont"); sym_presets = ftmax_new_symbol("presets"); sym_preset = ftmax_new_symbol("preset"); sym_reverb = ftmax_new_symbol("reverb"); sym_chorus = ftmax_new_symbol("chorus"); sym_polyphony = ftmax_new_symbol("polyphony"); sym_nearest = ftmax_new_symbol("nearest"); sym_linear = ftmax_new_symbol("linear"); sym_cubic = ftmax_new_symbol("cubic"); sym_sinc = ftmax_new_symbol("sinc"); fluidmax_version(NULL); return 0; } fluidsynth-1.1.9/bindings/fluidmax/fluidmax.mcp000066400000000000000000004316541322272076000216510ustar00rootroot00000000000000cool('( tCodeWarrior Project&=Sgz  %8M_x .@Sgz !"#$%&'-(E)[*p+,-./012+3@4W5k6789:;<"=<>L?`@uABCDEFGHI7JRKqLMNOPQR7STTqUVWXYZ$[C\f]^_`a b c 4d Oe kf g h i j k l &m Cn `o p q r s t u 5v Uw tx y z { | } /~ R s  @ X t  A\x&<Wr4Qn 'B`,Ok,Mm!@_y*Fc Af&B_}4Yt:Xs:V2Oi*Jj      #:Y|9Rj !"#5$O%h&'()*+ , /- I. `/ ~0 1 2 3 4!5!,6!I7!\8!s9!:!;!<!=!>"?"@"=A"]B"sC"D"E"F"G"H"I#J#K#-L#?M#UN#jO#P#Q#R#S#T#U$V$ W$3X$IY$aZ$t[$\$]$^$_$`$a%b%,c%Dd%\e%jf%|g%h%i%j%k%l%m&n&o&9p&Jq&\r&qs&t&u&v&w&x&y'z'#{';|'R}'i~'{''''(((-(E(_(t((((()))<)V)p)))))))**)*F*c*v******+++-+?+U+j++++++,, ,3,I,a,t,,,,,,--,-D-\-j-|------...9.Jryv{|zswu}~xqtIPMRSQJNLTUVWXYZ[\]^_`abcdOeHKfghijk   !"#mtqvwunrpxyz{|}~slo%,)./-&*(0123456789:;<=>?@+A$'BCDEFG      !"$%&'()*+,#-./0123456789:;<=>?@ABDKHMNLEIGOPRSTUVWXYZQ[\]^_`abcdefgJhCFijklmnopxMaxLib.stubsdsps~.csdsps~.rsrcMaxAudioLib.stubMSL_Runtime_PPC.LibCarbonLibMathLibdebugreleasedebug-classicrelease-classic../build-mac/sdsps~Lib Import PPCRezBalloon HelpMW C/C++ PPCPPCAsmXCOFF Import PPCPEF Import PPC../build-classic/sdsps~MacOS PPC LinkerSource TreesAccess PathsDebugger RuntimeDebugger TargetCustom KeywordsTarget SettingsFile MappingsBuild ExtrasC/C++ CompilerC/C++ WarningsOutput FlagsPackager PanelPPC CodeGenPPC DisassemblerPPC Global OptimizerPPC LinkerPPC PEFPPC ProjectPPCAsm PanelProperty ListRez Compilersdif-mem.csdif.csdsps.cslm1.csps_list.cpsola_sdif_data.cStdCLibStub../build-mac/Merge OutProperty List CompilerCopy To PackagePerl ToolShell ToolMacOS MergeMacOS Merge PanelMachO ImporterMW C/C++ PPC Mac OS XMacOS X PPC LinkerPPC CodeGen Mach-OPPC Mac OS X LinkerPPC Mac OS X Projectpsgrain.cpsgrain.hpsgroup.cpsgroup.hftm.cftm.hintphase.hfftab.cfftab.hftmsdif.cftmsdif.h../build-mac/psgroup~../build-classic/psgroup~../../../../../Library/Application Support/Cycling '74/externals/psgroup~../../MAXrepertoire/build-mac/psgroup~random.c../build-mac/sdif.psgroup~../build-classic/sdif.psgroup~StdCLibgpool.cgpool.hpagsemble.cpagsolo.ccubic.c../../build-mac/sdif.pagsemble~../../build-mac/sdif.psgroup~../../build-classic/sdif.psgroup~../../build-mac/sdif.pagsolo~sdif.pagsemble~sdif.pagsolo~sdif.sogs~sogs~../../build-mac/sogs~sogs.c../../build-classic/sogs~classicMSL_All_PPC.LibInterfaceLibrogs.crogs~../../build-mac/rogs~fluidsynth.hfluid_chan.cfluid_chorus.cfluid_conv.cfluid_defsfont.cfluid_event.cfluid_gen.cfluid_hash.cfluid_io.cfluid_list.cfluid_midi.cfluid_mod.cfluid_ramsfont.cfluid_rev.cfluid_settings.cfluid_strtok.cfluid_synth.cfluid_tuning.cfluid_voice.cfluidmax.c../../build-mac/fluid~../../build-classic/fluid~../build-mac/fluid~fluid_sys.cfluidmax_fakefuns.c../build-mac/fluidsynth~../build-classic/fluidsynth~OS X Volumefluidsynth~fluidsynth~OS9MacHeadersCarbon.hMacHeadersCarbon.h bstart@.\0L}Ya|gWZX_MKP~2JO b4V FG{hijklmnopqrstuvwxyzCDHIRSdE 978;c: !TU5"<#$%=>&'()3?@AB0N *ef+,[\]-6.`^/Q1]AMENU2PPob>RidLJaeteVversbcfrgz e())3[3#3 Inline Depth ditl_frontendEnglishGenerated cfrgmainstart (%P#4HRZ ` h v $1BRbr !"#$%&' ()%*3+@,K-R.Z/a0l1~23456789: ; <3=F>Z?o@yABCDEFGHIJKLMLNsO|PQRSTUVWX Y*ZL[j\z]^_`abcdefghi%j2kAlNm_nmoypqrstuvwxyz{|*}A~\p|@ :../../includeinclude../../srcnth/src:@System/Library/CFMSupport/StubLibraries@ /Applications/MaxMSP 4.5/MaxMSP Runtime 4.5 MacOS PPC Linkerdebug./build-macacire/build-maction Support/Cycling '74/externalsojecmainno connections availableEADC\BApC"ApC"* ASY@HWt@(gicnLINKJ endsobjdIsclvjTRGTbegsdoplTgtLobjdFCellTRGTZ.endsendsendsend. Merge Out&p&&հ&&@&@LLPH @LH\\XttPH00d\HP&ːp&(8&ˈ HH&ːH 8&( 8&(Ld 8&( | 8&(d ????APPLa.rsrc8&( 8&ʰ 8&(Pdj^v<<  88  PPtt&ʘ&ʐ,PPhh D&ʈ D&ʈ D&ʈ:DLGXckidProjWSPC????APPL::sdsps~ablemax2iLaF@Executable.rsrc:./Asdsps~a.exe????MEXE@a.rsrc:./a.exea.rsrc JavaClasses.jarZIP MWZPP'CODE' 'DATA' 'PICT'h/b[   "#$%&'( )*+(MacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRCRez`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrcRez`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrcRez`NoneMMPr@.plPerl ToolPerl.pshShell Tool.shShell ToolJava Linker RSRCJavaTEXT.htmlJavaTEXT.mfJavarsrcJava.auJAR ImporterJava@.classMW JavaJava.gifJAR ImporterJava@.jarMW JavaJava.javaMW JavaJava.plPerl ToolPerl.pshShell Tool.shShell Tool.zipMW JavaJavaMach-O PPC Linker0APPL`Appl`MDYLLib Import Mach-OC/C++MLIBLib Import Mach-OC/C++MMLBLib Import Mach-OC/C++MPLFLib Import Mach-OC/C++MWCD`RSRC`TEXT.arrTEXT.axpTEXT.cMW C/C++ MachPPCC/C++TEXT.c++MW C/C++ MachPPCC/C++TEXT.ccMW C/C++ MachPPCC/C++TEXT.cpMW C/C++ MachPPCC/C++TEXT.cppMW C/C++ MachPPCC/C++TEXT.expTEXT.hMW C/C++ MachPPCC/C++TEXT.h++MW C/C++ MachPPCC/C++TEXT.hppMW C/C++ MachPPCC/C++TEXT.lcfTEXT.mMW C/C++ MachPPCC/C++TEXT.mmMW C/C++ MachPPCC/C++TEXT.pchMW C/C++ MachPPCC/C++TEXT.pch++MW C/C++ MachPPCC/C++TEXT.pchmMW C/C++ MachPPCC/C++TEXT.pchmmMW C/C++ MachPPCC/C++TEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezTEXT.wkedocu`rsrc`.aLib Import Mach-OC/C++.docP.dylibLib Import Mach-OC/C++.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.nibCopy To PackageP.oLib Import Mach-OC/C++.plPerl ToolPerl.plistCopy To Package.ppob`.pshShell Tool.rsrc`.shShell Tool.stringsCopy To Package.tiffCopy To PackagePMacOS MergeAPPLRez`ApplRez`RSRCRez`TEXT.bhBalloon HelpTEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezappeRez`rsrcRez`shlbRez.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.nibCopy To PackageP.plPerl ToolPerl.plistCopy To Package.pshShell Tool.shShell Tool.stringsCopy To Package.tiffCopy To PackagePMacOS X PPC Linker0APPL`Appl`MDYLMachO ImporterC/C++MLIBMachO ImporterC/C++MMLBMachO ImporterC/C++MPLFMachO ImporterC/C++MWCD`RSRC`TEXT.arrTEXT.axpTEXT.cMW C/C++ PPC Mac OS XC/C++TEXT.c++MW C/C++ PPC Mac OS XC/C++TEXT.ccMW C/C++ PPC Mac OS XC/C++TEXT.cpMW C/C++ PPC Mac OS XC/C++TEXT.cppMW C/C++ PPC Mac OS XC/C++TEXT.expTEXT.hMW C/C++ PPC Mac OS XC/C++TEXT.h++MW C/C++ PPC Mac OS XC/C++TEXT.hppMW C/C++ PPC Mac OS XC/C++TEXT.lcfTEXT.mMW C/C++ PPC Mac OS XC/C++TEXT.mmMW C/C++ PPC Mac OS XC/C++TEXT.pchMW C/C++ PPC Mac OS XC/C++TEXT.pch++MW C/C++ PPC Mac OS XC/C++TEXT.pchmMW C/C++ PPC Mac OS XC/C++TEXT.pchmmMW C/C++ PPC Mac OS XC/C++TEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezTEXT.wkedocu`rsrc`.aMachO ImporterC/C++.docP.dylibMachO ImporterC/C++.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.libMachO ImporterC/C++.nibCopy To PackageP.plPerl ToolPerl.plistCopy To Package.ppob`.pshShell Tool.rsrc`.shShell Tool.stringsCopy To Package.tiffCopy To PackagePWin32 x86 LinkerTEXT.cMW C/C++ x86C/C++TEXT.c++MW C/C++ x86C/C++TEXT.ccMW C/C++ x86C/C++TEXT.cpMW C/C++ x86C/C++TEXT.cppMW C/C++ x86C/C++TEXT.defTEXT.hMW C/C++ x86C/C++TEXT.h++MW C/C++ x86C/C++TEXT.hppMW C/C++ x86C/C++TEXT.ordTEXT.pchMW C/C++ x86C/C++TEXT.pch++MW C/C++ x86C/C++TEXT.rcMW WinRCBalloon HelpiLIBLib Import x86iOBJObj Import x86.aLib Import x86.dllDLL Import x86.docP.exeDLL Import x86.libLib Import x86.oObj Import x86.objObj Import x86.plPerl ToolPerl.pshShell Tool.resWinRes ImportBalloon Help.shShell Tool  fluidsynth~o~e~max2iLaF:???? fluidsynth~o~e~H(HHPPΠΨΰP-ܡ)@";ԡ'ئRK"7Ƞ"8 b'̡';'SĠbbtL10c";'K̠"7H"4:Ġ"3$"7@"8,*"6"5 "6"7"7)..\"70">x.8";"7d)'<"<<'4cH">|&Ƞb"<0Info.plist"5KĠ"6Ȧ8";t";Ц<<b";0"B|b"6̠"6ЦD /"7"<`.40(8B-Ƞ": "3";̠"B.D<"7"B( "7$;L"; )<'"6ؠ";Ġ"7"6:"70DK4Ԡ"9"94/p"9"</4($(0($($($(%(%:plst/@(.(.(. (-(-`(-(,(,@(+(+0(*()()0((((P((('('`('(&(%(%p(% ($($($0(#(#(#0("("( noname.exe,3IMNOPRSUWXYZ[\^_`b,3I|MjNkOlPmRoSpUrWtXuYvZw[x\y^z_{`b2`a``  ZROOTFILE7FILEZFILE/FILEFILEGRUP fluidsynthFILE;FILEFILESFILETFILECFILEEFILEFFILEGFILEHFILEVFILEJFILEKFILELFILEMFILEYJavaClasses.jarZIP MWZPP MWZP]fMENU2PPob>RidLJaeteVversbcfrgz e())3[3#3 Inline Depth ditl_frontendEnglishGenerated cfrge b@ Uf b`Main-ClassAuto-GeneratedRez`rsrcRez`shlbRez.gifCopy To PackageP.icnsCopy To PackageP.jpgMRJApplicationMSIEhttp://java.sun.com/products/jdk/1.1/docs/api/main/7;j<k>mCrEtFuGvHwJyKzL{M|SoTpVxWlYZ2YXQQQ  /7;<>CEFGHJKLMSTVWYZMacHeadersCarbon.hMacHeadersCarbon.hstartstart@@ :../../includeinclude../../srcnth/src:@System/Library/CFMSupport/StubLibraries@  MacOS PPC Linkerreleases~o~./build-macacMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRCRez`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrcRez`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrcRez`NoneMMPr@.plPerl ToolPerl.pshShell Tool.shShell ToolJava Linker RSRCJavaTEXT.htmlJavaTEXT.mfJavarsrcJava.auJAR ImporterJava@.classMW JavaJava.gifJAR ImporterJava@.jarMW JavaJava.javaMW JavaJava.plPerl ToolPerl.pshShell Tool.shShell Tool.zipMW JavaJavaMach-O PPC Linker0APPL`Appl`MDYLLib Import Mach-OC/C++MLIBLib Import Mach-OC/C++MMLBLib Import Mach-OC/C++MPLFLib Import Mach-OC/C++MWCD`RSRC`TEXT.arrTEXT.axpTEXT.cMW C/C++ MachPPCC/C++TEXT.c++MW C/C++ MachPPCC/C++TEXT.ccMW C/C++ MachPPCC/C++TEXT.cpMW C/C++ MachPPCC/C++TEXT.cppMW C/C++ MachPPCC/C++TEXT.expTEXT.hMW C/C++ MachPPCC/C++TEXT.h++MW C/C++ MachPPCC/C++TEXT.hppMW C/C++ MachPPCC/C++TEXT.lcfTEXT.mMW C/C++ MachPPCC/C++TEXT.mmMW C/C++ MachPPCC/C++TEXT.pchMW C/C++ MachPPCC/C++TEXT.pch++MW C/C++ MachPPCC/C++TEXT.pchmMW C/C++ MachPPCC/C++TEXT.pchmmMW C/C++ MachPPCC/C++TEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezTEXT.wkedocu`rsrc`.aLib Import Mach-OC/C++.docP.dylibLib Import Mach-OC/C++.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.nibCopy To PackageP.oLib Import Mach-OC/C++.plPerl ToolPerl.plistCopy To Package.ppob`.pshShell Tool.rsrc`.shShell Tool.stringsCopy To Package.tiffCopy To PackagePMacOS MergeAPPLRez`ApplRez`RSRCRez`TEXT.bhBalloon HelpTEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezappeRez`rsrcRez`shlbRez.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.nibCopy To PackageP.plPerl ToolPerl.plistCopy To Package.pshShell Tool.shShell Tool.stringsCopy To Package.tiffCopy To PackagePMacOS X PPC Linker0APPL`Appl`MDYLMachO ImporterC/C++MLIBMachO ImporterC/C++MMLBMachO ImporterC/C++MPLFMachO ImporterC/C++MWCD`RSRC`TEXT.arrTEXT.axpTEXT.cMW C/C++ PPC Mac OS XC/C++TEXT.c++MW C/C++ PPC Mac OS XC/C++TEXT.ccMW C/C++ PPC Mac OS XC/C++TEXT.cpMW C/C++ PPC Mac OS XC/C++TEXT.cppMW C/C++ PPC Mac OS XC/C++TEXT.expTEXT.hMW C/C++ PPC Mac OS XC/C++TEXT.h++MW C/C++ PPC Mac OS XC/C++TEXT.hppMW C/C++ PPC Mac OS XC/C++TEXT.lcfTEXT.mMW C/C++ PPC Mac OS XC/C++TEXT.mmMW C/C++ PPC Mac OS XC/C++TEXT.pchMW C/C++ PPC Mac OS XC/C++TEXT.pch++MW C/C++ PPC Mac OS XC/C++TEXT.pchmMW C/C++ PPC Mac OS XC/C++TEXT.pchmmMW C/C++ PPC Mac OS XC/C++TEXT.plcProperty List CompilerProperty ListTEXT.plocProperty List CompilerProperty ListTEXT.rRezRezTEXT.wkedocu`rsrc`.aMachO ImporterC/C++.docP.dylibMachO ImporterC/C++.gifCopy To PackageP.icnsCopy To PackageP.jpgCopy To PackageP.libMachO ImporterC/C++.nibCopy To PackageP.plPerl ToolPerl.plistCopy To Package.ppob`.pshShell Tool.rsrc`.shShell Tool.stringsCopy To Package.tiffCopy To PackagePWin32 x86 LinkerTEXT.cMW C/C++ x86C/C++TEXT.c++MW C/C++ x86C/C++TEXT.ccMW C/C++ x86C/C++TEXT.cpMW C/C++ x86C/C++TEXT.cppMW C/C++ x86C/C++TEXT.defTEXT.hMW C/C++ x86C/C++TEXT.h++MW C/C++ x86C/C++TEXT.hppMW C/C++ x86C/C++TEXT.ordTEXT.pchMW C/C++ x86C/C++TEXT.pch++MW C/C++ x86C/C++TEXT.rcMW WinRCBalloon HelpiLIBLib Import x86iOBJObj Import x86.aLib Import x86.dllDLL Import x86.docP.exeDLL Import x86.libLib Import x86.oObj Import x86.objObj Import x86.plPerl ToolPerl.pshShell Tool.resWinRes ImportBalloon Help.shShell Tool main ({(ƀI|ƀk)u({(I|k,ǐ)u*ǐ)u)ǐ)uǐ)uǐԐǐ@ǐpǐǐ $K0Ѡ)vt[@( U , UX0,(0@`8pp@UHU8UU@@ 8Upp U,,@444pp( UURUU(( G{r 8UJJhJpJhJJ`exceptionvector::insert length error!std::exception!!std::logic_error!!std::length_error!!vector::assign length error!std::exception!!std::logic_error!!std::length_error!!DITL InterfaceLibAlicationZoneGetZone HandleZoneSetZone SystemZone{Main-ClassAuto-GeneratedTEXT.c++MW C/C++ MachPPCC/C++TEXT.ccMW C/C++ MachPPCC/C++TEXT.cpMW C/C++ MachPPCC/C++TEXT.cppMW C/C++ MachPPCC/C++TEXT.expMRJApplicationWARZmacosxmacosxMSIEhttp://java.sun.com/products/jdk/1.1/docs/api/ Merge Outddpddհdd@d@LLPH @LH\\XttPH00d\HPhppr8hh HHhpH 8r 8rLd 8r | 8rd ????APPLa.rsrc8r 8g 8rPdj^v<<  88  PPttgxgp,PPhh Dgh Dgh Dgh:DLGXckidProjWSPC????APPL::sdsps~ablemax2iLaF@Executable.rsrc:./Asdsps~a.exe????MEXE@a.rsrc:./a.exea.rsrc  fluidsynth~o~e~max2iLaF:???? fluidsynth~o~e~0 P UW !c &5 P  Ujioph'g pfeZ)H(HHPPΠΨΰP-ܡ)@";ԡ'ئRK"7Ƞ"8 b'̡';'SĠbbtL10c";'K̠"7H"4:Ġ"3$"7@"8,*"6"5 "6"7"7)..\"70">x.8";"7d)'<"<<'4cH">|&Ƞb"<0Info.plistd"5KĠ"6Ȧ8";t";Ц<<b";0"B|b"6̠"6ЦD /"7"<`.40(8B-Ƞ": "3";̠"B.D<"7"B( "7$;L"; )<'"6ؠ";Ġ"7"6:"70DK4Ԡ"9"94/p"9"</4 t8 d t t t( t u3:plst/@ a. a. a.  a- a-` a- a, a,@ a+ a+0 a* a) a)0 a( a(P a( a' a'` a' a& a% a%p a%  a$ a$ a$0 a# a# a#0 a" a" P'CODE' 'DATA' 'PICT' noname.exeMSIEhttp://java.sun.com/products/jdk/1.1/docs/api/debug:Source Treesdebug:Access Pathsdebug:Debugger Runtimedebug:Target Settingsdebug:File Mappingsdebug:Build Extrasdebug:Debugger Targetdebug:Remote Debugdebug:Auto-targetdebug:Custom Keywordsdebug:C/C++ Compilerdebug:C/C++ Warningsdebug:Java Command Linedebug:MacOS Merge Paneldebug:Output Flagsdebug:Packager Paneldebug:PPC CodeGendebug:PPC CodeGen Mach-Odebug:PPC Disassemblerdebug:PPC Global Optimizerdebug:PPC Linkerdebug:PPC Mac OS X Linkerdebug:PPC Mac OS X Projectdebug:PPC Mach-O Linkerdebug:PPC Mach-O Targetdebug:PPC PEFdebug:PPC Projectdebug:PPCAsm Paneldebug:Property Listdebug:Rez Compilerdebug:WinRC Compilerdebug:x86 CodeGendebug:x86 COFFdebug:x86 Disassemblerdebug:x86 Linkerdebug:x86 Projectrelease:Source Treesrelease:Access Pathsrelease:Debugger Runtimerelease:Target Settingsrelease:File Mappingsrelease:Build Extrasrelease:Debugger Targetrelease:Remote Debugrelease:Auto-targetrelease:Custom Keywordsrelease:C/C++ Compilerrelease:C/C++ Warningsrelease:Java Command Linerelease:MacOS Merge Panelrelease:Output Flagsrelease:Packager Panelrelease:PPC CodeGenrelease:PPC CodeGen Mach-Orelease:PPC Disassemblerrelease:PPC Global Optimizerrelease:PPC Linkerrelease:PPC Mac OS X Linkerrelease:PPC Mac OS X Projectrelease:PPC Mach-O Linkerrelease:PPC Mach-O Targetrelease:PPC PEFrelease:PPC Projectrelease:PPCAsm Panelrelease:Property Listrelease:Rez Compilerrelease:WinRC Compilerrelease:x86 CodeGenrelease:x86 COFFrelease:x86 Disassemblerrelease:x86 Linkerrelease:x86 Projectdebug-classic:Source Treesdebug-classic:Access Pathsdebug-classic:Debugger Runtimedebug-classic:Target Settingsdebug-classic:File Mappingsdebug-classic:Build Extrasdebug-classic:Debugger Targetdebug-classic:Remote Debugdebug-classic:Auto-targetdebug-classic:Custom Keywordsdebug-classic:C/C++ Compilerdebug-classic:C/C++ Warningsdebug-classic:Java Command Linedebug-classic:MacOS Merge Paneldebug-classic:Output Flagsdebug-classic:Packager Paneldebug-classic:PPC CodeGendebug-classic:PPC CodeGen Mach-Odebug-classic:PPC Disassemblerdebug-classic:PPC Global Optimizerdebug-classic:PPC Linkerdebug-classic:PPC Mac OS X Linkerdebug-classic:PPC Mac OS X Projectdebug-classic:PPC Mach-O Linkerdebug-classic:PPC Mach-O Targetdebug-classic:PPC PEFdebug-classic:PPC Projectdebug-classic:PPCAsm Paneldebug-classic:Property Listdebug-classic:Rez Compilerdebug-classic:WinRC Compilerdebug-classic:x86 CodeGendebug-classic:x86 COFFdebug-classic:x86 Disassemblerdebug-classic:x86 Linkerdebug-classic:x86 Projectrelease-classic:Source Treesrelease-classic:Access Pathsrelease-classic:Debugger Runtimerelease-classic:Target Settingsrelease-classic:File Mappingsrelease-classic:Build Extrasrelease-classic:Debugger Targetrelease-classic:Remote Debugrelease-classic:Auto-targetrelease-classic:Custom Keywordsrelease-classic:C/C++ Compilerrelease-classic:C/C++ Warningsrelease-classic:Java Command Linerelease-classic:MacOS Merge Panelrelease-classic:Output Flagsrelease-classic:Packager Panelrelease-classic:PPC CodeGenrelease-classic:PPC CodeGen Mach-Orelease-classic:PPC Disassemblerrelease-classic:PPC Global Optimizerrelease-classic:PPC Linkerrelease-classic:PPC Mac OS X Linkerrelease-classic:PPC Mac OS X Projectrelease-classic:PPC Mach-O Linkerrelease-classic:PPC Mach-O Targetrelease-classic:PPC PEFrelease-classic:PPC Projectrelease-classic:PPCAsm Panelrelease-classic:Property Listrelease-classic:Rez Compilerrelease-classic:WinRC Compilerrelease-classic:x86 CodeGenrelease-classic:x86 COFFrelease-classic:x86 Disassemblerrelease-classic:x86 Linkerrelease-classic:x86 ProjectProject File Listdebug:FTP Paneldebug:PJavaDebuggingdebug:Java Languagedebug:Java Manifest-JAD Setting Infodebug:Java MRJAppBuilderdebug:Java Outputdebug:Java Projectdebug:JavaDoc Projectdebug:x86 Exceptions Paneldebug:x86 Global Optimizerrelease:FTP Panelrelease:PJavaDebuggingrelease:Java Languagerelease:Java Manifest-JAD Setting Inforelease:Java MRJAppBuilderrelease:Java Outputrelease:Java Projectrelease:JavaDoc Projectrelease:x86 Exceptions Panelrelease:x86 Global Optimizerdebug-classic:FTP Paneldebug-classic:PJavaDebuggingdebug-classic:Java Languagedebug-classic:Java Manifest-JAD Setting Infodebug-classic:Java MRJAppBuilderdebug-classic:Java Outputdebug-classic:Java Projectdebug-classic:JavaDoc Projectdebug-classic:x86 Exceptions Paneldebug-classic:x86 Global Optimizerrelease-classic:FTP Panelrelease-classic:PJavaDebuggingrelease-classic:Java Languagerelease-classic:Java Manifest-JAD Setting Inforelease-classic:Java MRJAppBuilderrelease-classic:Java Outputrelease-classic:Java Projectrelease-classic:JavaDoc Projectrelease-classic:x86 Exceptions Panelrelease-classic:x86 Global Optimizersdif.pagsemble~:Source Treessdif.pagsemble~:Access Pathssdif.pagsemble~:Debugger Runtimesdif.pagsemble~:Target Settingssdif.pagsemble~:File Mappingssdif.pagsemble~:Build Extrassdif.pagsemble~:Debugger Targetsdif.pagsemble~:Remote Debugsdif.pagsemble~:Auto-targetsdif.pagsemble~:Custom Keywordssdif.pagsemble~:C/C++ Compilersdif.pagsemble~:C/C++ Warningssdif.pagsemble~:FTP Panelsdif.pagsemble~:Java Command Linesdif.pagsemble~:PJavaDebuggingsdif.pagsemble~:Java Languagesdif.pagsemble~:Java Manifest-JAD Setting Infosdif.pagsemble~:Java MRJAppBuildersdif.pagsemble~:Java Outputsdif.pagsemble~:Java Projectsdif.pagsemble~:JavaDoc Projectsdif.pagsemble~:MacOS Merge Panelsdif.pagsemble~:Output Flagssdif.pagsemble~:Packager Panelsdif.pagsemble~:PPC CodeGensdif.pagsemble~:PPC CodeGen Mach-Osdif.pagsemble~:PPC Disassemblersdif.pagsemble~:PPC Global Optimizersdif.pagsemble~:PPC Linkersdif.pagsemble~:PPC Mac OS X Linkersdif.pagsemble~:PPC Mac OS X Projectsdif.pagsemble~:PPC Mach-O Linkersdif.pagsemble~:PPC Mach-O Targetsdif.pagsemble~:PPC PEFsdif.pagsemble~:PPC Projectsdif.pagsemble~:PPCAsm Panelsdif.pagsemble~:Property Listsdif.pagsemble~:Rez Compilersdif.pagsemble~:WinRC Compilersdif.pagsemble~:x86 CodeGensdif.pagsemble~:x86 COFFsdif.pagsemble~:x86 Disassemblersdif.pagsemble~:x86 Exceptions Panelsdif.pagsemble~:x86 Global Optimizersdif.pagsemble~:x86 Linkersdif.pagsemble~:x86 Projectsdif.pagsolo~:Source Treessdif.pagsolo~:Access Pathssdif.pagsolo~:Debugger Runtimesdif.pagsolo~:Target Settingssdif.pagsolo~:File Mappingssdif.pagsolo~:Build Extrassdif.pagsolo~:Debugger Targetsdif.pagsolo~:Remote Debugsdif.pagsolo~:Auto-targetsdif.pagsolo~:Custom Keywordssdif.pagsolo~:C/C++ Compilersdif.pagsolo~:C/C++ Warningssdif.pagsolo~:FTP Panelsdif.pagsolo~:Java Command Linesdif.pagsolo~:PJavaDebuggingsdif.pagsolo~:Java Languagesdif.pagsolo~:Java Manifest-JAD Setting Infosdif.pagsolo~:Java MRJAppBuildersdif.pagsolo~:Java Outputsdif.pagsolo~:Java Projectsdif.pagsolo~:JavaDoc Projectsdif.pagsolo~:MacOS Merge Panelsdif.pagsolo~:Output Flagssdif.pagsolo~:Packager Panelsdif.pagsolo~:PPC CodeGensdif.pagsolo~:PPC CodeGen Mach-Osdif.pagsolo~:PPC Disassemblersdif.pagsolo~:PPC Global Optimizersdif.pagsolo~:PPC Linkersdif.pagsolo~:PPC Mac OS X Linkersdif.pagsolo~:PPC Mac OS X Projectsdif.pagsolo~:PPC Mach-O Linkersdif.pagsolo~:PPC Mach-O Targetsdif.pagsolo~:PPC PEFsdif.pagsolo~:PPC Projectsdif.pagsolo~:PPCAsm Panelsdif.pagsolo~:Property Listsdif.pagsolo~:Rez Compilersdif.pagsolo~:WinRC Compilersdif.pagsolo~:x86 CodeGensdif.pagsolo~:x86 COFFsdif.pagsolo~:x86 Disassemblersdif.pagsolo~:x86 Exceptions Panelsdif.pagsolo~:x86 Global Optimizersdif.pagsolo~:x86 Linkersdif.pagsolo~:x86 Projectsdif.sogs~:Source Treessdif.sogs~:Access Pathssdif.sogs~:Debugger Runtimesdif.sogs~:Target Settingssdif.sogs~:File Mappingssdif.sogs~:Build Extrassdif.sogs~:Debugger Targetsdif.sogs~:Remote Debugsdif.sogs~:Auto-targetsdif.sogs~:Custom Keywordssdif.sogs~:C/C++ Compilersdif.sogs~:C/C++ Warningssdif.sogs~:FTP Panelsdif.sogs~:Java Command Linesdif.sogs~:PJavaDebuggingsdif.sogs~:Java Languagesdif.sogs~:Java Manifest-JAD Setting Infosdif.sogs~:Java MRJAppBuildersdif.sogs~:Java Outputsdif.sogs~:Java Projectsdif.sogs~:JavaDoc Projectsdif.sogs~:MacOS Merge Panelsdif.sogs~:Output Flagssdif.sogs~:Packager Panelsdif.sogs~:PPC CodeGensdif.sogs~:PPC CodeGen Mach-Osdif.sogs~:PPC Disassemblersdif.sogs~:PPC Global Optimizersdif.sogs~:PPC Linkersdif.sogs~:PPC Mac OS X Linkersdif.sogs~:PPC Mac OS X Projectsdif.sogs~:PPC Mach-O Linkersdif.sogs~:PPC Mach-O Targetsdif.sogs~:PPC PEFsdif.sogs~:PPC Projectsdif.sogs~:PPCAsm Panelsdif.sogs~:Property Listsdif.sogs~:Rez Compilersdif.sogs~:WinRC Compilersdif.sogs~:x86 CodeGensdif.sogs~:x86 COFFsdif.sogs~:x86 Disassemblersdif.sogs~:x86 Exceptions Panelsdif.sogs~:x86 Global Optimizersdif.sogs~:x86 Linkersdif.sogs~:x86 Projectsogs~:Source Treessogs~:Access Pathssogs~:Debugger Runtimesogs~:Target Settingssogs~:File Mappingssogs~:Build Extrassogs~:Debugger Targetsogs~:Remote Debugsogs~:Auto-targetsogs~:Custom Keywordssogs~:C/C++ Compilersogs~:C/C++ Warningssogs~:FTP Panelsogs~:Java Command Linesogs~:PJavaDebuggingsogs~:Java Languagesogs~:Java Manifest-JAD Setting Infosogs~:Java MRJAppBuildersogs~:Java Outputsogs~:Java Projectsogs~:JavaDoc Projectsogs~:MacOS Merge Panelsogs~:Output Flagssogs~:Packager Panelsogs~:PPC CodeGensogs~:PPC CodeGen Mach-Osogs~:PPC Disassemblersogs~:PPC Global Optimizersogs~:PPC Linkersogs~:PPC Mac OS X Linkersogs~:PPC Mac OS X Projectsogs~:PPC Mach-O Linkersogs~:PPC Mach-O Targetsogs~:PPC PEFsogs~:PPC Projectsogs~:PPCAsm Panelsogs~:Property Listsogs~:Rez Compilersogs~:WinRC Compilersogs~:x86 CodeGensogs~:x86 COFFsogs~:x86 Disassemblersogs~:x86 Exceptions Panelsogs~:x86 Global Optimizersogs~:x86 Linkersogs~:x86 Projectclassic:Source Treesclassic:Access Pathsclassic:Debugger Runtimeclassic:Target Settingsclassic:File Mappingsclassic:Build Extrasclassic:Debugger Targetclassic:Remote Debugclassic:Auto-targetclassic:Custom Keywordsclassic:C/C++ Compilerclassic:C/C++ Warningsclassic:FTP Panelclassic:Java Command Lineclassic:PJavaDebuggingclassic:Java Languageclassic:Java Manifest-JAD Setting Infoclassic:Java MRJAppBuilderclassic:Java Outputclassic:Java Projectclassic:JavaDoc Projectclassic:MacOS Merge Panelclassic:Output Flagsclassic:Packager Panelclassic:PPC CodeGenclassic:PPC CodeGen Mach-Oclassic:PPC Disassemblerclassic:PPC Global Optimizerclassic:PPC Linkerclassic:PPC Mac OS X Linkerclassic:PPC Mac OS X Projectclassic:PPC Mach-O Linkerclassic:PPC Mach-O Targetclassic:PPC PEFclassic:PPC Projectclassic:PPCAsm Panelclassic:Property Listclassic:Rez Compilerclassic:WinRC Compilerclassic:x86 CodeGenclassic:x86 COFFclassic:x86 Disassemblerclassic:x86 Exceptions Panelclassic:x86 Global Optimizerclassic:x86 Linkerclassic:x86 Projectrogs~:Source Treesrogs~:Access Pathsrogs~:Debugger Runtimerogs~:Target Settingsrogs~:File Mappingsrogs~:Build Extrasrogs~:Debugger Targetrogs~:Remote Debugrogs~:Auto-targetrogs~:Custom Keywordsrogs~:C/C++ Compilerrogs~:C/C++ Warningsrogs~:FTP Panelrogs~:Java Command Linerogs~:PJavaDebuggingrogs~:Java Languagerogs~:Java Manifest-JAD Setting Inforogs~:Java MRJAppBuilderrogs~:Java Outputrogs~:Java Projectrogs~:JavaDoc Projectrogs~:MacOS Merge Panelrogs~:Output Flagsrogs~:Packager Panelrogs~:PPC CodeGenrogs~:PPC CodeGen Mach-Orogs~:PPC Disassemblerrogs~:PPC Global Optimizerrogs~:PPC Linkerrogs~:PPC Mac OS X Linkerrogs~:PPC Mac OS X Projectrogs~:PPC Mach-O Linkerrogs~:PPC Mach-O Targetrogs~:PPC PEFrogs~:PPC Projectrogs~:PPCAsm Panelrogs~:Property Listrogs~:Rez Compilerrogs~:WinRC Compilerrogs~:x86 CodeGenrogs~:x86 COFFrogs~:x86 Disassemblerrogs~:x86 Exceptions Panelrogs~:x86 Global Optimizerrogs~:x86 Linkerrogs~:x86 Project s Uf WARZmacosxmacosx0mstr(mstlmstn(mstr.mstl)mstn'(pref 6` pref@prefm6EprefdpLpref%L"prefmT2 prefWRpref0'0pref '4preff 'N@pref (pref) ]bpref bj&prefYT,|prefA*gpref("prefե("pref( pref(( pref-Tpref)(\pref1mprefU.lprefxuprefL}~prefuVhprefz.prefPJ8pref*< pref("pref)pref#0 )2pref; !.pref*"~pref#Hpref$XmtloP mtslpmtplpmtps)mtpi4mtgl:motiPLst|R,mpsiH(msti)(msti~prefGU.prefƢ prefG5 pref (pref W6prefH pref  pref#ppref4 prefmallmapl. mtsl mtpl mtpsmtpiXmtlo@prefh%> pref+>&!pref.'&preft(,Lpref)y"pref* pref +(pref,Hpref-Lprefxj.@pref/fpref08prefjprefP1tprefrprefprefFprefA6prefD  prefJpref^&prefCf2(pref3pref}4"prefU5"prefwc6 pref[7 pref8(pref`9"\pref$:šprefFt;~lpref6<ʆpref9)=rpref1>ӊpref?Zpref8@hprefAx pref!|B."pref CprefWD 2prefpEprefFpref2" prefEprefG"prefHfluidsynth-1.1.9/bindings/fluidmax/fluidmax_fakefuns.c000066400000000000000000000126061322272076000231660ustar00rootroot00000000000000/*************************************************************************************** * * fluidsynth~ * * Fluid Synth soundfont synthesizer for Max/MSP. * * Fluid Synth is written by Peter Hanappe et al. * see http://www.fluidsynth.org/ * * Max/MSP integration by Norbert Schnell ATR IRCAM - Centre Pompidou * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * See file COPYING.LIB for further informations on licensing terms. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. * */ /* * This file contains the (mostly empty) implementation of some functions without * which Fluidsynth wouldn't compile. * * The Max/MSP version of FluidSynth tries to link only with a minimum of needed * files. Some of the linked files depend on functions that are not necessary * and that are implemnted in files we don't want to include. So, alternate or empty * implemntations of these functions are provided here. * */ #include "ftmax.h" #include "fluidsynth.h" #include "fluidsynth_priv.h" #include "fluid_settings.h" unsigned int fluid_curtime() { return (unsigned int)gettime(); } double fluid_utime(void) { return 0.0; } typedef void fluid_timer_t; typedef int(*fluid_timer_callback_t)(void* data, unsigned int msec); fluid_timer_t * new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy) { /* just call it right away */ (*callback)(data, msec); return NULL; } void fluid_sys_config() { } char* fluid_error() { return NULL; } /* This code is (unelegantly) copied from fluid_sys.c since other parts of fluid_sys.c * that we don't need here depend on many other things that we don't want here. */ char * fluid_strtok (char **str, char *delim) { char *s, *d, *token; char c; if (str == NULL || delim == NULL || !*delim) { FLUID_LOG(FLUID_ERR, "Null pointer"); return NULL; } s = *str; if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */ /* skip delimiter chars at beginning of token */ do { c = *s; if (!c) /* end of source string? */ { *str = NULL; return NULL; } for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { s++; /* advance to next source char */ break; } } } while (*d); /* while token char match */ token = s; /* start of token found */ /* search for next token char or end of source string */ for (s = s+1; *s; s++) { c = *s; for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { *s = '\0'; /* overwrite token char with zero byte to terminate token */ *str = s+1; /* update str to point to beginning of next token */ return token; } } } /* we get here only if source string ended */ *str = NULL; return token; } int fluid_log(int level, char* fmt, ...) { char buf[1024]; va_list args; va_start (args, fmt); vsprintf(buf, fmt, args); va_end (args); if ((level > 0) && (level < LAST_LOG_LEVEL)) post("fluidsynth~ core (level %d): %s", level, buf); return -1; } void fluid_shell_settings(fluid_settings_t *settings) { } void fluid_audio_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "audio.driver", "", 0, NULL, NULL); } void fluid_midi_driver_settings(fluid_settings_t *settings) { fluid_settings_register_str(settings, "midi.driver", "", 0, NULL, NULL); } int fluid_midi_event_get_type(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type) { return FLUID_OK; } int fluid_midi_event_get_channel(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan) { return FLUID_OK; } int fluid_midi_event_get_key(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_key(fluid_midi_event_t* evt, int v) { return FLUID_OK; } int fluid_midi_event_get_velocity(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int v) { return FLUID_OK; } int fluid_midi_event_get_control(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_control(fluid_midi_event_t* evt, int v) { return FLUID_OK; } int fluid_midi_event_get_value(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_value(fluid_midi_event_t* evt, int v) { return FLUID_OK; } int fluid_midi_event_get_program(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val) { return FLUID_OK; } int fluid_midi_event_get_pitch(fluid_midi_event_t* evt) { return 0; } int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val) { return FLUID_OK; } fluidsynth-1.1.9/bindings/fluidmax/fluidsynth.jpg000066400000000000000000000501271322272076000222220ustar00rootroot00000000000000JFIFHH ExifMM*bj(1r2iAdobe Photoshop 7.02004:02:21 01:44:47@c(&HHJFIFHH Adobe_CMAdobed            c@"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?*iYM,mUT` kZѵccߢNA6]S#,~܋\׻n)IW̫;Lև9bvCڢ@))Sw8潡!:5%.em] pऒJ@Aq?cVc7zU4K±Ծ׏LϠӏhl\Yv i,a?CJ{yqBe,d~]4\o`NXC=YǦet'A: ;_OWk;ob+UZZ1ZKHIOQUcӫDOt ^S5..MkK#w3T/,;}fav,-sO5_oWKHs51IOKsON_4vdY}&~w֭`Orϯw?TgJ ɱ_G5b6Z[g<\}Fݽrz]B0SL;tk-kÖ֜[өV;7 &oo8usiva"v$N÷U[}e #+}]v&] Y?5r䱰w[jOZ(krk׾?~⎥P >G/Ӿ,1V:LtX+O=t_=gc%i'oXȶ٩)-Fu-ʥ[ oKtf-ĸ3,-\X~݀jř}9ڋ?iwz]5~:N3u~s>S>]խTcfR ln&K?_1q׬KtW8IڽRYu/D.EWM6Vn{ݧ褧n݉ip,vRYl 5#WnL:c}m Y^Z=>I)~EU]=ەWThƭ:w֬k[gO5 2rgY_]޳^GTu7YTNԋr2z$Yv3/IOw[6q{: u>׳~;Gb?\)_N9t:-n[WAJ- ?ٌll8CSH\aZ붏ϥWa5Cq[1˜d-fE=C JZ@G0P~}U0ɳeՓN:Kb`SES%?ZCykDw%5N;ov87tj].-Ż벷}nfįe]џcoo-ܽՌzB]f{0/zz/."Cy:LS)Kh[N ~OQ9>Ȃdx@bSA:tIHqq~>Mm> ~~ꎙ@{A$idLkˣ%h?[堒ǝd+I$T6ł;JTʩ$ꤗʩ$ꤗʩ$ꤗʩ$,rPhotoshop 3.08BIM%8BIM com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0.0 0.0 734 576 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -18 -18 774 594 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName na-letter com.apple.print.ticket.client com.apple.print.pm.PostScript com.apple.print.ticket.modDate 2003-07-01T17:49:36Z com.apple.print.ticket.stateFlag 1 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0.0 0.0 734 576 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 774 594 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2004-02-21T00:41:04Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName US Letter com.apple.print.ticket.client com.apple.print.pm.PostScript com.apple.print.ticket.modDate 2003-07-01T17:49:36Z com.apple.print.ticket.stateFlag 1 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PageFormatTicket 8BIMxHH@Rg(HH(dh 8BIMGG8BIM&?8BIM x8BIM8BIM 8BIM 8BIM' 8BIM5-8BIM8BIM8BIM8BIM@@8BIM8BIMMc@ Sans titre-3@cnullboundsObjcRct1Top longLeftlongBtomlongcRghtlong@slicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongcRghtlong@urlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM8BIM8BIM @cJ@JFIFHH Adobe_CMAdobed            c@"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?*iYM,mUT` kZѵccߢNA6]S#,~܋\׻n)IW̫;Lև9bvCڢ@))Sw8潡!:5%.em] pऒJ@Aq?cVc7zU4K±Ծ׏LϠӏhl\Yv i,a?CJ{yqBe,d~]4\o`NXC=YǦet'A: ;_OWk;ob+UZZ1ZKHIOQUcӫDOt ^S5..MkK#w3T/,;}fav,-sO5_oWKHs51IOKsON_4vdY}&~w֭`Orϯw?TgJ ɱ_G5b6Z[g<\}Fݽrz]B0SL;tk-kÖ֜[өV;7 &oo8usiva"v$N÷U[}e #+}]v&] Y?5r䱰w[jOZ(krk׾?~⎥P >G/Ӿ,1V:LtX+O=t_=gc%i'oXȶ٩)-Fu-ʥ[ oKtf-ĸ3,-\X~݀jř}9ڋ?iwz]5~:N3u~s>S>]խTcfR ln&K?_1q׬KtW8IڽRYu/D.EWM6Vn{ݧ褧n݉ip,vRYl 5#WnL:c}m Y^Z=>I)~EU]=ەWThƭ:w֬k[gO5 2rgY_]޳^GTu7YTNԋr2z$Yv3/IOw[6q{: u>׳~;Gb?\)_N9t:-n[WAJ- ?ٌll8CSH\aZ붏ϥWa5Cq[1˜d-fE=C JZ@G0P~}U0ɳeՓN:Kb`SES%?ZCykDw%5N;ov87tj].-Ż벷}nfįe]џcoo-ܽՌzB]f{0/zz/."Cy:LS)Kh[N ~OQ9>Ȃdx@bSA:tIHqq~>Mm> ~~ꎙ@{A$idLkˣ%h?[堒ǝd+I$T6ł;JTʩ$ꤗʩ$ꤗʩ$ꤗʩ$8BIM!UAdobe PhotoshopAdobe Photoshop 7.08BIMHhttp://ns.adobe.com/xap/1.0/ adobe:docid:photoshop:d4a4b3d8-64d3-11d8-a03a-d930622dfdfc AdobedC       c@  3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw?T1i MTs$I(>i?IZ{U׾bKh!תSmnx>Wt]VCϥ\eq_ $UWϱUY,8X$4x|N1;Z+5x^c'u7dIo{߉C0ӫYu.gU!˺mLޯO1oKM,occ~kYMok?=#;Oc5r}BvFKI{lNwk'A kCz~eGѵV8vk {~cm.nE5:̺6n>̐Mu]ɨgEs=2\M6X{[Kv5lsC[qp=+W8?ůDU;w-NFONɮ=_8\#7/㺠u$y_W1uAc:5nbQ[Xր`~H0?tN%teu0 y@R7b==K|lOcU’L8HV:]uG*ޗիc#u1:B`}kctf}#gQv'bXѬw X>uoyVk}sqw{3G[Nq⮵?MY:o챟]~&K2ɬևA d_Z. n^mg󫺧~W>LkqL\O7Qs߃1~G֯~լo,8Az^WӖgA;5mn?@]O>~3]oao).ܴ{SiYVI[X2,3g+Xx]fc+ӶFMr@xL5ߣJΣy/ahpOr>`u.;,t 5+ɬ??7;?Z=7Q~/:_9hM;p~],e%[|7.V~ҾVn&6~mBLhE?X3沦c  W<,j20݇5dMVmףACW?RBzczՋ߉wz2}GkY=C%d1;1<[1=...1>]tyx 1.1?pitch131@MIDI1415161Akey1B(011C127):1:1;1D[1=1>1E(the1Flast1Gtwo1Hgiven1Ivalues1Jwill11Kextrapolated)rs,t1Linlet -_Ptvwx$Atv1Pt1 2 1Mstripnotetv1Qt1 -> 1Nprependt1Ooutletn_Ptv1Pt1 P-> 1Nt1OPn_Pt1L-_Pt1 7i^ 1Pmakenotext1L7-_Pt1On_Pt1"9W 1Qcontrol1R$21S$11T$3tv1Qt1 9: 1UpackPPPt1 9: 1Vctlint1 C 1N1Wbendt1 : 1UPPt1 : 1Xbendint1 C 1N17t1 : 1UPPQt1 : 1Ynoteint1ZconnectZP[Pt1ZPPQPt1Z[PQPt1ZQPRPt1ZPQQQt1Z[QQQt1ZXPYPt1ZUPYPt1ZRPYPt1Z\PQRt1ZPRQRt1ZSPTPt1ZTPUPt1ZPPPt1ZPPt1ZP_Pt1Z\PTQt1ZSQTQt1ZPQQt1ZVPWPt1ZWPXPt1ZVR^Pt1ZPR^Pt1ZSQ^Pt1Z^P]Pt1ZPR]Pt1ZVQWQt1ZVRWRt1Z\PWRt1[popt11\newobjCmT 1]p1^MIDI-keyboardt1" 1_tuning-keysPP  ty2& 1`reset11aordinary111151H1bchanneltyx&k 1cselect11dbank}1151H1echannel:1:1;1ft1"%J 1gtuning-resetQt1"%_ 1htuning-selectPPQt1" 1ituning-octavePP2P2P2P2P2P2Ptys 1jresampling1kmethod1l(default1mis1ncubic)t11oumenuY'Q@Q1'1padd1qresample1rnearest1'1p1q1slinear1'1p1q1tcubic1'1p1q1usincty)S 1vFluidSynth1whome:1xhttp://www.fluidsynth.org/tyBC 1v1m1ywritten1zby1{Peter1|Hanappe1}et1~al.ty< 1output1list1of1presets:111:1;tyNq 1111presets11H1soundfont:111:1;ty 11'off'1or1current1chorus1parameters:11111<#11units>tyr} 11111reverb11111ty* 1111soundfonts:111ty` 11selected1preset15161e1f11:1;1t1 2 1print1infot1 mz 1route1bangt1"G;? 11t1"GMP 111GMt1"G; 11t1"Gq< 11t1"G)K 11soundfontst1"G_A 11channelstyb 1111t1"D 11ty 11111181H1soundfontt1"U 111t1"7 11offt1"7 11t1"wp 1Z$?(PZt1"4 11onty 111level1,1speed11depth11type1111units11switch11on/offt1"] 1R$>XPSt1"i 1$?$L?$?$333?ty  1111parameterst1"@ 11ty 1111t1" A 11t1"i5 11tyz 11111room1size11damping11width111reberb1t1"i 1$fff?$L>P$?ty 1change11"generator"1value11H1bt1flonumD0YPPPSPPPPPPt1"* 1gain1St1"2 1unload1allt1button_PtytI 1load1t1"X 1tyc 1Max/MSP1integration1z1Norbert1Schnell151Roland1Cahenty  1111modulator1I151H1b1l11)t1"R 11generatorst1[-YPPPSPPPPPPt11hslider_QPPt1"A 1mod31SQty| 1@1?1W1wheel1range131semitones11bt1"O{; 1\Qt1"{5 1RQt1Oi-YPPPSPPPPPPtyj 1@1?1W111(use1float1argument11use1LSB1114-bit1value)}1bt11~i_@Q@Pt11jW_QPPt1"i5 1W1SQty;  1#1sound}1`11@1controllers}1effectst1toggleG+_Pt1"+- 1mute1St1"[+! 1ؠt1"+) 1unmutet1"WP 1Q[1SQtyX 1@1Q111contoller111}1bty p 1111tyPS 1reload1t1"6 11t1"6 11ty 1c111d}1151H1@1bt1o#YPPPSPPPPPPt1"U 1c1P1SQty 1c1151H1@1bt1"! 1`t1"  1panict1"R 11t1"H 11t1"EI 17EQt11gain~-zP$'?$.@$ At11zP$'?$.@$ At1a#YPPPSPPPPPPt1O#YPPPSPPPPPPt1"G 11SQt1"5 1d1SQt1"cE= 17EPQt1"> 11GM.sf2tvwx$At1 Ez1fluidsynth~tvwx$AtyS 11[1[<#11@11116)>]]1[]tyF, 1syntax:tys 1c1d151H1@1btyP 1remove1111ty 1111d}115161btyF 1@17111?11velocity}1bty,p 1}11synthty:- 1#1}1`11@111modulators}1֠tvwx$AtyIW1ߠtvwx$@AtyI( 1v11font1synthesizertvwx$Atyvz 1s1overall11factortyK 11after1ty 1(Note:1Soundfonts1can1specified1z1name11id)tyB 1@1br1picture1#K1replace, 1P(R@ HH@RP Q@J8HH  \dJ8Q@;>>;;>t 1  b   w   h ҹ  ҹ  ҹ w!aQ!Qa!aQנ1z"`@;ϕ"`@;ϕ"`@;ϕɒ5BR!&NܐAN] 5BR!&NܐAN] 5BR!&NܐAN] ɕ >SW Yւ: {4f SW>Y: {4 f W>SY: ւ {f4ɛ$N:S{ !dQ  Mj$S1N:!{d Q j M$:SN!{d Q j Mɞ0%8WMfk 0%8WMfk 0%8WMfk ɞ0u) J1{Jx0u) J1{Jx0u) J1{Jxɞ0;"RA 5HA MAb0;"RA 5HA MAb10;"RA 5HA MAbɎ0@5=CO-(߳Ɏ0@5=CO-(߳Ɏ0@'=5CO(-߳ɬP8ݎ`f$P{m8P8ݎ`f$P{m8P8`'$fP{m8ɬN \%Eqq7n B*N \%Eqq7n B*1N \E'q%7qn B*ɬQҖ^ U7j  k|I%isQҖ^ U7j  k|I%isQҖ^ U7' k|jI %is" J<{ #l. ] 8C8\^ " J<{ #l. ] 8C8\^ " J<{ '#. l 88\]C ^ɬ <xm<_IEI5g+S <xm<_IEI5g1+S <xm<_I'EI5gS+ɬ9CO '2#%j[^S19CO '2#%j[^S19CO '2'%j[#^S1ɫ <x$"oH4?NٜEͮ <x$"oH4?NٜEͮ <x$"oH?NٜEͮɬܜYO *}4dupܜYO *}4du1pܜYO'*}4 updb[ ۜb[ ۜ' b[ۜɨS6@c窜cS6@@cS6ɬıɁxıɁx1ı'ɁxɬNN'NǷ 1 |ὯǷ  1Ғ|Ƿ 1 |Ὧȫ`ܺ )EC$y-'a|<U`ܺ )EC$y-1'a|<U`ܺ EC&$y-)a|<'Uɬ'Q.}W# #{L:y "m'Q.}W# #{L:y "m'Q.}W#' #{L׼: ym"ɬE.X!ď"cyE/RB!-:E.X!ď"cyE/RB!-:E.X'!ď"cyE/ÜRB!-:q4bɡQ0q1  Y<e%uݟq4bɡQ0q֠11  Y<e%uݟq'4bQq  0Y1e<%ݟuɬ>!b͒!J?t:ޖy>!b͒!J?t:ޖy!b'͒>!J?܀t:ޖyǩf4Ѻ- fϤ LIqM nDhf4Ѻ- fϤ LIqM nDhf5Ѻ- fϤ LIqM nDh'¶\Mjp Բq7$ (ӞWSH sR'¶\Mjp 1Բq7$ (ӞWSH sR'¶\Mj'pԲq $ 7(WS sHR ɫ? @(4 RW> "h]#*? @(4 RW> "h]#*? @(* RW> "h]#*ǪN+4CPB7yNPkѹ7`:'!yyN+4CPB7yNPkѹ7`:'!yyN+4CPB%y7PNkѹ:'!7yy`D18f$M⹂K1IOfY{ӴD18f$M1⹂K1IOfY{ӴD18f'$MKI1OfY{ӴɬNNɝkWQmγcٸ!dgNNɝkWQmγcٸ!dgNNɝkQm'cWdg!峔ǪJ.M嶁;)D L) J.M嶁;)D L) J.M嶁%;)DLǖ )ɬ"qڡf1B߹i&F-Ox"qڡf11B߹i&F-Ox"qڡf1'Bi&-OxFɬyC-G׽)t ܃˨yC-G׽)t ܃˨yC-G׽'t )܃˨ơ1˸"-g.b>1"g-.b>1˸"g-.b>Ǫ꾖 &%4#-B7 Ӡ1꾖 &%4#-B7 %꾖 %4# -B7& I'5wuٝt+GpTҶI'5wuٝt+GpTҶ'I'5ٝwt+uGpTҶ2ɨ I `;V=SBu}bK3I V`;b=SBu}٠K3I ;V`}b=SBu٠Kɤ2>Wz[>!}8'6>k`M1/<0N2[>>Wz1}'!8k6>1/0N`M<2>Wz[>!}8'6>k`M1/<0Nɫc"+f2B-|+| PW1K:C7Pc"+f2B-|+| PW1K:C7Pc"+f2B-|+| PW1K:C7Pǩ/m&E)?$ -;=Gk 0<>/m&E)?$ -;=Gk 0<>/m&E)?$ -;=Gk 0<>2Ģ!w  [7.*4 0\2!w 1 *4[07.\ 2!w  [7.*4 0\4V  +,%\   4V  +,%\   2V  +,%\   2P N;P @ ݞ2 PP@N;   ݞ2P N;P @ ݞť3¹iC N' 3CH>9"##3Ci N>' 1#3CH9"#3¹CiN H>'##3C9"ɧ1mprvCu~(F8Λj1pm~rv(CuFj8Λ1mpu~rv(CFj8/ɡ+'NGONȦt/'NG+NȦtO/N+'GtONɣ+M *{* M*{Զ1 * M* {Զy-5-5Ⱦ5-ȾɃɌ   ɏ 1   ɏ"""Ƀ   +1 ޱ  ޱ4   71 ```4 ත ත ත7   1+ t1vpictureYVCTt11ZPPt11ZPPt11ZPPt11Z*P)Pt11Z-P&Pt11Z0P,Pt11Z6P4Pt11Z?P>Pt11ZjP_Pt11ZQP_Pt11ZOP_Pt11ZMP_Pt11ZNP_Pt11ZLP_Pt11ZKP_Pt11ZIP_Pt11ZCP_Pt11ZHP_Pt11ZAP_Pt11ZDP_Pt11Z>P_Pt11Z:P_Pt11Z4P_Pt11Z&P_Pt11Z(P_Pt11Z'P_Pt11Z)P_Pt11Z"P_Pt11Z!P_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11ZP_Pt11Z,P_Pt11Z1P_Pt11Z2P_Pt11Z7P_Pt11Z=P_Pt11ZFP_Pt11ZVP_Pt11ZUP_Pt11ZSP_Pt11ZWP_Pt11ZXP_Pt11ZTP_Pt11ZcQ_Pt11ZgP_Pt11ZfP_Pt11ZeP_Pt11ZkP_Pt11ZrPPt1Z_PPt1ZPoPt11ZpPoPt11ZqPoPt1fasten_QPUl2lt11ZQPt1ZPoQt11Z.P0Pt11Z5P6Pt1Z_RYPt1ZYP MAX/MSP utilities * */ /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * See file COPYING.LIB for further informations on licensing terms. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. * */ #ifndef _FTMAX_H_ #define _FTMAX_H_ #include #ifndef MAGIC #include "ext.h" #include "z_dsp.h" #endif #define ftm_malloc(s) NewPtr(s) #define ftmax_realloc NO_realloc #define ftmax_free(p) DisposePtr((char *)(p)) #define restrict #define M_TWOPI 6.283185308 //#define M_PI 3.141592654 #define ftmax_object_t t_object #define ftmax_dsp_object_t t_pxobject typedef t_atom ftmax_atom_t; typedef t_symbol *ftmax_symbol_t; typedef t_messlist ftmax_class_t; typedef method ftmax_method_t; #define ftmax_is_int(a) ((a)->a_type == A_LONG) #define ftmax_is_float(a) ((a)->a_type == A_FLOAT) #define ftmax_is_number(a) ((a)->a_type == A_LONG || (a)->a_type == A_FLOAT) #define ftmax_is_symbol(a) ((a)->a_type == A_SYM) #define ftmax_get_int(a) ((a)->a_w.w_long) #define ftmax_get_float(a) ((a)->a_w.w_float) #define ftmax_get_number_int(a) (((a)->a_type == A_LONG)? ((a)->a_w.w_long): (int)((a)->a_w.w_float)) #define ftmax_get_number_float(a) (((a)->a_type == A_FLOAT)? (double)((a)->a_w.w_float): (double)((a)->a_w.w_long)) #define ftmax_get_symbol(a) ((a)->a_w.w_sym) #define ftmax_set_int(a, v) ((a)->a_type = A_LONG, (a)->a_w.w_long = (v)) #define ftmax_set_float(a, v) ((a)->a_type = A_FLOAT, (a)->a_w.w_float = (v)) #define ftmax_set_symbol(a, v) ((a)->a_type = A_SYM, (a)->a_w.w_sym = (v)) #define ftmax_symbol_name(s) ((s)->s_name) #define ftmax_new_symbol(s) gensym(s) extern void ftmax_class_message_varargs(ftmax_class_t *cl, ftmax_symbol_t sym, void *method); #endif fluidsynth-1.1.9/bindings/fluidsynth_jni/000077500000000000000000000000001322272076000205425ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/README000066400000000000000000000005001322272076000214150ustar00rootroot00000000000000 FluidSynth_JNI -------------- This directory contains a VC++ project, Java classes, and some C++ files to use FluidSynth from within Java. This project was written rather quickly and for a specific use. Is not intended to be complete but may be useful as a starting point for complete Java interface. [Peter Hanappe] fluidsynth-1.1.9/bindings/fluidsynth_jni/fluidsynth_jni.dsp000066400000000000000000000110511322272076000243010ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="fluidsynth_jni" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=fluidsynth_jni - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "fluidsynth_jni.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "fluidsynth_jni.mak" CFG="fluidsynth_jni - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "fluidsynth_jni - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "fluidsynth_jni - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "fluidsynth_jni - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FLUIDSYNTH_JNI_EXPORTS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "include" /I "C:\Program Files\Java\j2re1.4.0_03\include" /I "C:\Program Files\Java\j2re1.4.0_03\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FLUIDSYNTH_JNI_EXPORTS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "NDEBUG" # ADD RSC /l 0x40c /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 fluidsynth.lib libsndfile.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"lib" !ELSEIF "$(CFG)" == "fluidsynth_jni - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FLUIDSYNTH_JNI_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "include" /I "C:\Program Files\WebGain\VCafe\Java2\include" /I "C:\Program Files\WebGain\VCafe\Java2\include\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FLUIDSYNTH_JNI_EXPORTS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x40c /d "_DEBUG" # ADD RSC /l 0x40c /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 fluidsynth_debug.lib libsndfile.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"fluidsynth_jni.dll" /pdbtype:sept /libpath:"lib" !ENDIF # Begin Target # Name "fluidsynth_jni - Win32 Release" # Name "fluidsynth_jni - Win32 Debug" # Begin Source File SOURCE=.\src\fluidsynth_jni.cpp # End Source File # Begin Source File SOURCE=.\src\fluidsynth_jni.h # End Source File # Begin Source File SOURCE=.\src\fluidsynth_Sample.cpp # End Source File # Begin Source File SOURCE=.\src\fluidsynth_Sample.h # End Source File # Begin Source File SOURCE=.\src\fluidsynth_Synth.cpp # End Source File # Begin Source File SOURCE=.\src\fluidsynth_Synth.h # End Source File # End Target # End Project fluidsynth-1.1.9/bindings/fluidsynth_jni/fluidsynth_jni.dsw000066400000000000000000000010511322272076000243070ustar00rootroot00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "fluidsynth_jni"=".\fluidsynth_jni.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### fluidsynth-1.1.9/bindings/fluidsynth_jni/include/000077500000000000000000000000001322272076000221655ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/include/sndfile.h000066400000000000000000000336371322272076000237760ustar00rootroot00000000000000/* ** Copyright (C) 1999-2003 Erik de Castro Lopo ** ** This program 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 program 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 program; if not, write to the Free Software ** Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. */ /* ** sndfile.h -- system-wide definitions ** ** API documentation is in the doc/ directory of the source code tarball ** and at http://www.zip.com.au/~erikd/libsndfile/api.html. */ #ifndef SNDFILE_H #define SNDFILE_H /* This is the version 1.0.X header file. */ #define SNDFILE_1 #include #include /* For the Metrowerks CodeWarrior Pro Compiler (mainly MacOS) */ #if (defined (__MWERKS__)) #include #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* The following file types can be read and written. ** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise ** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and ** SF_FORMAT_SUBMASK can be used to separate the major and minor file ** types. */ enum { /* Major formats. */ SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */ SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ SF_FORMAT_VOC = 0x080000, /* VOC files. */ SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ /* Subtypes from here on. */ SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ /* Endian-ness options. */ SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ SF_FORMAT_SUBMASK = 0x0000FFFF, SF_FORMAT_TYPEMASK = 0x0FFF0000, SF_FORMAT_ENDMASK = 0x30000000 } ; /* ** The following are the valid command numbers for the sf_command() ** interface. The use of these commands is documented in the file ** command.html in the doc directory of the source code distribution. */ enum { SFC_GET_LIB_VERSION = 0x1000, SFC_GET_LOG_INFO = 0x1001, SFC_GET_NORM_DOUBLE = 0x1010, SFC_GET_NORM_FLOAT = 0x1011, SFC_SET_NORM_DOUBLE = 0x1012, SFC_SET_NORM_FLOAT = 0x1013, SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, SFC_GET_SIMPLE_FORMAT = 0x1021, SFC_GET_FORMAT_INFO = 0x1028, SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, SFC_GET_FORMAT_MAJOR = 0x1031, SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, SFC_GET_FORMAT_SUBTYPE = 0x1033, SFC_CALC_SIGNAL_MAX = 0x1040, SFC_CALC_NORM_SIGNAL_MAX = 0x1041, SFC_CALC_MAX_ALL_CHANNELS = 0x1042, SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, SFC_SET_ADD_PEAK_CHUNK = 0x1050, SFC_UPDATE_HEADER_NOW = 0x1060, SFC_SET_UPDATE_HEADER_AUTO = 0x1061, SFC_FILE_TRUNCATE = 0x1080, SFC_SET_RAW_START_OFFSET = 0x1090, SFC_SET_DITHER_ON_WRITE = 0x10A0, SFC_SET_DITHER_ON_READ = 0x10A1, SFC_GET_DITHER_INFO_COUNT = 0x10A2, SFC_GET_DITHER_INFO = 0x10A3, SFC_GET_EMBED_FILE_INFO = 0x10B0, /* Following commands for testing only. */ SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, /* ** SFC_SET_ADD_* values are deprecated and will disappear at some ** time in the future. They are guaranteed to be here up to and ** including version 1.0.8 to avoid breakage of existng software. ** They currently do nothing and will continue to do nothing. */ SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, SFC_SET_ADD_DITHER_ON_READ = 0x1071 } ; /* ** String types that can be set and read from files. Not all file types ** support this and even the file types which support one, may not support ** all string types. */ enum { SF_STR_TITLE = 0x01, SF_STR_COPYRIGHT = 0x02, SF_STR_SOFTWARE = 0x03, SF_STR_ARTIST = 0x04, SF_STR_COMMENT = 0x05, SF_STR_DATE = 0x06 } ; enum { /* True and false */ SF_FALSE = 0, SF_TRUE = 1, /* Modes for opening files. */ SFM_READ = 0x10, SFM_WRITE = 0x20, SFM_RDWR = 0x30 } ; /* Pubic error values. These are guaranteed to remain unchanged for the duration ** of the library major version number. ** There are also a large number of private error numbers which are internal to ** the library which can change at any time. */ enum { SF_ERR_NO_ERROR = 0, SF_ERR_UNRECOGNISED_FORMAT = 1, SF_ERR_SYSTEM = 2 } ; /* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ typedef void SNDFILE ; typedef __int64 sf_count_t ; /* A pointer to a SF_INFO structure is passed to sf_open_read () and filled in. ** On write, the SF_INFO structure is filled in by the user and passed into ** sf_open_write (). */ struct SF_INFO { sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ int samplerate ; int channels ; int format ; int sections ; int seekable ; } ; typedef struct SF_INFO SF_INFO ; /* The SF_FORMAT_INFO struct is used to retrieve information about the sound ** file formats libsndfile supports using the sf_command () interface. ** ** Using this interface will allow applications to support new file formats ** and encoding types when libsndfile is upgraded, without requiring ** re-compilation of the application. ** ** Please consult the libsndfile documentation (particularly the information ** on the sf_command () interface) for examples of its use. */ typedef struct { int format ; const char *name ; const char *extension ; } SF_FORMAT_INFO ; /* ** Enums and typedefs for adding dither on read and write. ** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE ** and SFC_SET_DITHER_ON_READ. */ enum { SFD_DEFAULT_LEVEL = 0, SFD_CUSTOM_LEVEL = 0x40000000, SFD_NO_DITHER = 500, SFD_WHITE = 501, SFD_TRIANGULAR_PDF = 502 } ; typedef struct { int type ; double level ; const char *name ; } SF_DITHER_INFO ; /* Struct used to retrieve information about a file embedded within a ** larger file. See SF_GET_EMBED_FILE_INFO. */ typedef struct { sf_count_t offset ; sf_count_t length ; } SF_EMBED_FILE_INFO ; /* Open the specified file for read, write or both. On error, this will ** return a NULL pointer. To find the error number, pass a NULL SNDFILE ** to sf_perror () or sf_error_str (). ** All calls to sf_open() should be matched with a call to sf_close(). */ SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; /* Use the existing file descriptor to create a SNDFILE object. If close_desc ** is TRUE, the file descriptor will be closed when sf_close() is called. If ** it is FALSE, the descritor will not be closed. ** When passed a descriptor like this, the library will assume that the start ** of file header is at the current file offset. This allows sound files within ** larger container files to be read and/or written. ** On error, this will return a NULL pointer. To find the error number, pass a ** NULL SNDFILE to sf_perror () or sf_error_str (). ** All calls to sf_open_fd() should be matched with a call to sf_close(). */ SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; /* sf_error () returns a error number which can be translated to a text ** string using sf_error_number(). */ int sf_error (SNDFILE *sndfile) ; /* sf_strerror () returns to the caller a pointer to the current error message for ** the given SNDFILE. */ const char* sf_strerror (SNDFILE *sndfile) ; /* sf_error_number () allows the retrieval of the error string for each internal ** error number. ** */ const char* sf_error_number (int errnum) ; /* The following three error functions are deprecated but they will remain in the ** library for the forseeable future. The function sf_strerror() should be used ** in their place. */ int sf_perror (SNDFILE *sndfile) ; int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; /* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; /* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ int sf_format_check (const SF_INFO *info) ; /* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses ** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as ** stdio.h function fseek (). ** An offset of zero with whence set to SEEK_SET will position the ** read / write pointer to the first data sample. ** On success sf_seek returns the current position in (multi-channel) ** samples from the start of the file. ** Please see the libsndfile documentation for moving the read pointer ** separately from the write pointer on files open in mode SFM_RDWR. ** On error all of these functions return -1. */ sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; /* Functions for retrieving and setting string data within sound files. ** Not all file types support this features; AIFF and WAV do. For both ** functions, the str_type parameter must be one of the SF_STR_* values ** defined above. ** On error, sf_set_string() returns non-zero while sf_get_string() ** returns NULL. */ int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; const char* sf_get_string (SNDFILE *sndfile, int str_type) ; /* Functions for reading/writing the waveform data of a sound file. */ sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; sf_count_t sf_write_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; /* Functions for reading and writing the data chunk in terms of frames. ** The number of items actually read/written = frames * number of channels. ** sf_xxxx_raw read/writes the raw data bytes from/to the file ** sf_xxxx_short passes data in the native short format ** sf_xxxx_int passes data in the native int format ** sf_xxxx_float passes data in the native float format ** sf_xxxx_double passes data in the native double format ** All of these read/write function return number of frames read/written. */ sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; sf_count_t sf_writef_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; sf_count_t sf_writef_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; sf_count_t sf_writef_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; sf_count_t sf_writef_double(SNDFILE *sndfile, double *ptr, sf_count_t frames) ; /* Functions for reading and writing the data chunk in terms of items. ** Otherwise similar to above. ** All of these read/write function return number of items read/written. */ sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; sf_count_t sf_write_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; sf_count_t sf_write_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; sf_count_t sf_write_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; /* Close the SNDFILE and clean up all memory allocations associated with this ** file. ** Returns 0 on success, or an error number. */ int sf_close (SNDFILE *sndfile) ; #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* SNDFILE_H */ fluidsynth-1.1.9/bindings/fluidsynth_jni/java/000077500000000000000000000000001322272076000214635ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/java/fluidsynth/000077500000000000000000000000001322272076000236545ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/java/fluidsynth/FluidException.java000066400000000000000000000002041322272076000274350ustar00rootroot00000000000000package fluidsynth; public class FluidException extends Exception { public FluidException(String what) { super(what); } } fluidsynth-1.1.9/bindings/fluidsynth_jni/java/fluidsynth/Sample.java000066400000000000000000000016031322272076000257400ustar00rootroot00000000000000package fluidsynth; public class Sample { protected int sampleNum = -1; protected int rootkey; protected String filename; public Sample(String filename, int rootkey) throws FluidException { sampleNum = newSample(filename, rootkey); if (sampleNum < 0) { throw new FluidException("Failed to load the sample (err=" + sampleNum + ")"); } this.filename = filename; this.rootkey = rootkey; } protected void finalize() { if (sampleNum >= 0) { deleteSample(sampleNum); sampleNum = -1; } } public int getRootKey() { return rootkey; } public String getFileName() { return filename; } int getSampleNum() { return sampleNum; } protected native int newSample(String filename, int rootkey); protected native void deleteSample(int sampleNum); } fluidsynth-1.1.9/bindings/fluidsynth_jni/java/fluidsynth/Synth.java000066400000000000000000000030051322272076000256220ustar00rootroot00000000000000package fluidsynth; public class Synth { protected int synth = -1; static { System.loadLibrary("fluidsynth_jni"); } public Synth() throws FluidException { synth = newSynth(); if (synth < 0) { throw new FluidException("Low-level initialization of the synthesizer failed"); } } protected void finalize() { if (synth >= 0) { deleteSynth(synth); synth = -1; } } public void add(Sample sample, int bank, int preset, int lokey, int hikey) throws FluidException { if (add(synth, sample.getSampleNum(), bank, preset, lokey, hikey) != 0) { throw new FluidException("Failed to add the sample"); } } public void remove(Sample sample, int bank, int preset) throws FluidException { if (remove(synth, sample.getSampleNum(), bank, preset) != 0) { throw new FluidException("Failed to remove the sample"); } } public void loadSoundFont(String filename) throws FluidException { if (loadSoundFont(synth, filename) != 0) { throw new FluidException("Failed to load the SoundFont"); } } protected native int newSynth(); protected native void deleteSynth(int synth); protected native int add(int synth, int sample, int bank, int preset, int lokey, int hikey); protected native int remove(int synth, int sample, int bank, int preset); protected native int loadSoundFont(int synth, String filename); } fluidsynth-1.1.9/bindings/fluidsynth_jni/java/fluidsynth/Test.java000066400000000000000000000027531322272076000254450ustar00rootroot00000000000000package fluidsynth; import java.io.*; public class Test { static public void main(String[] argv) throws Exception { try{ Synth synth = new Synth(); synth.add(new Sample("[accord guitar].wav", 55), 0, 0, 50,58); synth.add(new Sample("cabrel.wav_14.wav", 60), 0, 0, 59,59); synth.add(new Sample("cabrel.wav_16.wav", 60), 0, 0, 60,60); synth.add(new Sample("cabrel.wav_15.wav", 62), 0, 0, 61, 61); synth.add(new Sample("cabrel.wav_17.wav", 62), 0, 0, 62, 62); synth.add(new Sample("[TIR].wav", 64), 0, 0, 63,63); synth.add(new Sample("cabrel.wav_18.wav", 64), 0, 0, 64,64); synth.add(new Sample("cabrel.wav_5.wav", 64), 0, 0, 65,65); synth.add(new Sample("cabrel.wav_7.wav", 64), 0, 0, 66,66); synth.add(new Sample("cabrel.wav_8.wav", 64), 0, 0, 67,67); synth.add(new Sample("cabrel.wav_19.wav", 64), 0, 0, 68,68); synth.add(new Sample("cabrel.wav_9.wav", 65), 0, 0, 69,69); synth.add(new Sample("cabrel.wav_10.wav", 67), 0, 0, 70, 70); synth.add(new Sample("cabrel.wav_11.wav", 69), 0, 0, 71, 71); waitCR(); }catch(Exception e){System.out.println(e);waitCR();} } public static void waitCR() { System.out.println ("Appuyez sur CR ..."); try {System.in.read();} catch (IOException e) {System.out.println(e);} } } fluidsynth-1.1.9/bindings/fluidsynth_jni/lib/000077500000000000000000000000001322272076000213105ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/lib/libsndfile.lib000066400000000000000000000172301322272076000241160ustar00rootroot00000000000000! / 1071680735 0 1420 ` A JJNNjj$$ll>>ff((JJhh@@__IMPORT_DESCRIPTOR_libsndfile__NULL_IMPORT_DESCRIPTORlibsndfile_NULL_THUNK_DATA__imp__sf_command_sf_command__imp__sf_open_sf_open__imp__sf_close_sf_close__imp__sf_seek_sf_seek__imp__sf_error_sf_error__imp__sf_perror_sf_perror__imp__sf_error_str_sf_error_str__imp__sf_error_number_sf_error_number__imp__sf_format_check_sf_format_check__imp__sf_read_raw_sf_read_raw__imp__sf_readf_short_sf_readf_short__imp__sf_readf_int_sf_readf_int__imp__sf_readf_float_sf_readf_float__imp__sf_readf_double_sf_readf_double__imp__sf_read_short_sf_read_short__imp__sf_read_int_sf_read_int__imp__sf_read_float_sf_read_float__imp__sf_read_double_sf_read_double__imp__sf_write_raw_sf_write_raw__imp__sf_writef_short_sf_writef_short__imp__sf_writef_int_sf_writef_int__imp__sf_writef_float_sf_writef_float__imp__sf_writef_double_sf_writef_double__imp__sf_write_short_sf_write_short__imp__sf_write_int_sf_write_int__imp__sf_write_float_sf_write_float__imp__sf_write_double_sf_write_double__imp__sf_strerror_sf_strerror__imp__sf_get_string_sf_get_string__imp__sf_set_string_sf_set_string__imp__sf_open_fd_sf_open_fd/ 1071680735 0 1430 ` " JNj$l>f(Jh@A "  ! "  !__IMPORT_DESCRIPTOR_libsndfile__NULL_IMPORT_DESCRIPTOR__imp__sf_close__imp__sf_command__imp__sf_error__imp__sf_error_number__imp__sf_error_str__imp__sf_format_check__imp__sf_get_string__imp__sf_open__imp__sf_open_fd__imp__sf_perror__imp__sf_read_double__imp__sf_read_float__imp__sf_read_int__imp__sf_read_raw__imp__sf_read_short__imp__sf_readf_double__imp__sf_readf_float__imp__sf_readf_int__imp__sf_readf_short__imp__sf_seek__imp__sf_set_string__imp__sf_strerror__imp__sf_write_double__imp__sf_write_float__imp__sf_write_int__imp__sf_write_raw__imp__sf_write_short__imp__sf_writef_double__imp__sf_writef_float__imp__sf_writef_int__imp__sf_writef_short_sf_close_sf_command_sf_error_sf_error_number_sf_error_str_sf_format_check_sf_get_string_sf_open_sf_open_fd_sf_perror_sf_read_double_sf_read_float_sf_read_int_sf_read_raw_sf_read_short_sf_readf_double_sf_readf_float_sf_readf_int_sf_readf_short_sf_seek_sf_set_string_sf_strerror_sf_write_double_sf_write_float_sf_write_int_sf_write_raw_sf_write_short_sf_writef_double_sf_writef_float_sf_writef_int_sf_writef_shortlibsndfile_NULL_THUNK_DATAlibsndfile.dll/ 1071680735 0 731 ` Lߌ? .debug$SEl@B.idata$2@0.idata$6@  libsndfile.dll(Microsoft (R) LINK libsndfile.dll@comp.id.idata$2@h.idata$6.idata$4@h.idata$5@h#<X__IMPORT_DESCRIPTOR_libsndfile__NULL_IMPORT_DESCRIPTORlibsndfile_NULL_THUNK_DATA libsndfile.dll/ 1071680735 0 254 ` Lߌ?.debug$SEd@B.idata$3@0 libsndfile.dll(Microsoft (R) LINK@comp.id__NULL_IMPORT_DESCRIPTORlibsndfile.dll/ 1071680735 0 285 ` Lߌ?.debug$SE@B.idata$5@0.idata$4@0 libsndfile.dll(Microsoft (R) LINK@comp.id libsndfile_NULL_THUNK_DATA libsndfile.dll/ 1071680735 0 45 ` Lߌ?_sf_closelibsndfile.dll libsndfile.dll/ 1071680735 0 47 ` Lߌ?_sf_commandlibsndfile.dll libsndfile.dll/ 1071680735 0 45 ` Lߌ?_sf_errorlibsndfile.dll libsndfile.dll/ 1071680735 0 52 ` Lߌ? _sf_error_numberlibsndfile.dlllibsndfile.dll/ 1071680735 0 49 ` Lߌ? _sf_error_strlibsndfile.dll libsndfile.dll/ 1071680735 0 52 ` Lߌ? _sf_format_checklibsndfile.dlllibsndfile.dll/ 1071680735 0 50 ` Lߌ?<_sf_get_stringlibsndfile.dlllibsndfile.dll/ 1071680735 0 44 ` Lߌ?_sf_openlibsndfile.dlllibsndfile.dll/ 1071680735 0 47 ` Lߌ?F_sf_open_fdlibsndfile.dll libsndfile.dll/ 1071680735 0 46 ` Lߌ?_sf_perrorlibsndfile.dlllibsndfile.dll/ 1071680735 0 51 ` Lߌ?_sf_read_doublelibsndfile.dll libsndfile.dll/ 1071680735 0 50 ` Lߌ?_sf_read_floatlibsndfile.dlllibsndfile.dll/ 1071680735 0 48 ` Lߌ?_sf_read_intlibsndfile.dlllibsndfile.dll/ 1071680735 0 48 ` Lߌ?_sf_read_rawlibsndfile.dlllibsndfile.dll/ 1071680735 0 50 ` Lߌ?_sf_read_shortlibsndfile.dlllibsndfile.dll/ 1071680735 0 52 ` Lߌ? _sf_readf_doublelibsndfile.dlllibsndfile.dll/ 1071680735 0 51 ` Lߌ?_sf_readf_floatlibsndfile.dll libsndfile.dll/ 1071680735 0 49 ` Lߌ?_sf_readf_intlibsndfile.dll libsndfile.dll/ 1071680735 0 51 ` Lߌ?_sf_readf_shortlibsndfile.dll libsndfile.dll/ 1071680735 0 44 ` Lߌ?_sf_seeklibsndfile.dlllibsndfile.dll/ 1071680735 0 50 ` Lߌ?=_sf_set_stringlibsndfile.dlllibsndfile.dll/ 1071680735 0 48 ` Lߌ?2_sf_strerrorlibsndfile.dlllibsndfile.dll/ 1071680735 0 52 ` Lߌ? (_sf_write_doublelibsndfile.dlllibsndfile.dll/ 1071680735 0 51 ` Lߌ?'_sf_write_floatlibsndfile.dll libsndfile.dll/ 1071680735 0 49 ` Lߌ?&_sf_write_intlibsndfile.dll libsndfile.dll/ 1071680735 0 49 ` Lߌ? _sf_write_rawlibsndfile.dll libsndfile.dll/ 1071680735 0 51 ` Lߌ?%_sf_write_shortlibsndfile.dll libsndfile.dll/ 1071680735 0 53 ` Lߌ?!$_sf_writef_doublelibsndfile.dll libsndfile.dll/ 1071680735 0 52 ` Lߌ? #_sf_writef_floatlibsndfile.dlllibsndfile.dll/ 1071680735 0 50 ` Lߌ?"_sf_writef_intlibsndfile.dlllibsndfile.dll/ 1071680735 0 52 ` Lߌ? !_sf_writef_shortlibsndfile.dllfluidsynth-1.1.9/bindings/fluidsynth_jni/src/000077500000000000000000000000001322272076000213315ustar00rootroot00000000000000fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_Sample.cpp000066400000000000000000000007721322272076000255350ustar00rootroot00000000000000#include "fluidsynth_Sample.h" #include "fluidsynth_jni.h" JNIEXPORT jint JNICALL Java_fluidsynth_Sample_newSample(JNIEnv *env, jobject obj, jstring filename, jint rootkey) { const char *cfilename = env->GetStringUTFChars(filename, 0); int err = fluid_jni_new_sample(cfilename, rootkey); env->ReleaseStringUTFChars(filename, cfilename); return err; } JNIEXPORT void JNICALL Java_fluidsynth_Sample_deleteSample(JNIEnv *env, jobject obj, jint samplenum) { fluid_jni_delete_sample(samplenum); } fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_Sample.h000066400000000000000000000011641322272076000251760ustar00rootroot00000000000000/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class fluidsynth_Sample */ #ifndef _Included_fluidsynth_Sample #define _Included_fluidsynth_Sample #ifdef __cplusplus extern "C" { #endif /* * Class: fluidsynth_Sample * Method: newSample * Signature: (Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_fluidsynth_Sample_newSample (JNIEnv *, jobject, jstring, jint); /* * Class: fluidsynth_Sample * Method: deleteSample * Signature: (I)V */ JNIEXPORT void JNICALL Java_fluidsynth_Sample_deleteSample (JNIEnv *, jobject, jint); #ifdef __cplusplus } #endif #endif fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_Synth.cpp000066400000000000000000000020041322272076000254070ustar00rootroot00000000000000#include "fluidsynth_Synth.h" #include "fluidsynth_jni.h" JNIEXPORT jint JNICALL Java_fluidsynth_Synth_newSynth(JNIEnv *env, jobject obj) { return fluid_jni_new_synth(); } JNIEXPORT void JNICALL Java_fluidsynth_Synth_deleteSynth(JNIEnv *env, jobject obj, jint synth) { fluid_jni_delete_synth(synth); } JNIEXPORT jint JNICALL Java_fluidsynth_Synth_add(JNIEnv *env, jobject obj, jint synth, jint samplenum, jint bank, jint preset, jint lokey, jint hikey) { return fluid_jni_add(samplenum, bank, preset, lokey, hikey); } JNIEXPORT jint JNICALL Java_fluidsynth_Synth_remove(JNIEnv *env, jobject obj, jint synth, jint samplenum, jint bank, jint preset) { return fluid_jni_remove(samplenum, bank, preset); } JNIEXPORT jint JNICALL Java_fluidsynth_Synth_loadSoundFont(JNIEnv *env, jobject obj, jint synth, jstring filename) { const char *cfilename = env->GetStringUTFChars(filename, 0); int err = fluid_jni_sfload(cfilename); env->ReleaseStringUTFChars(filename, cfilename); return err; } fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_Synth.h000066400000000000000000000020571322272076000250640ustar00rootroot00000000000000/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class fluidsynth_Synth */ #ifndef _Included_fluidsynth_Synth #define _Included_fluidsynth_Synth #ifdef __cplusplus extern "C" { #endif /* * Class: fluidsynth_Synth * Method: newSynth * Signature: ()I */ JNIEXPORT jint JNICALL Java_fluidsynth_Synth_newSynth (JNIEnv *, jobject); /* * Class: fluidsynth_Synth * Method: deleteSynth * Signature: (I)V */ JNIEXPORT void JNICALL Java_fluidsynth_Synth_deleteSynth (JNIEnv *, jobject, jint); /* * Class: fluidsynth_Synth * Method: add * Signature: (IIIIII)I */ JNIEXPORT jint JNICALL Java_fluidsynth_Synth_add (JNIEnv *, jobject, jint, jint, jint, jint, jint, jint); /* * Class: fluidsynth_Synth * Method: remove * Signature: (IIII)I */ JNIEXPORT jint JNICALL Java_fluidsynth_Synth_remove (JNIEnv *, jobject, jint, jint, jint, jint); JNIEXPORT jint JNICALL Java_fluidsynth_Synth_loadSoundFont (JNIEnv *env, jobject obj, jint synth, jstring filename); #ifdef __cplusplus } #endif #endif fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_jni.cpp000066400000000000000000000113531322272076000250710ustar00rootroot00000000000000#include #include "fluidsynth_jni.h" const int fluid_jni_maxsamples = 1024; static int _initialized = 0; static fluid_settings_t* _settings = 0; static fluid_synth_t* _synth = 0; static fluid_audio_driver_t* _adriver = 0; static fluid_midi_driver_t* _mdriver = 0; static fluid_sfont_t* _sfloader = 0; static fluid_sample_t* _sample[fluid_jni_maxsamples]; void fluid_jni_init() { if (_initialized == 0) { _initialized++; for (int i = 0; i < fluid_jni_maxsamples; i++) { _sample[i] = 0; } } } int fluid_jni_new_synth() { if (_synth != 0) { return -1; } fluid_jni_init(); _settings = new_fluid_settings(); if (_settings == 0) { goto error_recovery; } fluid_settings_setstr(_settings, "midi.driver", "midishare"); _synth = new_fluid_synth(_settings); if (_synth == 0) { goto error_recovery; } _adriver = new_fluid_audio_driver(_settings, _synth); if (_adriver == 0) { goto error_recovery; } _mdriver = new_fluid_midi_driver(_settings, fluid_synth_handle_midi_event, _synth); if (_mdriver == 0) { goto error_recovery; } _sfloader = fluid_ramsfont_create_sfont(); if (_sfloader == 0) { goto error_recovery; } fluid_ramsfont_set_name((fluid_ramsfont_t*) _sfloader->data, "Tada"); fluid_synth_add_sfont(_synth, _sfloader); return 0; error_recovery: fluid_jni_delete_synth(0); return -1; } int fluid_jni_delete_synth(int num) { if (_mdriver) { delete_fluid_midi_driver(_mdriver); _mdriver = 0; } if (_adriver) { delete_fluid_audio_driver(_adriver); _adriver = 0; } if (_synth) { delete_fluid_synth(_synth); _synth = 0; } if (_settings) { delete_fluid_settings(_settings); _settings = 0; } return 0; } int fluid_jni_sfload(const char* filename) { if (_synth == 0) { return -1; } fluid_synth_sfload(_synth, filename, 1); return 0; } int fluid_jni_add(int samplenum, int bank, int preset, int lokey, int hikey) { if (_synth == 0) { return -1; } if (_sfloader == 0) { return -1; } fluid_sample_t* sample = fluid_jni_get_sample(samplenum); if (sample == 0) { return -2; } if (fluid_ramsfont_add_izone((fluid_ramsfont_t*) _sfloader->data, bank, preset, sample, lokey, hikey) != 0) { return -3; } fluid_synth_program_select(_synth, 0, _sfloader->id, bank, preset); return 0; } int fluid_jni_remove(int samplenum, int bank, int preset) { if (_synth == 0) { return -1; } fluid_sample_t* sample = fluid_jni_get_sample(samplenum); if (sample == 0) { return -2; } if (fluid_ramsfont_remove_izone((fluid_ramsfont_t*) _sfloader->data, bank, preset, sample) != 0) { return -3; } return 0; } int fluid_jni_get_sample_num() { for (int i = 0; i < fluid_jni_maxsamples; i++) { if (_sample[i] == 0) { return i; } } return -1; } int fluid_jni_new_sample(const char* filename, int rootkey) { SF_INFO sfinfo; SNDFILE* sndfile = 0; fluid_sample_t* sample = 0; short *data = 0; sf_count_t count; int err; int num = fluid_jni_get_sample_num(); if (num < 0) { return -1; } sndfile = sf_open(filename, SFM_READ, &sfinfo) ; if (sndfile == 0) { return -2; } //printf("fluid_jni_new_sample: channels=%i, srate=%i, frames=%i\n", // sfinfo.channels, sfinfo.samplerate, sfinfo.frames); if (sfinfo.channels != 1) { err = -3; goto error_recovery; } if (sfinfo.samplerate != 44100) { err = -4; goto error_recovery; } sample = new_fluid_ramsample(); if (sample == 0) { err = -5; goto error_recovery; } data = new short[sfinfo.frames]; if (data == 0) { err = -6; goto error_recovery; } // printf("fluid_jni_new_sample 2\n"); count = sf_readf_short(sndfile, data, sfinfo.frames); if (count != sfinfo.frames) { err = -7; goto error_recovery; } //printf("fluid_jni_new_sample 3\n"); if (fluid_sample_set_sound_data(sample, data, sfinfo.frames, 1, rootkey) != 0) { err = -8; goto error_recovery; } //printf("fluid_jni_new_sample 4: sample=%p\n", sample); _sample[num] = sample; sf_close(sndfile); delete data; return num; error_recovery: if (sndfile) { sf_close(sndfile); } if (sample) { delete_fluid_ramsample(sample); } if (data) { delete data; } return err; } fluid_sample_t* fluid_jni_get_sample(int num) { if ((num >= 0) && (num < fluid_jni_maxsamples) && (_sample[num] != 0)) { return _sample[num]; } return 0; } int fluid_jni_delete_sample(int num) { if ((num >= 0) && (num < fluid_jni_maxsamples) && (_sample[num] != 0)) { delete_fluid_ramsample(_sample[num]); _sample[num] = 0; } return 0; } fluidsynth-1.1.9/bindings/fluidsynth_jni/src/fluidsynth_jni.h000066400000000000000000000010211322272076000245250ustar00rootroot00000000000000 #ifndef _FLUID_JNI_H #define _FLUID_JNI_H #include int fluid_jni_new_synth(void); int fluid_jni_delete_synth(int num); int fluid_jni_add(int samplenum, int bank, int preset, int lokey, int hikey); int fluid_jni_remove(int samplenum, int bank, int preset); int fluid_jni_sfload(const char* filename); int fluid_jni_new_sample(const char* filename, int rootkey); int fluid_jni_delete_sample(int num); int fluid_jni_get_sample_num(void); fluid_sample_t* fluid_jni_get_sample(int num); #endif /* _FLUID_JNI_H */ fluidsynth-1.1.9/cmake_admin/000077500000000000000000000000001322272076000161445ustar00rootroot00000000000000fluidsynth-1.1.9/cmake_admin/CheckDIRSymbolExists.cmake000066400000000000000000000066671322272076000231270ustar00rootroot00000000000000# - Check if the DIR symbol exists like in AC_HEADER_DIRENT. # CHECK_DIRSYMBOL_EXISTS(FILES VARIABLE) # # FILES - include files to check # VARIABLE - variable to return result # # This module is a small but important variation on CheckSymbolExists.cmake. # The symbol always searched for is DIR, and the test programme follows # the AC_HEADER_DIRENT test programme rather than the CheckSymbolExists.cmake # test programme which always fails since DIR tends to be typedef'd # rather than #define'd. # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link MACRO(CHECK_DIRSYMBOL_EXISTS FILES VARIABLE) IF(NOT DEFINED ${VARIABLE}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") SET(MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) IF(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") ELSE(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS) ENDIF(CMAKE_REQUIRED_LIBRARIES) IF(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") ELSE(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES) ENDIF(CMAKE_REQUIRED_INCLUDES) FOREACH(FILE ${FILES}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") ENDFOREACH(FILE) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nint main()\n{if ((DIR *) 0) return 0;}\n") CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c" @ONLY) MESSAGE(STATUS "Looking for DIR in ${FILES}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS} "${CHECK_DIRSYMBOL_EXISTS_LIBS}" "${CMAKE_DIRSYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - found") SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeOutput.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "passed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - not found.") SET(${VARIABLE} "" CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "failed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) ENDIF(NOT DEFINED ${VARIABLE}) ENDMACRO(CHECK_DIRSYMBOL_EXISTS) fluidsynth-1.1.9/cmake_admin/CheckPrototypeExists.cmake000066400000000000000000000023741322272076000233170ustar00rootroot00000000000000# - Check if the prototype for a function exists. # CHECK_PROTOTYPE_EXISTS (FUNCTION HEADER VARIABLE) # # FUNCTION - the name of the function you are looking for # HEADER - the header(s) where the prototype should be declared # VARIABLE - variable to store the result # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # Copyright (c) 2006, Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. INCLUDE(CheckCSourceCompiles) MACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) SET(_INCLUDE_FILES) FOREACH (it ${_HEADER}) SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") ENDFOREACH (it) SET(_CHECK_PROTO_EXISTS_SOURCE_CODE " ${_INCLUDE_FILES} int main() { #ifndef ${_SYMBOL} int i = sizeof(&${_SYMBOL}); #endif return 0; } ") CHECK_C_SOURCE_COMPILES("${_CHECK_PROTO_EXISTS_SOURCE_CODE}" ${_RESULT}) ENDMACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) fluidsynth-1.1.9/cmake_admin/CheckSTDC.cmake000066400000000000000000000023151322272076000206420ustar00rootroot00000000000000message(STATUS "Checking whether system has ANSI C header files") include(CheckPrototypeExists) include(CheckIncludeFiles) check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" StandardHeadersExist) if(StandardHeadersExist) check_prototype_exists(memchr string.h memchrExists) if(memchrExists) check_prototype_exists(free stdlib.h freeExists) if(freeExists) message(STATUS "ANSI C header files - found") set(STDC_HEADERS 1 CACHE INTERNAL "System has ANSI C header files") set(HAVE_STRINGS_H 1) set(HAVE_STRING_H 1) set(HAVE_FLOAT_H 1) set(HAVE_STDLIB_H 1) set(HAVE_STDDEF_H 1) set(HAVE_STDINT_H 1) set(HAVE_INTTYPES_H 1) set(HAVE_DLFCN_H 1) endif(freeExists) endif(memchrExists) endif(StandardHeadersExist) if(NOT STDC_HEADERS) message(STATUS "ANSI C header files - not found") set(STDC_HEADERS 0 CACHE INTERNAL "System has ANSI C header files") endif(NOT STDC_HEADERS) check_include_files(unistd.h HAVE_UNISTD_H) include(CheckDIRSymbolExists) check_dirsymbol_exists("sys/stat.h;sys/types.h;dirent.h" HAVE_DIRENT_H) if (HAVE_DIRENT_H) set(HAVE_SYS_STAT_H 1) set(HAVE_SYS_TYPES_H 1) endif (HAVE_DIRENT_H) fluidsynth-1.1.9/cmake_admin/DefaultDirs.cmake000066400000000000000000000071311322272076000213560ustar00rootroot00000000000000# Several directory names used by FluidSynth to install files # the variable names are similar to the KDE4 build system # DEFAULT_SOUNDFONT - automatically loaded in some use cases if ( WIN32 ) set (DEFAULT_SOUNDFONT "C:\\\\soundfonts\\\\default.sf2" CACHE STRING "Default soundfont file") else ( WIN32 ) set (DEFAULT_SOUNDFONT "share/soundfonts/default.sf2" CACHE STRING "Default soundfont file") endif ( WIN32 ) mark_as_advanced (DEFAULT_SOUNDFONT) # BUNDLE_INSTALL_DIR - Mac only: the directory for application bundles set (BUNDLE_INSTALL_DIR "/Applications" CACHE STRING "The install dir for application bundles") mark_as_advanced (BUNDLE_INSTALL_DIR) # FRAMEWORK_INSTALL_DIR - Mac only: the directory for framework bundles set (FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "The install dir for framework bundles") mark_as_advanced (FRAMEWORK_INSTALL_DIR) # BIN_INSTALL_DIR - the directory where executables will be installed set (BIN_INSTALL_DIR "bin" CACHE STRING "The install dir for executables") mark_as_advanced (BIN_INSTALL_DIR) # SBIN_INSTALL_DIR - the directory where system executables will be installed set (SBIN_INSTALL_DIR "sbin" CACHE STRING "The install dir for system executables") mark_as_advanced (SBIN_INSTALL_DIR) # LIB_INSTALL_DIR - the directory where libraries will be installed set (LIB_INSTALL_DIR "lib" CACHE STRING "The install dir for libraries") mark_as_advanced (LIB_INSTALL_DIR) # INCLUDE_INSTALL_DIR - the install dir for header files set (INCLUDE_INSTALL_DIR "include" CACHE STRING "The install dir for headers") mark_as_advanced (INCLUDE_INSTALL_DIR) # DATA_INSTALL_DIR - the base install directory for data files set (DATA_INSTALL_DIR "share" CACHE STRING "The base install dir for data files") mark_as_advanced (DATA_INSTALL_DIR) # DOC_INSTALL_DIR - the install dir for documentation set (DOC_INSTALL_DIR "share/doc" CACHE STRING "The install dir for documentation") mark_as_advanced (DOC_INSTALL_DIR) # INFO_INSTALL_DIR - the info install dir set (INFO_INSTALL_DIR "share/info" CACHE STRING "The info install dir") mark_as_advanced (INFO_INSTALL_DIR) # MAN_INSTALL_DIR - the man pages install dir set (MAN_INSTALL_DIR "share/man/man1" CACHE STRING "The man pages install dir") mark_as_advanced (MAN_INSTALL_DIR) # SYSCONF_INSTALL_DIR - the config file install dir set (SYSCONF_INSTALL_DIR "/etc" CACHE PATH "The sysconfig install dir") mark_as_advanced (SYSCONF_INSTALL_DIR) # XDG_APPS_INSTALL_DIR - the XDG apps dir, where .desktop files are installed set (XDG_APPS_INSTALL_DIR "share/applications" CACHE STRING "The XDG apps dir") mark_as_advanced (XDG_APPS_INSTALL_DIR) # XDG_MIME_INSTALL_DIR - the XDG mimetypes install dir set (XDG_MIME_INSTALL_DIR "share/mime/packages" CACHE STRING "The install dir for the xdg mimetypes") mark_as_advanced (XDG_MIME_INSTALL_DIR) # DBUS_INTERFACES_INSTALL_DIR - the directory where dbus interfaces are # installed set (DBUS_INTERFACES_INSTALL_DIR "share/dbus-1/interfaces" CACHE STRING "The dbus interfaces install dir") mark_as_advanced (DBUS_INTERFACES_INSTALL_DIR) # DBUS_SERVICES_INSTALL_DIR - the directory where dbus services are installed set (DBUS_SERVICES_INSTALL_DIR "share/dbus-1/services" CACHE STRING "The dbus services install dir") mark_as_advanced (DBUS_SERVICES_INSTALL_DIR) # DBUS_SYSTEM_SERVICES_INSTALL_DIR - the directory where dbus system services # are installed set (DBUS_SYSTEM_SERVICES_INSTALL_DIR "share/dbus-1/system-services" CACHE STRING "The dbus system services install dir") mark_as_advanced (DBUS_SYSTEM_SERVICES_INSTALL_DIR) fluidsynth-1.1.9/cmake_admin/FindMidiShare.cmake000066400000000000000000000012721322272076000216160ustar00rootroot00000000000000# Try to find the READLINE library # MidiShare_FOUND - system has MidiShare # MidiShare_INCLUDE_DIR - MidiShare include directory # MidiShare_LIBS - Libraries needed to use MidiShare if ( MidiShare_INCLUDE_DIR AND MidiShare_LIBS ) set ( MidiShare_FIND_QUIETLY TRUE ) endif ( MidiShare_INCLUDE_DIR AND MidiShare_LIBS ) find_path ( MidiShare_INCLUDE_DIR NAMES MidiShare.h ) find_library ( MidiShare_LIBS NAMES MidiShare ) include ( FindPackageHandleStandardArgs ) find_package_handle_standard_args( MidiShare DEFAULT_MSG MidiShare_INCLUDE_DIR MidiShare_LIBS ) mark_as_advanced( MidiShare_INCLUDE_DIR MidiShare_LIBS ) fluidsynth-1.1.9/cmake_admin/FindOSS.cmake000066400000000000000000000021511322272076000204120ustar00rootroot00000000000000# - Find Oss # Find Oss headers and libraries. # # OSS_INCLUDE_DIR - where to find soundcard.h, etc. # OSS_FOUND - True if Oss found. FIND_PATH(LINUX_OSS_INCLUDE_DIR "linux/soundcard.h" "/usr/include" "/usr/local/include" ) FIND_PATH(SYS_OSS_INCLUDE_DIR "sys/soundcard.h" "/usr/include" "/usr/local/include" ) FIND_PATH(MACHINE_OSS_INCLUDE_DIR "machine/soundcard.h" "/usr/include" "/usr/local/include" ) SET(OSS_FOUND FALSE) if ( NOT WIN32 ) IF(LINUX_OSS_INCLUDE_DIR) SET(OSS_FOUND TRUE) SET(OSS_INCLUDE_DIR ${LINUX_OSS_INCLUDE_DIR}) SET(HAVE_LINUX_SOUNDCARD_H 1) ENDIF() IF(SYS_OSS_INCLUDE_DIR) SET(OSS_FOUND TRUE) SET(OSS_INCLUDE_DIR ${SYS_OSS_INCLUDE_DIR}) SET(HAVE_SYS_SOUNDCARD_H 1) ENDIF() IF(MACHINE_OSS_INCLUDE_DIR) SET(OSS_FOUND TRUE) SET(OSS_INCLUDE_DIR ${MACHINE_OSS_INCLUDE_DIR}) SET(HAVE_MACHINE_SOUNDCARD_H 1) ENDIF() ENDIF(NOT WIN32) MARK_AS_ADVANCED ( OSS_FOUND OSS_INCLUDE_DIR LINUX_OSS_INCLUDE_DIR SYS_OSS_INCLUDE_DIR MACHINE_OSS_INCLUDE_DIR ) fluidsynth-1.1.9/cmake_admin/FindReadline.cmake000066400000000000000000000015161322272076000214750ustar00rootroot00000000000000# Try to find the READLINE library # HAVE_READLINE - system has READLINE # READLINE_INCLUDE_DIR - READLINE include directory # READLINE_LIBRARIES - Libraries needed to use READLINE if ( READLINE_INCLUDE_DIR AND READLINE_LIBRARIES ) set ( READLINE_FIND_QUIETLY TRUE ) endif ( READLINE_INCLUDE_DIR AND READLINE_LIBRARIES ) find_path ( READLINE_INCLUDE_DIR NAMES history.h readline/history.h ) find_library ( READLINE_LIBRARIES NAMES readline ) if ( READLINE_INCLUDE_DIR ) set ( HAVE_READLINE TRUE CACHE TYPE BOOL ) endif ( READLINE_INCLUDE_DIR ) include ( FindPackageHandleStandardArgs ) FIND_PACKAGE_HANDLE_STANDARD_ARGS( READLINE DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARIES ) mark_as_advanced( READLINE_INCLUDE_DIR READLINE_LIBRARIES HAVE_READLINE ) fluidsynth-1.1.9/cmake_admin/Makefile.am000066400000000000000000000006121322272076000201770ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in EXTRA_DIST = CheckDIRSymbolExists.cmake \ CheckPrototypeExists.cmake \ CheckSTDC.cmake \ cmake_uninstall.cmake.in \ DefaultDirs.cmake \ FindMidiShare.cmake \ FindOSS.cmake \ FindPthreads.cmake \ FindReadline.cmake \ report.cmake \ TestInline.cmake \ TestVLA.cmake \ UnsetPkgConfig.cmake fluidsynth-1.1.9/cmake_admin/TestInline.cmake000066400000000000000000000011501322272076000212210ustar00rootroot00000000000000include ( CheckCSourceCompiles ) foreach ( _keyword "inline" "__inline__" "__inline" ) if ( NOT INLINE_KEYWORD ) set ( CMAKE_REQUIRED_DEFINITIONS "-DTESTKEYWORD=${_keyword}" ) check_c_source_compiles ( "typedef int foo_t; static TESTKEYWORD foo_t static_foo(){return 0;} foo_t foo(){return 0;} int main(int argc, char *argv[]){return 0;}" _have_${_keyword} ) if ( _have_${_keyword} ) set ( INLINE_KEYWORD ${_keyword} ) endif ( _have_${_keyword} ) endif ( NOT INLINE_KEYWORD ) endforeach ( _keyword ) fluidsynth-1.1.9/cmake_admin/TestVLA.cmake000066400000000000000000000004301322272076000204250ustar00rootroot00000000000000include ( CheckCSourceCompiles ) if ( NOT SUPPORTS_VLA ) check_c_source_compiles ( "int main(int argc, char *argv[]){int arr[argc]; return 0;}" _have_vla ) if ( _have_vla ) set ( SUPPORTS_VLA 1 ) endif ( _have_vla ) endif ( NOT SUPPORTS_VLA ) fluidsynth-1.1.9/cmake_admin/UnsetPkgConfig.cmake000066400000000000000000000010411322272076000220300ustar00rootroot00000000000000macro ( unset_pkg_config _prefix ) unset ( ${_prefix}_VERSION CACHE ) unset ( ${_prefix}_PREFIX CACHE ) unset ( ${_prefix}_CFLAGS CACHE ) unset ( ${_prefix}_CFLAGS_OTHER CACHE ) unset ( ${_prefix}_LDFLAGS CACHE ) unset ( ${_prefix}_LDFLAGS_OTHER CACHE ) unset ( ${_prefix}_LIBRARIES CACHE ) unset ( ${_prefix}_INCLUDEDIR CACHE ) unset ( ${_prefix}_INCLUDE_DIRS CACHE ) unset ( ${_prefix}_LIBDIR CACHE ) unset ( ${_prefix}_LIBRARY_DIRS CACHE ) unset ( __pkg_config_checked_${_prefix} CACHE ) endmacro ( unset_pkg_config ) fluidsynth-1.1.9/cmake_admin/cmake_uninstall.cmake.in000066400000000000000000000015551322272076000227320ustar00rootroot00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"${file}\"") IF(EXISTS "${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF("${rm_retval}" STREQUAL 0) ELSE("${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"") ENDIF("${rm_retval}" STREQUAL 0) ELSE(EXISTS "${file}") MESSAGE(STATUS "File \"${file}\" does not exist.") ENDIF(EXISTS "${file}") ENDFOREACH(file) fluidsynth-1.1.9/cmake_admin/report.cmake000066400000000000000000000100301322272076000204530ustar00rootroot00000000000000message( "\n**************************************************************\n" "Summary:" ) message( "Build type: " ${CMAKE_BUILD_TYPE} ) if ( LIBSNDFILE_SUPPORT ) if ( LIBSNDFILE_HASVORBIS ) message ( "libsndfile: yes (with ogg vorbis support)" ) else ( LIBSNDFILE_HASVORBIS ) message ( "libsndfile: yes" ) endif ( LIBSNDFILE_HASVORBIS ) else ( LIBSNDFILE_SUPPORT ) message ( "libsndfile: no (raw audio file rendering only)" ) endif ( LIBSNDFILE_SUPPORT ) if ( DBUS_SUPPORT ) message ( "D-Bus: yes" ) else ( DBUS_SUPPORT ) message ( "D-Bus: no" ) endif ( DBUS_SUPPORT ) if ( PULSE_SUPPORT ) message ( "PulseAudio: yes" ) else ( PULSE_SUPPORT ) message ( "PulseAudio: no" ) endif ( PULSE_SUPPORT ) if ( JACK_SUPPORT ) message ( "JACK: yes" ) else ( JACK_SUPPORT ) message ( "JACK: no" ) endif ( JACK_SUPPORT ) if ( ALSA_SUPPORT ) message ( "ALSA: yes" ) else ( ALSA_SUPPORT ) message ( "ALSA: no" ) endif ( ALSA_SUPPORT ) if ( PORTAUDIO_SUPPORT ) message ( "PortAudio: yes" ) else ( PORTAUDIO_SUPPORT ) message ( "PortAudio: no" ) endif ( PORTAUDIO_SUPPORT ) if ( OSS_SUPPORT ) message ( "OSS: yes" ) else ( OSS_SUPPORT ) message ( "OSS: no" ) endif ( OSS_SUPPORT ) if ( MIDISHARE_SUPPORT ) message ( "MidiShare: yes" ) else ( MIDISHARE_SUPPORT ) message ( "MidiShare: no" ) endif ( MIDISHARE_SUPPORT ) if ( COREAUDIO_SUPPORT ) message ( "CoreAudio: yes" ) else ( COREAUDIO_SUPPORT ) message ( "CoreAudio: no" ) endif ( COREAUDIO_SUPPORT ) if ( COREMIDI_SUPPORT ) message ( "CoreMIDI: yes" ) else ( COREMIDI_SUPPORT ) message ( "CoreMIDI: no" ) endif ( COREMIDI_SUPPORT ) if ( WINDOWS_SUPPORT ) message ( "Windows: yes" ) else ( WINDOWS_SUPPORT ) message ( "Windows: no" ) endif ( WINDOWS_SUPPORT ) if ( LADSPA_SUPPORT ) message ( "LADSPA support: yes" ) else ( LADSPA_SUPPORT ) message ( "LADSPA support: no" ) endif ( LADSPA_SUPPORT ) if ( LASH_SUPPORT ) message ( "LASH support: yes (NOTE: GPL library)" ) else ( LASH_SUPPORT ) message ( "LASH support: no" ) endif ( LASH_SUPPORT ) if ( LADCCA_SUPPORT ) message ( "LADCCA support: yes (NOTE: GPL library)" ) else ( LADCCA_SUPPORT ) message ( "LADCCA support: no" ) endif ( LADCCA_SUPPORT ) if ( DART_SUPPORT ) message ( "OS/2 DART support: yes" ) else ( DART_SUPPORT ) message ( "OS/2 DART support: no" ) endif ( DART_SUPPORT ) if ( AUFILE_SUPPORT ) message ( "Audio to file driver: yes" ) else ( AUFILE_SUPPORT ) message ( "Audio to file driver: no" ) endif ( AUFILE_SUPPORT ) if ( IPV6_SUPPORT ) message ( "IPV6 Support : yes" ) else ( IPV6_SUPPORT ) message ( "IPV6 Support : no" ) endif ( IPV6_SUPPORT ) if ( WITH_READLINE ) message ( "Readline: yes (NOTE: GPL library)" ) else ( WITH_READLINE ) message ( "Readline: no" ) endif ( WITH_READLINE ) if ( WITH_FLOAT ) message ( "Samples type=float: yes" ) else ( WITH_FLOAT ) message ( "Samples type=float: no (using double)" ) endif ( WITH_FLOAT ) if ( WITH_PROFILING ) message ( "Profiling: yes" ) else ( WITH_PROFILING ) message ( "Profiling: no" ) endif ( WITH_PROFILING ) if ( ENABLE_DEBUG ) message ( "Debug: yes" ) else ( ENABLE_DEBUG ) message ( "Debug: no" ) endif ( ENABLE_DEBUG ) if ( ENABLE_TRAPONFPE ) message ( "Trap on FPE (debug): yes" ) else ( ENABLE_TRAPONFPE ) message ( "Trap on FPE (debug): no" ) endif ( ENABLE_TRAPONFPE ) if ( ENABLE_FPECHECK ) message ( "Check FPE (debug): yes" ) else ( ENABLE_FPECHECK ) message ( "Check FPE (debug): no" ) endif ( ENABLE_FPECHECK ) message ( "**************************************************************\n\n" ) fluidsynth-1.1.9/configure.ac000066400000000000000000000431261322272076000162100ustar00rootroot00000000000000dnl -------------------------------------------------- dnl configure.in for FluidSynth dnl -------------------------------------------------- AC_INIT(src/fluidsynth.c) dnl *** NOTE *** Don't forget to update library version below also FLUIDSYNTH_VERSION_MAJOR=1 FLUIDSYNTH_VERSION_MINOR=1 FLUIDSYNTH_VERSION_MICRO=9 FLUIDSYNTH_VERSION=$FLUIDSYNTH_VERSION_MAJOR.$FLUIDSYNTH_VERSION_MINOR.$FLUIDSYNTH_VERSION_MICRO AC_SUBST(FLUIDSYNTH_VERSION_MAJOR) AC_SUBST(FLUIDSYNTH_VERSION_MINOR) AC_SUBST(FLUIDSYNTH_VERSION_MICRO) AC_SUBST(FLUIDSYNTH_VERSION) AM_INIT_AUTOMAKE(fluidsynth, $FLUIDSYNTH_VERSION) dnl Convert to quoted string for version.h substitution FLUIDSYNTH_VERSION="\"$VERSION\"" dnl *** NOTICE *** dnl Update library version upon each release (follow these steps in order) dnl if any source code changes: lt_revision++ dnl if any interfaces added/removed/changed: lt_current++ and lt_revision=0 dnl if any interfaces have been added: lt_age++ dnl if any interfaces have been removed/changed (compatibility broken): lt_age=0 m4_define([lt_current], [6]) m4_define([lt_revision], [0]) m4_define([lt_age], [7]) LT_VERSION_INFO="lt_current:lt_revision:lt_age" AC_SUBST(LT_VERSION_INFO) AM_CONFIG_HEADER(src/config.h) AC_CONFIG_MACRO_DIR([m4]) CFLAGS="$CFLAGS" dnl Check for programs AC_PROG_CC AM_PROG_CC_C_O AC_PROG_INSTALL dnl AC_PROG_RANLIB AC_LIBTOOL_WIN32_DLL AM_PROG_LIBTOOL AC_PROG_MAKE_SET dnl Check for libraries AC_CHECK_LIB(pthread, pthread_create) dnl Check for header files AC_HEADER_STDC AC_CHECK_HEADERS(string.h stdlib.h stdio.h math.h errno.h stdarg.h unistd.h sys/mman.h sys/types.h sys/time.h sys/stat.h fcntl.h sys/socket.h netinet/in.h netinet/tcp.h arpa/inet.h limits.h pthread.h signal.h windows.h) dnl Compiler and machine specs AC_C_INLINE AC_C_BIGENDIAN LIBFLUID_LIBS="-lm" dnl Machine specific checks and defines case "${host_os}" in darwin*) AC_DEFINE(DARWIN, 1, [Define if building for Mac OS X Darwin]) ;; mingw*) mingw32_support="yes" CFLAGS="$CFLAGS -mms-bitfields" AC_DEFINE(MINGW32, 1, [Define if using the MinGW32 environment]) LIBFLUID_LIBS="-ldsound -lwinmm -lws2_32" LIBFLUID_CPPFLAGS="-DFLUIDSYNTH_DLL_EXPORTS" LIBFLUID_LDFLAGS="-no-undefined" FLUID_CPPFLAGS="-DFLUIDSYNTH_NOT_A_DLL" ;; os2*) LDFLAGS="$LDFLAGS -Zbin-files" LIBFLUID_LDFLAGS="-no-undefined" ;; solaris*) LDFLAGS="$LDFLAGS -lnsl -lsocket" ;; esac AC_SUBST(LIBFLUID_LIBS) AC_SUBST(LIBFLUID_CPPFLAGS) AC_SUBST(LIBFLUID_LDFLAGS) AC_SUBST(FLUID_CPPFLAGS) dnl Check for GNU LD AC_LIB_PROG_LD_GNU AM_CONDITIONAL(GNU_LD_SUPPORT, test "x$with_gnu_ld" = "xyes") AM_CONDITIONAL(MINGW32_SUPPORT, test "$mingw32_support" == "yes") AC_ARG_ENABLE(double, AS_HELP_STRING([--enable-double], [double floating point for dsp (default=float)]), ENABLE_FLOAT_SAMPLES=$enableval, ENABLE_FLOAT_SAMPLES=yes) if test "x$ENABLE_FLOAT_SAMPLES" = "xyes" ; then AC_DEFINE(WITH_FLOAT, 1, [Define to do all DSP in single floating point precision]) fi AC_ARG_ENABLE(profiling, AS_HELP_STRING([--enable-profiling], [profile the dsp code (default=no)]), profiling_flag=$enableval, profiling_flag=no) if test "x$profiling_flag" = "xyes" ; then AC_DEFINE(WITH_PROFILING, 1, [Define to profile the DSP code]) fi AC_ARG_ENABLE(ladspa, AS_HELP_STRING([--enable-ladspa], [Include LADSPA effect unit (default=no)]), ENABLE_LADSPA=$enableval, ENABLE_LADSPA=no) if test "x$ENABLE_LADSPA" = "xyes" ; then AC_DEFINE(LADSPA, 1, [Include the LADSPA Fx unit]) dnl LADSPA plugins are loaded as dynamic libraries. dnl In this case, -ldl is required for linking. AC_CHECK_LIB(dl, dlopen) fi AC_ARG_ENABLE(trap-on-fpe, AS_HELP_STRING([--enable-trap-on-fpe], [Enable SIGFPE trap on Floating Point Exceptions (debugging)]), ENABLE_TRAPONFPE=$enableval, ENABLE_TRAPONFPE=no) if test "$ENABLE_TRAPONFPE" = "yes"; then AC_DEFINE(TRAP_ON_FPE, 1, [Define to enable SIGFPE assertions]) fi AC_ARG_ENABLE(fpe-check, AS_HELP_STRING([--enable-fpe-check], [Enable Floating Point Exception checks and debug messages]), ENABLE_FPECHECK=$enableval, ENABLE_FPECHECK=no) if test "$ENABLE_FPECHECK" = "yes"; then AC_DEFINE(FPE_CHECK, 1, [Define to enable FPE checks]) fi AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debugging (default=no)]), ENABLE_DEBUG=$enableval) # Those are only valid for GCC if test "$GCC" = yes; then if test "$ENABLE_DEBUG" = "yes"; then CFLAGS="${CFLAGS} ${FCCFLAGS} -g -Wall -W -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wcast-align -Wstrict-prototypes -Wno-unused-parameter -Wno-cast-qual" AC_DEFINE(DEBUG, 1, [Define to activate debugging message]) else CFLAGS="${CFLAGS} ${FCCFLAGS} -O2 -fomit-frame-pointer -funroll-all-loops -finline-functions -Wall -W -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wcast-align -Wstrict-prototypes -Winline -Wno-unused-parameter -Wno-cast-qual" AC_DEFINE(DEBUG, 0, [Define to activate debugging message]) fi fi dnl Assert that glib and gthread are available PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.6.5 gthread-2.0 >= 2.6.5) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) dnl - Check for DBUS support AC_ARG_ENABLE(dbus-support, AS_HELP_STRING([--disable-dbus-support], [Do not compile with support for D-Bus (default=auto)]), enable_dbus_support=$enableval, enable_dbus_support="yes") if test "x$enable_dbus_support" != "xno"; then PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0.0, DBUS_SUPPORT=1, DBUS_SUPPORT=0) else DBUS_SUPPORT=0 fi if test "$DBUS_SUPPORT" = "1"; then AC_DEFINE(DBUS_SUPPORT, 1, [Define to enable D-Bus support]) fi AM_CONDITIONAL(DBUS_SUPPORT, test "$DBUS_SUPPORT" = "1") AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) dnl Check for libsndfile support AC_ARG_ENABLE(libsndfile-support, AS_HELP_STRING([--disable-libsndfile-support], [Do not compile libsndfile support (default=auto)]), enable_libsndfile_support=$enableval, enable_libsndfile_support="yes") if test "x$enable_libsndfile_support" != "xno"; then PKG_CHECK_MODULES(LIBSNDFILE, sndfile >= 1.0.0, LIBSNDFILE_SUPPORT=1, LIBSNDFILE_SUPPORT=0) else LIBSNDFILE_SUPPORT=0 fi if test "$LIBSNDFILE_SUPPORT" = "1"; then PKG_CHECK_MODULES(LIBSNDFILE_VORBIS, sndfile >= 1.0.18, LIBSNDFILE_HASVORBIS=1, LIBSNDFILE_HASVORBIS=0) AC_DEFINE(LIBSNDFILE_SUPPORT, 1, [Define to enable libsndfile support]) if test "$LIBSNDFILE_HASVORBIS" = "1"; then AC_DEFINE(LIBSNDFILE_HASVORBIS, 1, [libsndfile has ogg vorbis support]) fi else LIBSNDFILE_HASVORBIS=0 fi AM_CONDITIONAL(LIBSNDFILE_SUPPORT, test "$LIBSNDFILE_SUPPORT" = "1") AM_CONDITIONAL(LIBSNDFILE_HASVORBIS, test "$LIBSNDFILE_HASVORBIS" = "1") AC_SUBST(LIBSNDFILE_CFLAGS) AC_SUBST(LIBSNDFILE_LIBS) dnl dnl Check support for all the drivers dnl dnl - Sound file support check AUFILE_SUPPORT=0 AC_ARG_ENABLE(aufile-support, [ --disable-aufile-support Do not compile support for sound file output], enable_aufile_support=no) if test "x${enable_aufile_support}" != "xno"; then AUFILE_SUPPORT=1 AC_DEFINE(AUFILE_SUPPORT, 1, [Define to activate sound output to files]) fi dnl - Check for PulseAudio support AC_ARG_ENABLE(pulse-support, AS_HELP_STRING([--disable-pulse-support], [Do not compile PulseAudio support (default=auto)]), enable_pulse_support=$enableval, enable_pulse_support="yes") if test "x$enable_pulse_support" != "xno"; then PKG_CHECK_MODULES(PULSE, libpulse-simple >= 0.9.8, PULSE_SUPPORT=1, PULSE_SUPPORT=0) else PULSE_SUPPORT=0 fi if test "$PULSE_SUPPORT" = "1"; then AC_DEFINE(PULSE_SUPPORT, 1, [Define to enable PulseAudio driver]) fi AM_CONDITIONAL(PULSE_SUPPORT, test "$PULSE_SUPPORT" = "1") AC_SUBST(PULSE_CFLAGS) AC_SUBST(PULSE_LIBS) dnl - Check support for ALSA AC_ARG_ENABLE(alsa-support, AS_HELP_STRING([--disable-alsa-support], [Do not compile ALSA support (default=auto)]), enable_alsa_support=$enableval, enable_alsa_support="yes") if test "x$enable_alsa_support" != "xno"; then PKG_CHECK_MODULES(ALSA, alsa >= 0.9.1, ALSA_SUPPORT=1, ALSA_SUPPORT=0) else ALSA_SUPPORT=0 fi if test "$ALSA_SUPPORT" = "1"; then AC_DEFINE(ALSA_SUPPORT, 1, [Define to enable ALSA driver]) fi AM_CONDITIONAL(ALSA_SUPPORT, test "$ALSA_SUPPORT" = "1") AC_SUBST(ALSA_CFLAGS) AC_SUBST(ALSA_LIBS) dnl - Check support for PortAudio AC_ARG_ENABLE(portaudio-support, AS_HELP_STRING([--disable-portaudio-support], [Do not compile PortAudio support (default=auto)]), enable_portaudio_support=$enableval, enable_portaudio_support="yes") if test "x$enable_portaudio_support" != "xno"; then PKG_CHECK_MODULES(PORTAUDIO, portaudio-2.0 >= 19, PORTAUDIO_SUPPORT=1, PORTAUDIO_SUPPORT=0) else PORTAUDIO_SUPPORT=0 fi if test "$PORTAUDIO_SUPPORT" = "1"; then AC_DEFINE(PORTAUDIO_SUPPORT, 1, [Define to enable PortAudio driver]) fi AM_CONDITIONAL(PORTAUDIO_SUPPORT, test "$PORTAUDIO_SUPPORT" = "1") AC_SUBST(PORTAUDIO_CFLAGS) AC_SUBST(PORTAUDIO_LIBS) dnl - Check support for OSS audio AC_OSS_AUDIO AM_CONDITIONAL(OSS_SUPPORT, test "$OSS_SUPPORT" = "1") dnl - Check support for MidiShare AC_MIDISHARE dnl - Check support for JACK AC_ARG_ENABLE(jack-support, AS_HELP_STRING([--disable-jack-support], [disable JACK support (default=auto)]), enable_jack=$enableval, enable_jack="yes") if test "x$enable_jack" != "xno"; then PKG_CHECK_MODULES(JACK, jack, JACK_SUPPORT=1, JACK_SUPPORT=0) else JACK_SUPPORT=0 fi if test "$JACK_SUPPORT" = "1"; then AC_DEFINE(JACK_SUPPORT, 1, [Define to enable JACK driver]) fi AM_CONDITIONAL(JACK_SUPPORT, test "$JACK_SUPPORT" = "1") AC_SUBST(JACK_CFLAGS) AC_SUBST(JACK_LIBS) dnl dnl - Check support for CoreAudio dnl AC_CHECK_HEADER(CoreAudio/AudioHardware.h, COREAUDIO_FOUND="yes", COREAUDIO_FOUND="no") AC_ARG_ENABLE(coreaudio, AS_HELP_STRING([--disable-coreaudio], [disable CoreAudio support (default=auto)]), enable_coreaudio=$enableval, enable_coreaudio="yes") COREAUDIO_SUPPORT=0 if test "$enable_coreaudio" = "yes" -a "$COREAUDIO_FOUND" = "yes"; then AC_DEFINE(COREAUDIO_SUPPORT, 1, [whether or not we are supporting CoreAudio]) COREAUDIO_SUPPORT=1 COREAUDIO_LIBS="-Wl,-framework,CoreAudio" fi AM_CONDITIONAL(COREAUDIO_SUPPORT, test "$COREAUDIO_SUPPORT" = "1") AC_SUBST(COREAUDIO_LIBS) dnl dnl - Check support for CoreMIDI dnl AC_CHECK_HEADER(CoreMIDI/MIDIServices.h, COREMIDI_FOUND="yes", COREMIDI_FOUND="no") AC_ARG_ENABLE(coremidi, AS_HELP_STRING([--disable-coremidi], [disable CoreMIDI support (default=auto)]), enable_coremidi=$enableval, enable_coremidi="yes") COREMIDI_SUPPORT=0 if test "$enable_coremidi" = "yes" -a "$COREMIDI_FOUND" = "yes"; then AC_DEFINE(COREMIDI_SUPPORT, 1, [whether or not we are supporting CoreMIDI]) COREMIDI_SUPPORT=1 COREMIDI_LIBS="-Wl,-framework,CoreMIDI" fi AM_CONDITIONAL(COREMIDI_SUPPORT, test "$COREMIDI_SUPPORT" = "1") AC_SUBST(COREMIDI_LIBS) dnl dnl - Check support for DART dnl AC_CHECK_HEADER(os2me.h, DART_FOUND="yes", DART_FOUND="no", -) AC_ARG_ENABLE(dart, AS_HELP_STRING([--disable-dart], [disable DART support (default=auto)]), enable_dart=$enableval, enable_dart="yes") DART_SUPPORT=0 if test "$enable_dart" = "yes" -a "$DART_FOUND" = "yes"; then AC_DEFINE(DART_SUPPORT, 1, [whether or not we are supporting DART]) DART_SUPPORT=1 fi AM_CONDITIONAL(DART_SUPPORT, test "$DART_SUPPORT" = "1") AC_SUBST(DART_CFLAGS) AC_SUBST(DART_LIBS) dnl dnl Check for readline support (Josh Green 2003-06-10) dnl AC_ARG_WITH(readline, [ --without-readline disable readline lib line editing (default=auto)], with_readline=$withval, with_readline="yes") if test "$with_readline" != "no"; then AM_PATH_READLINE(FOUND_READLINE=1, FOUND_READLINE=0) fi dnl dnl The following script checks for ncurses support. dnl I copied and adapted it from DataDisplayDebugger's (DDD) dnl configure.in, written by Andreas Zeller . dnl dnl Look for the tgetent() function - either in libtermcap, libcurses dnl dnl On FreeBSD systems, `-lmytinfo' is preferred to `-lncurses'. dnl Reported by Vincenzo Capuano dnl dnl On Linux ELF systems, `-lncurses' is preferred to `-ltermcap'. dnl Reported by jdassen@wi.leidenuniv.nl (J.H.M. Dassen) dnl WITH_READLINE=0 have_termcap=false if test "${FOUND_READLINE}" = "1"; then _termlib="mytinfo ncurses curses termcap terminfo termlib" for termlib in ${_termlib}; do AC_CHECK_LIB(${termlib}, tgetent, [READLINE_LIBS="$READLINE_LIBS -l${termlib}"; have_termcap=true; break]) done fi if test "x${have_termcap}" = "xtrue"; then AC_DEFINE(WITH_READLINE, 1, [Define to use the readline library for line editing]) WITH_READLINE=1 fi dnl dnl lash stuff dnl AC_ARG_ENABLE(lash, AS_HELP_STRING([--disable-lash], [disable LASH support (default=auto)]), fluid_enable_lash=$enableval, fluid_enable_lash="yes") if test "$fluid_enable_lash" = "yes"; then PKG_CHECK_MODULES(LASH, lash-1.0 >= 0.3, LASH_FOUND="yes", LASH_FOUND="no") AC_SUBST(LASH_CFLAGS) AC_SUBST(LASH_LIBS) if test "$LASH_FOUND" = "yes"; then AC_DEFINE(HAVE_LASH, 1, [whether or not we are supporting lash]) fi else LASH_FOUND="no" fi AM_CONDITIONAL(LASH_SUPPORT, test "$LASH_FOUND" = "yes") dnl dnl ladcca stuff (depricated by lash and will be removed in the future) dnl AC_ARG_ENABLE(ladcca, AS_HELP_STRING([--disable-ladcca], [disable LADCCA support (default=auto)]), fluid_enable_ladcca=$enableval, fluid_enable_ladcca="yes") if test "$fluid_enable_ladcca" = "yes" -a "$LASH_FOUND" = "no"; then PKG_CHECK_MODULES(LADCCA, ladcca-1.0 >= 0.3, LADCCA_FOUND="yes", LADCCA_FOUND="no") AC_SUBST(LADCCA_CFLAGS) AC_SUBST(LADCCA_LIBS) if test "$LADCCA_FOUND" = "yes"; then AC_DEFINE(HAVE_LADCCA, 1, [whether or not we are supporting ladcca]) fi else LADCCA_FOUND="no" fi AM_CONDITIONAL(LADCCA_SUPPORT, test "$LADCCA_FOUND" = "yes") #Checking for IPV6 support AC_MSG_CHECKING(for IPv6 support) AC_ARG_ENABLE(ipv6, AS_HELP_STRING([--enable-ipv6], [Enable IPV6 support (default=auto)]), ENABLE_IPV6=$enableval, ENABLE_IPV6=yes) if test "$ENABLE_IPV6" = "yes" ; then AC_CACHE_VAL(ac_cv_ipv6, AC_TRY_RUN([ #include #include #include #include main() { int fd; struct sockaddr_in6 foo; fd = socket(AF_INET6, SOCK_STREAM, 0); exit(fd >= 0 ? 0 : 1); }], ac_cv_ipv6=yes, ac_cv_ipv6=no, ac_cv_ipv6=yes)) AC_MSG_RESULT($ac_cv_ipv6) if test $ac_cv_ipv6 = yes ; then AC_DEFINE(IPV6,1,[check if system supports IPV6]) else ENABLE_IPV6="no" fi fi AC_OUTPUT([ Makefile cmake_admin/Makefile src/Makefile src/config_win32.h doc/Makefile include/Makefile include/fluidsynth/Makefile include/fluidsynth/version.h fluidsynth.pc fluidsynth.spec]) echo echo "***************************************************************" echo "The autotools build system is deprecated and might be removed " echo "in a future version. Please use the CMake build system instead." echo "***************************************************************" echo "Summary:" if test "${LIBSNDFILE_SUPPORT}" = "1"; then if test "${LIBSNDFILE_HASVORBIS}" = "1"; then echo "libsndfile: yes (with ogg vorbis support)" else echo "libsndfile: yes" fi else echo "libsndfile: no (raw audio file rendering only)" fi if test "${DBUS_SUPPORT}" = "1"; then echo "D-Bus: yes" else echo "D-Bus: no" fi if test "${PULSE_SUPPORT}" = "1"; then echo "PulseAudio: yes" else echo "PulseAudio: no" fi if test "${JACK_SUPPORT}" = "1"; then echo "JACK: yes" else echo "JACK: no" fi if test "${ALSA_SUPPORT}" = "1"; then echo "ALSA: yes" else echo "ALSA: no" fi if test "${PORTAUDIO_SUPPORT}" = "1"; then echo "PortAudio: yes" else echo "PortAudio: no" fi if test "${OSS_SUPPORT}" = "1"; then echo "OSS: yes" else echo "OSS: no" fi if test "${MIDISHARE_SUPPORT}" = "1"; then echo "MidiShare: yes" else echo "MidiShare: no" fi if test "${COREAUDIO_SUPPORT}" = "1"; then echo "CoreAudio: yes" else echo "CoreAudio: no" fi if test "${COREMIDI_SUPPORT}" = "1"; then echo "CoreMIDI: yes" else echo "CoreMIDI: no" fi if test "${ENABLE_LADSPA}" = "yes"; then echo "LADSPA support: yes" else echo "LADSPA support: no" fi if test "${ENABLE_IPV6}" = "yes"; then echo "IPV6 support: yes" else echo "IPV6 support: no" fi if test "${LASH_FOUND}" = "yes"; then echo "LASH support: yes (NOTE: GPL library)" else echo "LASH support: no" fi if test "${LADCCA_FOUND}" = "yes"; then echo "LADCCA support: yes (NOTE: GPL library)" else echo "LADCCA support: no" fi if test "${DART_SUPPORT}" = "1"; then echo "OS/2 DART support: yes" else echo "OS/2 DART support: no" fi if test "${AUFILE_SUPPORT}" = "1"; then echo "Audio to file driver: yes" else echo "Audio to file driver: no" fi if test "$WITH_READLINE" = "1"; then echo "Readline: yes (NOTE: GPL library)" else echo "Readline: no" fi if test "${profiling_flag}" = "yes"; then echo "Profiling: yes" else echo "Profiling: no" fi if test "${ENABLE_DEBUG}" = "yes"; then echo "Debug: yes" else echo "Debug: no" fi if test "${ENABLE_TRAPONFPE}" = "yes"; then echo "Trap on FPE (debug): yes" else echo "Trap on FPE (debug): no" fi if test "${ENABLE_FPECHECK}" = "yes"; then echo "Check FPE (debug): yes" else echo "Check FPE (debug): no" fi echo "***************************************************************" echo fluidsynth-1.1.9/doc/000077500000000000000000000000001322272076000144615ustar00rootroot00000000000000fluidsynth-1.1.9/doc/CMakeLists.txt000066400000000000000000000023211322272076000172170ustar00rootroot00000000000000# FluidSynth - A Software Synthesize # # Copyright (C) 2003-2010 Peter Hanappe and others. # # 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 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307, USA # CMake based build system. Pedro Lopez-Cabanillas find_package ( Doxygen ) if ( DOXYGEN_FOUND ) configure_file ( Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ) add_custom_target ( doxygen ${DOXYGEN} Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endif ( DOXYGEN_FOUND ) if ( UNIX ) install ( FILES fluidsynth.1 DESTINATION ${MAN_INSTALL_DIR} ) endif ( UNIX ) fluidsynth-1.1.9/doc/Doxyfile000066400000000000000000000172571322272076000162030ustar00rootroot00000000000000# Doxyfile 1.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth PROJECT_NUMBER = 1.1.9 OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" "The $name widget" "The $name file" is provides specifies contains represents a an the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = ../ STRIP_FROM_INC_PATH = ../include/ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = NO TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = NO GENERATE_TESTLIST = NO GENERATE_BUGLIST = NO GENERATE_DEPRECATEDLIST = YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ../doc/fluidsynth-v11-devdoc.txt ../include ../include/fluidsynth ../src INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c *.h RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = fluid_*.h EXCLUDE_SYMBOLS = EXAMPLE_PATH = ../doc EXAMPLE_PATTERNS = *.c EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = YES CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_FONTNAME = FreeSans DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES fluidsynth-1.1.9/doc/Doxyfile.cmake000066400000000000000000000174431322272076000172570ustar00rootroot00000000000000# Doxyfile 1.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth PROJECT_NUMBER = @VERSION@ OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" "The $name widget" "The $name file" is provides specifies contains represents a an the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/ STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@/include/ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = NO TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = NO GENERATE_TESTLIST = NO GENERATE_BUGLIST = NO GENERATE_DEPRECATEDLIST = YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @CMAKE_SOURCE_DIR@/doc/fluidsynth-v11-devdoc.txt @CMAKE_SOURCE_DIR@/include @CMAKE_SOURCE_DIR@/include/fluidsynth @CMAKE_SOURCE_DIR@/src INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c *.h RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = fluid_*.h EXCLUDE_SYMBOLS = EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/doc EXAMPLE_PATTERNS = *.c EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = YES CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_FONTNAME = FreeSans DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES fluidsynth-1.1.9/doc/FluidSynth Thread safety paper for LAC 2011.odt000066400000000000000000002173061322272076000245220ustar00rootroot00000000000000PK)K>^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK)K>dmEE-Pictures/100000000000000800000008DD0ADA29.pngPNG  IHDRt& IDATcπ? ȀIENDB`PK)K>J3__-Pictures/10000201000000F20000005D37AA1023.pngPNG  IHDR]";sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<tEXtTitleFluidSynth Logo{h:tEXtAuthorJoshua Element Green f+tEXtCreation Time2010-11-29 !P IDATxyeYUU\*Ɩ1P 1@a9/a0.ҝ &qӄfh(`lnH*d`@i\RI5zÝ|sﻯ޽dɒ}KU7oKx^D!9Y52{^8UY +ȫk$ٳ^Iv8BIJ'%uEʪXYI]+Q6Ԭnlg;g_UW&ȯ0"gҋQĽX'<L@^@ ͨ/(1j!Vkc jmQZS 7*ϫ$/0K[jl}^UWȯ"3g08%V e{~Еa~R)-xB=B)\kZ"y>4EޯAQf:2%ѩϟ^B*˜&x#RnOzߎDax@^L9g>L`ͭ QUVe|XfyқE6QezeYo۪[0RUڟ^rEjvUH|"Va>2{a&`Ex^K/dBH…`r0B -` 1Zku],L<.fWY~LFlpL,MEHsczp*8}b!>kV8ꮭȠu4tO}~}܏[d, lq酌sr!PJ(SdkA5Zc1UkT*We1tPٍ|4\$dj8~.ln aR(.>>[9¤0#f՚_>7)rrYql^QD^a&` Q DЪ& ư&1+eumij={}VB = ruaӽ?tyQ&=gB J#RFwu<3juYE1d3hx0%vuuOUϟן/<;G^KV[|NP,VRQ=JjW6/xZ!T OkNV_dU Sj?RҏSc/YXcQJ*,p=l]'㛛ObjZԃ{X]N'x~%6hł;2R=yK\"2 £I™ 2B]$c֘:WUe"۪fniYp|}PUOr~wo`D+}-hw{q^hUAIS&ǹsA)Xi7kX1cQ2֥Q*WJtU<Tefe7l'զUIf.z g>[wTKAs4. W K ?jJ?2F]x;!xhR<+v6_.'[Ox#iZew\ &]ͳzmJ?|lxy\Q;-q ZZ~Ծku1/ 鵘1I)gQF@3N0-,!Lc)16JU]TUUmy^e"MJ%׫hPՙF/BF0dӊeNKKkV➐^@1YkԭReQY6*l}q>l|Ff?_TctVF':[~pHx^atXF5\&eą3!dJ\V`%ºcFZU,ʼ_e*]Γ3Y2x菟Ia-]=/y-w1D+[L s ea@*]GlâkŌF)S׵.qg:֋t|Li|8r_2> :V7Xi Kǝ^wҏ;d2@Ȅ_/5hmt]uE2*Fgօ|8x6/'.VU;vz_.:˧VF=&r.(Κ(/E%;a8>9gmfյRERei̓e2Y2"ˀF^' ȵ.E1Wv|*,?u_:dZ~x/O6*̒a '_Ͳԝgxܺ&Yo9eZѪGvwNq/jh^ ϧ\ #lRܥw Sy|i$UcR*ȆE2V Ne:Lԥ)8t0nGCaoYym:Am.=2F,5zZB)"O2*z"]*W\A)Q] Mw1hŭŭ%Y>wO)} ,ALyNCːFJiR&l4xm=ϨdxLƋ_#<-{vN ?aV >C %ŗS2iU]Uyf9^L'7?ZZN=)2!>{V{|xxi QoAw‹+"=EDU#k,\G]IU\/ƃe2TdK0׃46I:lkygqfOٳcql@[ݎ 5kvu{Qe2!8c%]Qc5V(2e:E2tl_*F[7q^pzAIսǏZe?ls.JdJ,iŤcVUx;Hr666ΟDoȓX•G۫W9ꭽ>.A=g\0B_d'GQt!wlF9]."_*ߴb ﶃLpZvʰ, Bz ]Ф?LkT]"K,Z1_۟3y\-lÔa10YEYLZϯZr"Up֍O7IO+cW|'=K7Z(v,?K'dQ2 KeFn g)<ƃhYCU=*YՆγA]SY%-kj'4/qOp0kK* ^: hUMRXB`HSx$ǤPABR܋{2j.^?K֫,2n2ʠtiJmaK[-IBpsq? t0 /8 k\z>J(,N_~W|B&ľ; W$0J9132j VL]JUUXcJue*155 r1*™O@d\Lx2`\zL3B i<Dpa˹y@`Sj-e;(0N zN'Ȥdfs4_A NlyOHi\{c) 4w P~$/J 2_ !d#;G}F+Ǻ<}^>dءL J`Qn7ZK|0Q{vQd^w|z4a3bM{kh"{pﻈ` aEC8wNᖩRf̽Gðya;ǙPӎ,Rscچ;_`_<]/_zQք2^{>eûy^|5a[#2~AP-B"۵6v1|wi2 3fM^sD>ah2θs}P^ermO^y"@*qhur/QZ}YOc`5ǻ52BSŴf2(Θw734րu>c!'÷n= k͂5q\"5mWfk)%TzLKo2_#uiݜ*rO⚅Z^05ƃZ2-/`[3Gn"uJi ZUu *XBw.{`QA`&K]7s\1fLLܹ27cs.ܜ>P 2}%(h w=kq2mjg>?΋MףNפVVJAYɚ玨0d"^e^0UEW' "3aDеvEƽaƳk:c ]0F<Ç Zg04q*hUCFU05LEE3Sr;yzx]FmBSţS%VU*̆Pe U{PekM (ϣ{╅DŽ^t@ze>pF)wb$^phq?Z _nlv]$N1T;rKyJA)D!x&ʴ]0F &|xQaBppaA_Xܼhȑ7nF0Vv*XK"@)QV xi:&"Q5r]A8'Bu.S*@)w^x`B1 A<˄xTk&21ۉv5Qf & "1 KȰXpHa1 8̏W<"Y?s|1 G}NgAPRf={caF](&=@7}W!*&<Ƞ ?^u>F6<@SW0V %lex&!AT1_ 1Zi@o#;!ßo=?sLT.S::&2NJ,1ġÑ(&5nnK  TU znLc4A>@6\GxTk@oH{F[X A(`[(Pc}[@kף>lއAEAE^<ƹD` %Al30/\4L,sD%x̨˸mv,fl4TU.RIđ{ncestP誄 {,FkmAf!nZ ,jZj,{V%TU( Aаf`Qucp)L]BWTY6G"پ-|wxCi \Z?>oG !6(:vܷ-&pw .Pٱt@U܅ƾ/a-FpFR.aƭkt^0]U jw 3 fG*`-!2a ٳ%f/uAo-eVcEMx]}y'dq jPp;ޱhQZiX̛j#vbD3C&5 ąv(=1v IDATНglР4vXU3LZ'e@" du$[W^cw=)~P%(ms;3 ~+_ysg m&XpcVU U+h)έdkwT5TUZvKIt\ 0N((;zKW䙍ӄB8V"$ !%iepj,04pgȎ(u^ c;NrغQ3e&r̤AdCdє j5Myh-%د,gk9}vהxbEBkjQC !,B[t U΃ͽ)C|(@)1|5bErak)!|ꇼQQb%^Di-Ƹg_jo9 lk-lVoq hHWbCAv<{z1M ":"6pX"ڔy1!BZKic#펩gB(DBI>5a\^L@Y.D]h( LQSߧE_s?>x.; ԃ5|b ^k~?] @!I];/?}N;Mވ ý'Bރ7c,~[PetAEmc#̹/[8(+ k=E]@9Gtl+eCk'YH¼X(qg6N J}"eGz xxjc鮩 ʅ+ށҀ/}GIRuut:Uk覄U.jиLk"Cn ݾ/>_#WS|WU7\%J w|xdU6\N)3jOlq~Mwon%,7Q}%V׻Jk)!DpR,Pf-=k//"k *C Q$Ȇ7:፦qxe6DUdu >bn7e!WZ\MfZ.Ԫ;CF{ՔÚ"5٫B6m`߆~g޺ߝ%6PB%.]c!އ [HWn_FuYMA AP’wG{9ElL(1,q}̌eo[ >z 7~x=嘥7<+ߢP$[ Ҥkz_R:0@r;J'Iu2 ;%0 0A`%)W~2Z(0k}wR%Gmr/5/MɄ{~|uaMiiNh\9 wc&/wߝ8磸Ƙ'glď}ognPnes C\溶=S-|Ce]jz ߸sĽrhE@0 ^@_monf6De~4(LxM` 5RAjIfŒ,nM`Zkv1SUC@Pu HxLQ;|k'8ſMo\K.Pe|N'.px}1 `^Y~n}Opxwktc U\Ћ`d‡?2;߱[ԥ>(@̇͟|d Vp p}msNc>םn9![e{!8 X?!>1d]U;2-E`%04 "3613b,!1 _"DVDgf<<)vwνw=cx.-Ur _;3۰l~qZGP%m3& 0‹Cۂ~v??Ũȓ Ek,( ..] [ʷu&Md.lH7EkfEQ6>kG"@E ,C^/ ڐ{EԽϭGt6bEBR-1>,6x ֵh}t#[B%0嬵mіtjݥ7xHhu3uӬxz ݹVv Gge_ /6> 13_KA!X- zg5ƻnnwZT6zMB;h1}c4GxeD;)rBek5XkuwD{P}`Y$+)v- u@mrgo̳sZWvI Ys*Onp:r<1q5CTF2,21w!e=R\vao PUmc9+}|7 VjLKm-c耉.~[seg/~边ϸō'K?HJ z͞o}Q|=lO}CMa&p{Rz%Xhx46rX'/=Tu VFo-V0Jc3i;@;4#rps ZbFkc)h9MSD@czIw0!P7d3WZ]Zo["j55*Aeև/ ,懛Otf GD܉.ΫV 6/Pf cQۨ1S`iv%u؆*ǰ0Se$tć>O?Q0 k'/di!坻ۿ0?_?e0a!@$`wҧJ1>jr9*8w=`5JCimXCҹ{$g%( !νq]tuk|. Dn1*냀C]7mq5 poڶ (%)z!>-**7fU Pe[ 팧^ͻӶ׬/ZO=065 PXR% ;#< 7>Ƿs5BP?{ -M)Z[G 耱( 0v~bN 7' }$El05^qb"Y*M!ԽqEvc$NFLvyMd7JnZ+ k+S1ֳ4VcW?$`M8o[`62)?k=R õe+z_aVWxCm,vJTwR&36%i񎽊_֓x•Tkj|WX&GvT¨o?O0GF}Bw^`t]`H$%B}x@$5.xFi/tG QcxME:6^w  wEp 0S(߻g>ïx}{E;& wLozC ozC ~xrO=bsKa8Օe|_{Ž'*=b"` .xo]ݣ/G9܉皍YD.,s=* )5Ʋ$)V0м޸V!K[Liq@a (/BGeYH92ZcVss粩wT|YTR%LOH)zl_=  YmS D57`DMn~ce@C@>M}Qh81?8sypʬ /O)( 1[9g~~"ش‹ D).00QlP 0Ljd7?U-BigQPMw^oDl$tPa^u3a@(J;{_LP ڮTo@˞Ln&RrCO5~K4OKbӄWoc{gXo܊an~Daj!q u{0*ZdܼS$ Ew>KpQ/y_`.W M $kWc[~o~cV3YفiTQWJIDAT`ŏb`(=:/ sxvnVm5U9(1s&9& #Dϻڞ3̵ CE[>W2\zB&a}.n 6n:TJ SouaUmEqgycīq-k?~ԋsgEoXf yZ#-m=}qCdYҠȶQvQ(^W 6swUpԨўEdSv>e!`KqmQ}3,W)1\C9CNEmlL U:dQm6Ihw_"ud5d RCleރ/86ڊx(]"d|zG'k1Cx7wA}F걶QOC'qwT[!OԆ?UJ|q{.-F7]ύ?o٣{?|iY$'Tuĝ ȿ/G(g(bƹMjl.$ \ޯ5);Xa`LT]5aǪamn( ANy'<~QVn@;_Kk =+/\VUGTԏ=sEn /I!H5h1 m P8.&VVXQbÞ״% 6ـ !ce[6m0ժ*)L]:H;q`}PjI$0$`[" Ixɀ9~YKLWwS [$YX`v_ԃ mĭ oFg?>yPrq8_-V:6`l#nj!K(=~w~^$`>gaRV\~bm!!qPի 0-㹮J Uq F XTʢWEqxh82]g7й TPlbbzEW ~b߸:@P]7$P1anj5ari6OX-"n@O}9dlMyXˁe7̩TP$BkxO(8QXԗYϹlOe_9hЭفd7&Mm9lĦ( qpT!8S.n Zq#J rzXj{>1#Ֆ'//&}A~f! @'BdsxN.#}Z(|U"'i67Q\a U(`!*Kjd}>9,Q"˙+gv3y.ZjyɱS]w~mlT1b>ϓj}qwN~2O;j߱^%G:.Ax֮h\O;<ޞ/9_fS=_N˲f/YK|Mw\^4o'LDjNfYNlA};}zߡ 0uZY*J|++ fWJm*||+m}G|Qr6pxGɎsnnJ?`I+skbvC&j)33$>M nu|gK۟A,DTFHkfWn@=sӉK9rVOPxG,Ki^͑3nhx}фZK ^_<  t`c):D9qi8\ гK<9k}rr2t~_F x^T?1E'i-̬½` C鱜rn~H dBRẏsRz_\>|ӻUGEzN5ݾ :bp{L(]Qrb}8 9 jjI)Ql 2(]S'UzIwHc)  9LD:Qerxɇ/w;^o:6>s{yn&ݵeU.u$e""sSJ*UU$@;,b+CUALD&T wg1l 09|0 8L;sV%"EU|(TDꜪsDdd cLsH6p|9xU]D_S0 tkE}Ӵ7"ClLMxG$Ꜩ+H\TC}r@fP_JϲM`ja31qT#NRF 8 ;TY:k ]- h>SɧL?SrJ9đ*& 5ldP)QE1D\坟*ԉl&n}^m s?DEV~ߟ }r.4nAX`N9-DTW |2̆*jv:{O~ @2ˠIW6ƦNw2%-2Uz(m:qG`C :8#N zR$dIOtT D Te0(%Ƥqm&NWj+q8mDId"%v-*PxU)rq/obﺟ}Cf[{s:" oވ(n&4JUMdY9 V+9P&'q^;]>ٸrfT A inĖą7$&.=p&DqJٶ6'ɶ6ddSZو GuDxxM@'}nj.#Ҝ]3G ڄM T6:v9$S?>G\o%5`>:@!)sq+\{uprq5P@OκWbRrR,+.rl"~Sn7xCd|Sp!g `g7&7eDpU6eȍ/I k]f#(!jmni^$jŸr#R[vY7>nnj]{MyMk(MA6x68VxŦuzq`(&`Wgiӱ \UEʤ2-^.E%wn?hmNpA-boρi\x6nV3ܼLUC˰MQCTG `<*VCȟh; V |*HjQB˙j>UGӞt[d|xK1]qLOݥODͲӊ9C+W9ZV6cQC݋0$"K6$R;Y&~^T][wQHu6^Y'ض./7.i#):Lq8`1\bZ"F/l,&;-'}]nݹp~`3GMkku9sy:Q SFǀ@8bIBr& UQx i#uAB(м ]um=wWe:bN%nrܹdyY6>ƼdWč&~Z HTʨ'rpSKw_~ҏvGѝmϢzI8Tm43:q}('P1!J0dͽ[=L#9U@dOu ?~tatxV {F_LLPhƞmmJUͨkM"SlL6J;^A-USj>Sh1-G$@YQYI9nuJ(q:ٔv44o#1!\ wu^޾8/RHe-~콨~h6eo? Yt/0\V[N-ZMvRU0'*#q uMAQB01>1+񅗈z2S)Ɗ!4Sw=WL=O^;;VCf&IYy$qs8m .[t*~҃n{n^_* `31n*+|? aVveeIh8&ɨuZĜ*pPFH$U\U&b@t 8˱E> >>O|ъ#cCiL&^bȨ*[9yWOL{ظ=Η[nV;32^mpk}b&u=׎yCOwfYUĭ.VW#W*ߡ< VnsjL9 Jj8Ґ{"'SAD"USl o^Sh&towlcl\2=@' [خ>dXkI6^ n? j]"Ӽ@cWL}ʁ;ucE~H  jRGNOResMT B!2c,2Y~_Tzȏ(/R(e״"5J ĤZQD*DZݩ&')cEн QO>VG?<@%:IENDB`PK)K> content.xml}ٖȕػ'N֨[.u[=SnR}|(,ɢ>~,}H ¦f-"ō_2R4 M(A%~?|uoG_~~Xq>8x;ޱ_i.q0{ {{Uo~Gc+YNo|l]w6|dzX|OЗY<,r,>Ga"W뵺6$}Ӧݭ&Uϭ4|.,T|vb9 qsa0E<=pA7kkK7_I𱦅t9t,|*/ WɞO*-<: >WA"~nWgO?y`8ݸLHعR. VIW-"_F>(LǸ=on|?ALb+PEaU=A|^iw#$2@A%wM9.?QϷP^ywFF@P X"S7 `6S #*}}d/lw0?=*Լk4hz(LV }7)A\-h#޽aΟ6Yu|FaQ!R?H:>_8ӿ "S~,Xm2U>vUCYvkǍI"r`pcC=<uF7_fwFA S9-FC'x /v"mXIԜ(`̃9)!Sdyo`)r,|c^EK7.ΒKR*qSk+7uRw(o я@EH AA ,>E`G֜Kwہ s[SMqe/\7X>ziR>|^6_ Sƾ nT]ә(C>AL`y WkYo;^޻d>MY6c|3k5nn/Y$sVzM 4n_-4<9ϳKnHtVWjc$.-O d`IM`=#gJԱN͂'^t:cۗZi,3DN-33k<Ƞ;M0t3ks r9'~u[|0ӥA:Z!e-|*%2Yz9{| i_/GaTQ?Jb 9*^$VQ|+eEO#beQ69c֚nE !k.]ⴺV~!]b+rMdtŦ4󭾧L r ((q-l=Tw ͠VCľ0yx ,A2TQ;В˴xjE5-gl?vKۍk"A4qn/ YbiѳCU_d3퐹ʙ*,+ Y[g,fwmQlm]d,sCv> `/ Y޴2 +盜2 |0Ü[buOlxQ.+`Ⱦ*gc˻Ⱦ*264ܷύxIZ.DxȎyt"'O?P22nܡ)g朩IƝ9cJo1G;? gd3B9.ĭkq"`v64`xu!0;cڇ` fhi1q]Rд}VĹtttop&:c]̞03]}py`fǁu)^_z0{&?<"`vj?J,VbD9#|gV9vgLGbD98ڽ%Gn-;"tH([@2RTeF.kX2#)Me8YvD˟ӱRJw\ːh~y,;dv`%ۚݷ;JHjRbģ.#L}ͩ숬Tbcei#Jow.K#+sdOo˓Qv;6!12G|vc2go7QzStܘv!7P+sdؼw9O9L26s̱4edXcMnȜɱ2DF#O9>q5[nṟuwcem&+slO[2`ȓ=~ṟKӵ۵A͝iK#Pӵ v5!Nᯊ-oƆK\r8Uy{ i 'hddC C"I?Hoz:^ca'(YآT$ }e>Hl)+O坎[ Tx.hC@C |W?Gf2а0qZzF,~ǞaE-4E-W'^")^ϭOX{>I<!u\! X= wt.tGa K$l<.1]\/7c`$waoh;VfDLX)%a΢j-?ygrxhD󑒫d?6' G__ST*Qև]jja}u/t(u@<<͊fp~+z YΒ((+3S/o ׿ o%YHe@Ay·;vx:>6]'QAπ( ap 0IV J\EU%MKԨ\4݇ lo[[W [i?d"[+φ-* 9 %y|ٰe]%!ǖj$o<$L%y|ٰ\%!ǖ$o<&WIȱec[W [ӫ$eO%y|ٰ]Ư娆!\+ǢK=$_ǥkmax;غ?񏷅k`xKغ?7W`_k>vӾzݕ{/@P8@<*,J ^"m4"|A%Le u{E4yIJ-,ڃ<=KeECA}y=}#Yb5Sg %Pw7ZiuZHDo>wn#?Y7Vd[CEb|bZ[M/e@ֶ}mݨzl^a(3mt,7Q,J\\>MIzNIBr&CMKsF%AHWT͜t׍_iuWZ>gԍúj;;6K\$+ѿi7~+'+ѿiՉ_~?ѷTƩ <ߧ@C{_ןevЯֽ~m{'[o[-~񮒨^oa_el[' j4O7!Y+@nm$ɕ_33z[y`q_}T͢\ԾF|'!psoE0ݡGxg|L&>Ű\孌5rqM\VT˹*?:5Q *7C˛̽[jչl]K_'K8֮Y7v&p1dY*!"FhҮj}T]՛ع>/ չƾz,U{|MQu՛zYg,JDY75MjJTM}Woqk]Uh_~.RzMkL]90Ⱦ1KM1ǺY2Gxh|,q7OWdjmF~ .iUz{rOӃg(OQ͊NL;uϫ$ Ȃ?΋H0[Er?(Gz3JXoIZ"ԕlbbblҴmČU{b UeLHi0>p>)w(nm⮆R̡3T F{z}+`} r%ٛe Eaz/am ? U+XԦ<YAfEb3xף)թfAU@W`|VWt!LA%q 3IhpphHi?4:14A$n9sQuZ$6\H65u35GU-՟*hf qY\`ӜSmd'/im 4Uۀ<8D1bZ㗁l`m.Bcڦ&B4:al5,a؊3]ƒE|tWhJt*u>G:R ,'7}:sWg6 1Ӓ1,TLlmXb^UӶ}W]ƺh}UǘdtxǍAv*M @ܞc Фv:a֡ѭ-]h|et>~kXs95y6>*KOC:/MdH5v߼Nr{wUg˳QKڞqnѵ &pj^*#g-tqR4~*u,JM"c }?A,z)sO)El"GT1SC&ګ D\U3/MB5cakÄ,O?ŸB'cvfN]O%Dgj^)"r[MzcMt;.p#Z߶okzoN۰V8 ; [LA4#pRe$y̦Rq}Q, @ ("ځ!]Su.܆]nX[lҵ(w;B3DZ0NJ 9.}(@ڎ)]FRm)C5̺HI_4]LP4{b(e'i7Rg^h1t D&XZk8D( MɀTc-64`cHdι#İ~P0FAg2X㞢_tSo0" 0V4US',dP42/ r]e/;&)X$?9}Nj-TG,?( u<Ii9Js"[!!T6DgH_HHFvͤX{gVio,y3i]B >83RvoyTp:יjkzПv3X|2l@{h'NB/PF2`{ 5WMTs2_Ҿ!gɑ ݱ©9yVQWXmYWMpWMp@tma-767dqC߶>O&U FP>qj4l>94ٳό !V޴A<[ XILp.~B0aUb,>M(P|[K,)i8q²$Ót#%.-N@-*A{$!h 3^oOh>`'9BE>zg07nJOɔsOV ^öS1%"apZo?U~*,xφӧ[]}G!C\Op"A'5!U`/pξ܇N1}&GJȈsPC>loK$6@(h#\,j(XjhԍYf&@?Ŋf)lFW35#7F[^#Gsb:fRvS dJN >NX.aE:|^F9v4˲'M{ D[G);jsg44#$E($z-4mPʹ(h{eJy"++Rrl !e`kpK[ tmnR~ XĊ><';A3+w 3B)%LmC}_8Hj/L64잩&*b ! NWň(d)[1TLH#e-o?y1@QyV.|MA(G7Ty*UA9uTn9H \Obrv>l:$(L}L#Jߦ켿c(! /`|[6g`23I\'bD:d@hI%t <:.\YZPV ]S\:@m?sGvp0 cFql䭮)v?WBSl4nb@ Qt2'kSڂxr/ØWpJ|,~4uiӹ?lc1G2?~Qb,/#όuga☂:x?֏@ s}l _i9Nq4hwznhK _w=$T[Q _Ja1BdlJ) {U0Plp0=y(C0YrafLv`,[M<$&Xɥføb_eH*8-90T% t{%R)^IXE|@#mS=enA:J(7U/6d5XLQ)rS|a 0 H\&%7KSz۽)2ƌ2X6wFFk_VcgR R@TQĬʲep3wX XKwck22]^ #Ae-ƁL0D&ydK=(` Ǿ O0afl4K#)lV\$C*FD4ǜS<- x)xO3UMco2!:']]Bx\HdXBx` ǥNG08)U'ΐ1*"4QjLʵNVGǡ&qJ8 z ,bAJI%bpna6.¡ !!>J 3& / :zKu(nʤ>.Ii#;bDXs!sEM\=4&CdO6. G2 KiS~)Wj _dSyEP$e0=Z͇^q( n^T 8kKQȬ2}R0!kmL$S!7EY4 er1X[ C',_[#">  X!*T.+ؙ$Ҽ%s.OH' @qff⣍{Edxֹ)2 7vY/Z:`V!ˆ ˉ Tf|_J? /nKmIy8әa.X.S6IZ)AsP*x)x֖-#.P<M]B~_tN5!L pm거\F%Lf2rL"7UBpLtHdL*dr^Iיs ^0N4D˿1 eݜFͼJDoUmf,n/( V/i[TlVglVcu:I*^ܬ>jMٌ^p15DpV XȤ[RҪrv.)4@^`50LlV}NJ3vGoK%5<\$C:וiG9Jw%*ErNJzXqz"䞔卋B23_'Gci`0l4)y܆[&k7{@z&^ lYHx4KSYFF_1}v~@*o4)pokĸ2r͸~i\%;8Mdb̟ytZ;3mH縍cs7?n j~3/4LHH(QPsڬ܂We _e~ȰVsWJT\ !NQPSg̖[T%= ?qU9%>h^1DPC9K〜=n IwВ̉F$+DPIѡ 7X0R U-Ɖ3Fs4"iȬgH1aI ^YgX_XtWFs<2j“bus&ޟ-j񍲼 (wd d )ӣ @Fd@|45Ie@ -kБPJL9-/ikvDn'IİSHQlw!BegXL_X~%QYu2) koKY;6T,j|*m[DC(K_u4a8BS|x>f1u) ௕Tg2D?,PKSx *l̫dK* O iecˢxMbh$]ϣrd$Zh/b[;!D!zT'7ˤw# bbBFvczv꽗"B L i؍vR'ݕMdr3CnefFJR""oXηVƼ$g.j2Uf8Њ~۰l-Y9ŻÏln,G<۬TN[k{%,+ 0,E!,2LR֛DƁYXH@HzeT7I"(ypΤNkN GՔBƀN8`zbDj#lb='d*A*i3O1ZRcYL%H#kLI*VXҠUb\ S̝bv6wb0' ԉXb6ŤO&~l 1|c,_K3?n)20O?qOTT1n2KA5hM DZ-R_)vՐԩX@D}b3 aMB?t*Y)PkEDrzUیu싶!7]֥J(.USI1IU1/1LaVRʋ*S4M霦l9M=pٸ5Y Bq9,uZ& Y7Lep U,(UjY Ic.7ƎsB&fx9p),#oU+^X"` G gP]_QhwP#Šaf`]hQIK+tx*W M1?_9<⊕\@Tsh/Le:jV2:R PABYhyV(+]Ft:|`lf %Dutքbm¬,B@̻+b+yV6(0-* QdV.:xı1Dq^V2dJ9A b8_"8Zfߜ]`#$+Q *Ip+ oHyK}9IfF*WXpxcnзq'<LohxA }VTU{&=(щ*U~"3<2X).vh U1b,vxŝb YGw c#htIfFl=@ aϑtLٱum*h7/p\e}0hF{"0ˍ, N_]ڳl%6'Cp@("TW)ncNHp+5`tUXem1 .|Z"ݖ55ul` ̖ v˸ hi#9)0A@ RЃZ.p o7B+nl @0nT!΂g(8=`)(guKT۶PC0v_7B,ˆCuEб7X/A(eT<Ɲ1vϾl(y'{q,q-??i4IyOUHmoq-u8ێC8FāFllW7ݢhf߫˯OX ջ;+TawX;ܳw7Ppn>wdV0?~3hjWٶhiPK$XZ;5PK)K> layout-cachecd`d(& $؁d C2Uw\   i7F:&x QJN(1f3XR6: E=<1 manifest.rdfn0EwrfYdhjCP}]ҢUչ:8<{Όl[7?QF=O,-p*Q,Tk!ɲ C"[2b,#V`_A":q4v!Zl2 Kc~|' *S "tr5M }j"'.#*u15teJ]a2zPK)K> styles.xml][s6~_QF$K8M$5oB`$H-ۭDppw.|ý0017^ooƵ{T郇4KQy5ޓ2D_%/iK%Jxgmsb5mcF[hGrkCƌd*7߄mǞ 5;#D^wFpS=$ۉZ&6c=8cO`IJcheƤhEwֈֈ!ީ^SGn#r*_or,X *Qi j}0Pήe-QĥHv#<;x諄t(4|`R6ڞЌM{ұ2Q߫7/Vn(IL ݹݸ9*)cM8Qfq<*~:E03K'uI b<02Vu.Qq;/&$t6q6:M& A6l{7d#j|]֞ ׎' ,~yסF<ҍ4`#7#ȥ68;D\#C|t{**zwx9Q0F7*Ea*(v|ǯg{ ` R2O5(,j-0q&a5rBg ZSZCR'|UZKQOrm{eJiCL O7Υ_qkyhS?UVW]g=Z16Kr~)/wڏb~D Kek˵`X?Jmڔ>Dc?I//wla!EF(&DNƜ-&2cׄu a2 )M QKC߄2de;c]T pE;o-"AꂫjA@9hSO3+JkqlУ Є*5QDM&q#8@,h49&jIohp:<@5,a.?@C˝%,9(@9[@ER}2*X)4*ˣǃ6COM2tpje<#7^a *B]xXؕ^ᜡ|lm0\<aqp()HE=dž kM?UbXe18 jހ" fxy!q;TyHGL~j"+ n"!w).D偄*7|Ai}=`VC\X1`^ߨ.Vk[ &wf+WZB~,{:T96յdMSDC~Rv!3-j=_0rwT^@%gִyΙgso ΚҽTHN`-XmXZSbd>igWc|E7 P~59kH L\1*\!cSd1]5ڬC)`_TSVDUwxf㱞xOn! lC3tbH^ L d2}.b/@@7Yb O^ժCRD-ݸ8wZQ>oFhnp2ԧ;y% D$iG?.m^"`|0.!x}l,RA[VpՌ+ _( +ir+(:k?Akݸ1탞#2urªs$Ù':섓4Mjj6x_ߊ3`>f},T5Kk^ 6S %Wَxc0\R#K8. E\7(-zհV;) iRj6:5UÆ`}loWU+ʙng:Va>)s{w.2\'d^B ^ϒdy~MA;U/5<%Q ( -c*I02È*0.[L~?"6HR\Ĥ }@OT6 KܥOdR%7Ut,_93wsS !j:nhYdB_L/Z%B_n>ѭ# Dk]c>^4APK~` Ψ$ԭ /g qz*㱓NB&xAz6,[Y&ݧ$1/3\.$gT@3?հY~MJzY<xzWMK[r1/7ϓJ/ OS1^ eO'N к'u2Tg/wR]=71>v8-hpC9'x>FeA'' a }J: ݨH=lCnCB+wݰw~9-=C2R˛Mrc7q}B5j(G%4!p(&!kKK#$(9"QaNz,͗w 8w{Y[>ҋM$^xwBp?^ާK4Ca&OA|*#hYoq(Fcn1ܓҊ'}!*Ey'5%$z(&RmM7i9=_Q؉j/_o>eg& qg ȭJiE0&ߺm ZC ,d[ }qn>׍ݒ+K͕I}.o+í0Np;-κ0;ɬX .aE/n-ݘdd..zrk-2en=UfW=]6mybK~PJ֗22c X ˚;pw68ONg,mhV|Xtff‚P^Ks#خX4G#URҒ5mB"ji""jeB"jiR϶TvtrQV%gQ+^eLqi6) >p=xaY,7mQcc2k/,$8J\vz ΧENrۉI؄EK((b8.pO IϭC"*@5-CgY.bE !@T 3I>>EHK+TbՋr M&%4|BZ}6'vR'#qz#_~f?iF/ a0U ]JPDronԚNj{?>1g*V;j+J)9Y|Rd] )yX?%ƒ]fˉ oPKE2عzPK)K>'mmmeta.xml OpenOffice.org/3.2$Unix OpenOffice.org_project/320m19$Build-9505FluidSynth real-time and thread safety challengesMarianne STARLANDER2004-09-23T23:50:002011-04-02T11:25:172004-11-17T02:17:0498PT30H36M32SDavid HenningssonPK)K>Thumbnails/thumbnail.pngS.<lζmO۶m۶ml۶mwj%$dwF"e`Vm) #!,}㦏 Gr ݏMXMlʹQ,Cfb Q, -QψndO?;]?{]J(ɸ:'|-ڑ _eYdp/=sR|a_A&!-?R*Z/R,f60 KxŌ(L1>$Y1iOngxJ-X+6 m~=*G7{{Y}s1IJ $Ϳ+:$~{p`6~ V.gYkkHk[~f]O/a 3,eJ'Ojuӹ^^eELp{? &مwSר ěķn~#r}[~.Fߓ́FKn>?P'l|ͳeؾ3tpGe]غŰX@cam9!ayA |7 ѽV̉ZWqNMU\N~ȟ6żmk0x$9{k4?ꭡLVOIm/p" Q jE{EsS6X~U/jYUc*~z vOξ-w)Y? 4VdQӕjg5bt$ x!A"`o+K_.çEM?i러DRwF eZC~_(܏`Z}ւP9#4Iy|G]mnw7DTFv֑DGŊ GirMNT$n_3Aڌ=?olWoQG?W+rd zno _pWP܉]4dSyźVL+`%9\k 8{ٶj[n%~,C'l H2@PˊY%b?-Y!_(ṘkyvhRDIqrOOhj6-Q VsTqjG'F8=_8Ϝ#q8 NM1j> =fKxW5ơJ9{vCn6 ^j|LG~nM"ޜ ^Ko{-JVhu>>xU}d δ7Eصu݂~mYÇWaC1W1vv~ꩈ foY,:uʯE |3M1a8t"L* 7KwWTqr|>+1s4H(w~2M~ |M$'Đw;Q^n>*Pߜ^@t wȼJwwa|1HUqy`=",*`&uQ*t],(d7mwx4k7ZXYtV%RP)oŏed|4e+I=c |lȲpGF!Y_g|C߼6>|:2W@ԟïM P{3񃫾_OnM~H9y#w|.2z{rv0n:ZΛn\mmG=c6ds||?[{(Eؕ սMcG l>!REs [)7ziǎ]$H X1hX[frxB#Ad0G#ZQeq `㓕{ "MKk'TӤ]1 ~0l\~X.˄wΫN_c<4҉*jUt~DZԳV tFuC_Tkc4XJ 5h4MGT=N&?dXxLgN&Dé1ը(ްL7!x^?\ed-c8=(~Zv%S r}Rv.ӤFo!qt#ى=" ~5f~)b!c[)+aKK|1lht*bm_ihA$H낊V;YȅU65oذV+/'u!$9Olc^2Td0Mx2l)}s^wtXJf~Z8z.jeVTqڒSV qCi?ȏH7Ãڣ_y.~`4n3#qwqiM{Jyg}ith<7L)>WQe*6 ڟ`$|Y֝H/K! +8N͕[ƃP>9 QN$^g1N(o%."(_` #zߡ.!ٗOy9 =nbz|@s8B`ݝU~פAߣ̩$ގsE8I< Io`@OR`7/~ޔ2Xisn H\"L6{vt2e]n6;Zuy q$AԧYXt9lE79.$~U h|]ur1Mp|:7X-,z%+l l=uܮ{7s7tW|Q? 6V RXɱAi--,KzJ&{BI"; Yι-8;S3^buE4;P2qxUJB_kTp},| ^&?Zk''+P&dmVIUZfeg AaeZX~"nmTMQj lաYhi׫:h0#AN2,a%Z7./`u7v NH쏎ul8 MDB!BS0f0/2!`=#%FGN&TZP3Ģ(Q@,Pe  7f}ao ς0t"WilE|zBt fx˓U 2[{:Ոؗݩ,V7z%01J/`{4CC{Itx.{_擆ߺ}UZhLe'0-f&>i78dpˀlyD:*<fPraq3N }OJ)փڏ e -wA (ׄ"{]8v /slo-d_>88⁺wஔ?Nh +j?W!t/0 J0$1Boj}o2rx!>U\D< 3(Y>Ť=nB诖]bU+5x ]c?9hȫ.nJĂ Ŕ)}e֕hfQ 7;{t-DFJMֵF-J?{!h,4Mc3xg7fhVSܬ8SbW ZEY.-e䠶L:c3$u"}xD\>h 2(ڶ/$al)%+4%ym_js14( 3ƃYl <ٿ`6|߭Wgwe{qLEKWm w~N*ֆ&iq׺];DJ!uN(*2‰(G/j O߷bl`2%!Un曬uSq883__Q6% Ұ[֛f2ךY_iv?\ k5,=)Csp`G0(WS$ pr#-Iԟ*ɭA5@ʪN.O䔹ᛓ""krщ\h " /Hz꫅*ӿ@H)Vq+~3A\jl^ňiUTP*&`utgӾHQsrQe+ uA0\ I ۧIZ}`idnUXDb)ep7\+4/3C Sz#R87po5za)׷UfN*6 0i~=]#$.d^;^y߹F' 3= t]}|}b)!9d63-!aH 0dZwY#\Z-X'beă^V^ϡ H~h  #lC$׸P G?{ގc 1?owj u:(@ cqq2$XN8~HN!'PTnk:ؚuAq祉P lθ5^1 :)IH:3h+% *ndWg~ұY+OqT-‚X~}jk< ?2|di9+n=sRι3=27V%sf*lxwIjo xs]4-X9ǷpcSɒ&nb1.Ry92QsOhjB?|)]4t#f ޫ^-g,)E?r$).v+$d/D"BZ ˏԒ"ErCH+ԛ%tUę;"\F-FXƼ;LxSIJ*7+>F(b)xT;ʃ+dd$MW͢Z]}d7cB G`Kƺ<j~76#$Rk}ˀG2tn1)v z O,KXZ02M u=₇8TXjwSױV~*K) K@|/:I- C>l5cpfÛ@<&㝨%Hv fQ1ze9==$+(, |x Xαٟͦ{4JKƷ`U("{y@詔}N :K s,!^^z7/+X'`Yq׶t#`l74v˷7 |F뱍ԚkL1ry➈Fe|SW>ϑ^L0뮻BP=0pNľƊ(?U/{̏*![~8ЏR<2 S_b[ybK&]]c#R2g-G*XA@O{*v}9$oC&Ԛ/ (t־)6|YQUMDi@FJV2w GnE24݋I{r#lxNytWL)UtΝDBVFAzl- X`޼j&JdB҂kB kOp-M۪Msˆ)i*+6 kEu93 z gg((¹PR&[ 0>i wo{G(鼻l% ޢf/ 4 ~=nPm_.DQ[8\;gYd$P볳Pٖ.SiWI!u@)@fQ5?F]U]vLz*8H nWp c5jǓR%W{>˽je}Ja<YWm1L;HPZo _K+bp[of1_cBZ 2Knc@cˁbX>ZGo[%)/ l7*MoNӉ-c̻, ,5:,?/`Q%bCLPc-9T-D ,ܛ T&H1$18Yyv#maX]~=GioRC7%)ih͔ '"\($r9V#a~  Ymn 7-&ِIѲ߷6aN^p8% 's-I&zr1 I a2@ hxsJ_Qm?꠬:f]fo?܁VRf}s EbbSlU4kS*gS?-U £ZԪbsQWv;2Y6^✯lm,oj^iF,4L vaỒG>1!r$s?)6g65׼`(0s$Z P'72#UnP=^aA.Y_tʒAS4FCt?3;; *#&U e^fT:+TXç^G/5 _y:,U=8:5 K1/z1!~gAW"w2#A14 a0Eb[`A&(EF|]YiNAz 3$\\$̀"@I(F'9~q[RԇA@L;Kny'̂9ć >wPSm&ǤT9Z<;Ǘ3\vڶ/}{sQngffE~/Ї蒊UM\  5eEV+WM@5U?烉\_Ю`sk |' y{ˇi贽7hG,Iӭc ^t'3зMݫTYM.p%oSMUygo5F wil$MX!Cv%4K@TrxuDf,*ʊy MnU9*hO{ ¹N Qrk ,~M= DKL[kqV?FeW ~:;+ȴ6g,PY .+tkL6*y D@$@k_=V'[Xj'b},RBonh1YFÂPy|@t12l9Sd'hu.zkFQ|ёP]rXi _&+gbn=5[}bPG9Uo#a b26jW7 < &O5c}XJb/W`ZŨI*|Ǡ4d;xt G7d -FDKLԸM6k]t0v ݍmS9+@%du4)ދ*!ES*[:D/35)I eu`t vNh&Iq5 2EbdA]jP ׵/Bv$%,$M;*I,fS|yO!YX퉁%OApO2BB9Dݨ9rZ4$+RtJҐ8B9J\Q (TYK$-eXZ88B/2]i3"LۤPY\c LxHř2Lk鼞T\(*u7 eyp<>fm8+t)"Y6ʠozqmB(%85zϟ)<=fw1fa>n>' f *]}ϰY5u*J0a??$:2;$"x(678j-W9-uC&Nkv@]dvqN\O(7LSv#ҬibFb}l|o#0l ŪzmM=nb{wAHtyI6EfJoз.PV&Ko<XQ*ݕ|kh**Iw2j,D^b(瘸M`уHŌ3ڇ!w}_ ȩ}e:rp AjL `j')g].[ Q4-.1dhSb h&wI`$mkFc/G(Dr!-]}c֮OQ=rgXm.uǶ#"J+\BD|%7HF(>)()Xf)ZnK6u7eW'j-0?µSOU[[mpZC. U- C;m"O *\2~aLۗ[`, 79_Zf2@L~)tdJRdm-gY=Ы6/xo#eN6#t?sUض:azJ HHfn\p(C`q~_`iem $h_Faނf"c'A[%d^Kpᭃ~BKAz *tuiI:H:K" e [T6X|xG!ϷNKͣG LcY1ns8!CT +9bc㖟SuIsg/d1>nDd vH=21ǘbo$aYhGz.oѮ<@,e@(#)nj,,AΙbN%8$[3Ud4&a&+ߡK 8fvB2$g,EV&bŝ[ނ1+ x,ݬʼnJQ`6y/d* J&@dkLdW}!5m<:F/—U6 My("k0nJD\P},1PznV)S#3rw0-DB܆^)NBjY+̘^bӞژÕ%-*pk-n'm7}?;{$1ϼPƟOCLdLxsddNl\ E$tH; Gt{ߨӇ,zݪRu|vd92)ua/t\yŎzb3eN4Xe,13D6caH)93lt}\;[)Xp)PZ~}p_3PRU'Ce4 l )׸K:KS/#Νnzf!%7ȈԀ+|8H_}K~>L[o ;!,fY&*wۺ%E'|0(eTGE"*BOGnPJ;EW~L?|gx:d5U_ r++9H|sfnQtdKƳv[-zrd@;jF*C0'>dA/3?# S!bhÝ 7L_tn!30%.K ha=갉}:Yܦ&AX;?C9 xww{Y[%Zsuܱ;!*Y7+%fq'"횏pG( krGA$$:Һun/& |MI;5:Eu`lH^vmlP(~j < ^I;|k|PR37YmaRX7m({ ú̈~`i#4/^^WmHҞ>KoWod2~Re4U C۸?9&0Wrq!t>RƹfbcYRXo,i6H"^7Ux q:_odYuJYSb(,To./W ˕k bҊYVLqEIǴ7cY$<d.;hYH@B@~Oh R(bڨf-hZ@ lJ)8RWπ֍?i .-%~h!U>Yz(n`< #LybhTyH%BnSԈG ߊ0kbټ|;#Loy@7]'VP_77^?e zl qݛ|A^NonЭ.[!9N;nxfj} <1y9+\scmIQ 3h#aS% ҼHW7ZuXθdw#L8̭yKqN|Wuf463o~^-;Q'l4ց_Su߫g)|G"˽|"U-e ],5Zb?/G7VsiMo/sw+WѤ끐];ο/f2B E}0-?ʈb$Y5t)ˊz{`E]4ꮡ%QOxVrb %@;wiVFRZquy}t %AY3cM2- lU=Csƒf0qm-(EM6.\a37 n w y3My&>XE>醢"]MĝCUh 1_atctgL ʀ&,N ʦ;D /$D)UNUWdc6ޟOw5I>ٶDKInuy>O 3`buy2jw?/>j)ܭgQ!JUAtWFmq\Rr1>ؔrH<ŜkS,H絬UgùR"GPqX􉐗t`$g[A!-ޫuFs)?̹ͦD>z[VT.ErrmJ1^Sy0z^(C;yi;{geE&[+l'nv"C>:d luBN@!COdi4,x;6^bOj4`K:&U;@"cE;eBO]yc%fz;UgG%Xf P̑'6;=* U/? E r!p˥C['~IbtE[,:-#OAqĂi6ض5[FwW69/{v ?4䡅ԔzBbҹ&Pz|USS9?SV:7iA@.UD q۟HZ=nV~Aӯ\.&8ldLyh69p! Q8W lxHy)Mk'-xr֩E\JO0n[z#6K;>C ւUcRA=6Pj#2;:抌%--N!$R7e "!8'e_˓9[= ήYITkΦ4 HclWfhdnNU eN\.q9xr%rA̩Nf޽ JCѶ {7H\Hq :%7AK?y֗XXb5OD"ˮs(nǏKW.z;#BPߐkck{2>[UK e7ěL>j"QhGH h8 K; Qka0~wt96yZ,'xzr(3ln}o&,:FI#[wH4#$]JKI uB(]ե5zn&B<$ŕeM9G5 1!xHL19"׃G^- c$֕M9AK Gj~HUT"y[6+'L?(F4u/N87 '#~/EKPe3G ?H#C;,MB2@6K֐ XOxx7b:Dpm9 H,P*lXi$}P*!>y ˤ7K$^ɾ=Ʈ|I ݛm۠/#u$~2fP.%EiLt I&1-aSv68ٿ@2%t_ S۶.0a n±y&uR@d`Y1X|$k2"K*_oN#PrTa WC5拖[m5pN#FM A_P=r"΍nBܛO-F}U JqZJ]g+ *^6;X<bR)oGD;Ndv}` * /I ЎY-I蔨/q>aj(hhdJ[g(3pWAv^2>*j֧ Z΄׬?rvseaW>O(խ]>kbZjei9iG7?c'f:-h~OeLv*_۱aoi!~ʉ%C"vq?d5cdb`nk R}?j\Z+D 1=eډD3P l(vWk/ml@ RJ>#/ֺXlz&7/r[|a E)2)ykpB+:~ ɿ *-]'J3Y V ]#3- i1{Mx}ٯG` `JCV&6{9YZ4?,?k&aA TB~zqO8x|k0>i9I8Jʄo @~(=U燜RT'L:f#!~8=Όy9 gIR~L'+ӄII|@3J?(Qv)QD'#=8 Wڑ.4X眬_eżr$B jөXHj#!W &5k)d0ʜO {4#},}̜`Zdx(@$zPbƌ#u<6/zDEH6׏B|i%B*#U!Ia ؉] !cb^,k+Д6g|{}1!}E֕NaX@_"Ms. ?pUc5k~=ٜſ-VHa0*A!,&7pSi3k;¦~LXwk Q Kfk2doLi`pp]nˆX"Ư/5C;%/-;J K1cq M\qW$PVeaԃᡕ&2ҒEtc_pT N"(Q99 (gtE K4kJ̇^D~^S+HJ/l#"3kh(K#ip͆rI[+!jbV/]|fxj \giQ2;ˣg C{ K:F΀x!0mS\xiVL"2kY3>N0u(k~~3VaOv:l)p+=R|'4ׅqocYٞ_T)S($F !%˪388sV_Q0^QR!ACyk4|=(z5bgIEe}:cdD-iGE*Ҽ#${i&R"g*hMXZ*~v#9{+9m:_*C0'iWoKL LblCg^]x 8clivx+'2܍ zKE"t"IA@WI_+ Y#c*Ns ߗ3r,iː|gkUc LE2[~Ok\>*A&fm[ Z:th >7P}-d'1ِNEU.ZO%sId䝦Ч3)0GQa0V<0 Bl^/[D Brp&Wj!f/H-vZk]}Bﯳ.AmذY\to=nY% B,?$TW.C6+A//]#EpP~åw+>0\`Hyx$⨷&^O /,J?h;vLJGR')"(fŬ֋p͜=mcZ%A e$% K!G})ĕޝ v :cÝG2*uHNrrlT1̛L"+r;L P鑒LL{rr,+@3Z ecd"x"ɡOyy.Vu!O?&w6H|W!U`-Y֍#K*kEW M%DYdWa'OzRV㪏'. Wk@2!֏]jw'?~v$˴1l$Ž䅨Ur6o{{dto]4Yx1S.~K  Tn{5[Lu/Jwvktنp}[qX)ʽ2 h0 ℨ*xX lcÁ w|}E Xo#H.X(8;gqNL`SWpk1JfMqZOt=Hwnsb^O[3x K>_[$Ştэ2GʵkjhA {WFjo0wv:IrW8f۷cHs6o]pG+=z-\ݦݗ!̹P6z~U}b\F]5ZƢIÐuZޤ8~Zf6APe=v%Zb|Ő=u8Y%EߍsiGQ=|/;hK~kFksFڝh K35#SB>+{:\υCAHWy.S"2#=8$h|ẎR֪-V(∓V !2.f22Cs^dT@&9Nce_O)P+1f;oB2Ô}bRC?r/ƣQJ:'ы ᤪlKބ>ߏWڄn­W8Ği(]x nѼ8G[|mrL,WV"<ݕ +͠[ry~by4XRgc$xG*,j͡ǚGn39ʊVh&.P~ht&.<;͐jLؔ1=3TI_lf#Sf:j~O!Uz_Gp#(88`p CqޔL+CG3ϣ)2^hhg*q(DbpwSiԖv$_x[kh{*5@d:1J'Y'qx[#c`ʽ\FP߷XG{?{!8jE4/>i.>|{lMz jOH6mE-4姼vgdX Q4>_+tiA]8{WauwBˏ1thEqEϔ򀃐L??](p6Qvk[~ayk|:Dgs,L|. Bp +xx䡅n,*e,o"5C کYOȧ'iWݗl~!WɱAÖ1n"![*h͛ K%D%"C}|fNdmOg%:|/ J_*_v4Q0[;xxd.۵M&ں2XeiEi$Bـ~] FQE-2,\SMKm 16?HHwc^xX1rQRP(+!(S?/5; µ"cF)!Iz]\cU%<`iNo9S=q(Wd!AovWcn9+3oFcARƱ yʒ.O$5^jb}̶燑,IVNJg!9%EˌeϚSB&ځc&${xmT,1s`י}>q^]m[y' 49s3@ Y+[bFt%ECPjFA5&4a!Q쏯|YL\唾p4c#l Clp4JS @ę"5SV /),T=p:I`ɌB,̗|!)^`Æ^Otq6u :xҝrQܚV isNqB%{V;-%kq%Mx:vHOQaMܽճ[[$=Nu %ZԚ>?+\>FC79aWaVMJ, A >,bU/`Lv.#~&Owqk`ǂeTbBox^C ¢pa#UQl-$GsGu\j M{BۋPTQ+(2R(>P-Q.gi'/A/>irlF#O2P"*̰;,zx E+J?woSeU2^ 6~5/WŻ_@ ;O''y7cMCʇ"ץw={jV#+%)%QxyƬy+av3z 6Em7eP?5!fNZ=ڎeԑ`,}:jnIq޵&Kwe"SsL'i!)12+<, gb 1$ɬ2~4}8O a Ts1!K.,Pu Do`0F7uX8or,QkB$~;"&Jf6@ku,^ϛ:85DU!YtK—=;ᜃњn# }L r<_.cOZ@uPyW1dݧܯ#--^4wcue>v4yTIB\Jf5r(e#7=L305;!H)]h7yjca`#eKsN&%W w7b{*#%yW׽9孴N !,F]?A Ur;kG2d ^-,̃43%=.`~ȥg[9V!vB dEIC|yrP-yv^縺|)t@vzN!Dz;-+@6zWSe!o3٫O5~4h"3&>}1ezW`d8cڄkT.ᮙA%R+UM˖+*k07rNWS#7[#7ہoba)mUqYӳ 't|f@Z|aqEɮhw6-/?ڍF1UWf ~Ԝ؄[tp8W ~p}Vŭ w_^ z?w MڮQ`tӽ Fݿ\ii/ѿp*suΝWWES^H+_`+РlV$^%|(1Ө+4EnDb,ͪa$$*$t?]EY"ק?h|@+ѭIȭ?RaBL-:[YWG4j:S5^]2\}l{ {P3KUyb!ݧш?m׉Rki1,(EraCstAھ8jwLM]MWg^h:X2ݳdMtxYg,oDqg~m7pr8z5!oZQt0#Mى@Uvgj &_KCrJgmvƟoR*X|BB*:`5|_>ǀ(>Sڡl/AbΒYd#gc9E ]Ϥw?  <^1v#&%ڊ)@j~>{| ɂ Ɲz#m7]blb=yV'61ȣc gÆgύ٧]Lwœw94ޗ/F;sGBA'S+Bb ^1;׏}99 t_9ͱGKdFǼڷ?DiMpu0I|V4ʓfM){Vq1M6օ6T_?K O? rT*Okh 6Px%X V6zkKK}o6du>-%MƶU.} |448@961%7p 4cy4 6B$54X 4iDn";cZ9j׭%UzMkGq]Sgz1r/u&.kC&[U;3Q@Qva#%7DwOlbES㭽mΛj+\~iWzSyjy:u^=`hɤ[ҩ 0\ON$&g=9:2t@/Q\Z @\п$I.e!^th ŧ]i7<&g^ɸOI1b-÷L@~qf +D9+ %r$!V8/&oak)x2b &d/iistAܧ?AןƥT㄄D LĹP#YrA"UIO`uAWG n*h@/XP $-'LE9UiPKC&<[\PK(K>'Configurations2/accelerator/current.xmlPK)K>Configurations2/progressbar/PK)K>Configurations2/floater/PK)K>Configurations2/popupmenu/PK)K>Configurations2/menubar/PK)K>Configurations2/toolbar/PK)K>Configurations2/images/Bitmaps/PK)K>Configurations2/statusbar/PK)K> settings.xmlZs8 ~M@wi iiB˛ITH!п.?v93ׇfHɖO\dB] _ 0]垺Ӌ܏_5s/S Jy3Y_bʜ@ AWkϣ˩Δ*7V**I|Iξp1JR>}qvUsT_&*+Z|;w2's˯\d@Ams2vCIjMNHG5 l/JŋE7Umm&jQYp;F.[wlJYiHӀ0X,}Cv3y_T"\+^X("w`Hђ5ߩdwk`ZɮrxQo=cyEQ.7BJ 2r8Cǒ^hn^<~M=gxZ`=f7bAf`mJyF0S?!z|OI&xF1{x}ElM]hHסMFW*RV׻A0[c8SqASEX]w ӷW5.VSo s|6ў|`#pj.(Ƙ%YҳΟU?^fִf'BJ_=zOQ٭'M1x Cݴm;NX ۚұH5x[{3k^+~ K8KqWx̝儭wNX{ Cixÿ-!{,5$i\۲iGot.\-1ҷAJZ}toZ!s)ܸcV#7 #53&5 L (=aHޟAg 0U}A0|ks3i|妩pvqw)aG=I(SLm"H 靈xڲGWPvx j a阪VAYү o- N|afm9qjP~* YlMye3J {M9z15JlƸJ5l+{BJ.OLkdZvh"n)`)¸^W?cיXԸIXLhUy?S!koRS]6mtϹipg=c3#КI31_vD;-- zܷur\{Ⱦ):f2xXP\FX+euլxTWKWqa>66 F x6]F:pӬF-S8IЩ[K֘o[n#}$xVc`!؞XLhr' R`вp#~TJ/a=`KZ't/-R:{ p۝Ua0Q{Kao?'2[ʇdItX{=nD3-l33}CEF)%ҍuEu?{~m ?ld'5=[;zƟ hj ,[wDM80O]zǍE̵ Izvtk !h-|kc1ւCuGqi#JK[/9K+:> aDGQl~*PKʄTP&PK)K>META-INF/manifest.xmln0 } Cbhg)=>#ӎY$~r4nnP?ERwϝJyitYR{|Oojف z*$mXtiK_jЗ$JcQF5AiuTFC'G1%[X~']g{l,>;M7A*?A4z}vכu-3JKus}^/z_Ϥ INS4r08; z( vn|\.OB?!:vH0z VTӡ{/at#I!PaEpnZ-5u'oa&F {cz?g fԁj'z8Yk(>ޮ%ŴPKDwfl PK)K>^2 ''mimetypePK)K>dmEE-MPictures/100000000000000800000008DD0ADA29.pngPK)K>J3__-Pictures/10000201000000F20000005D37AA1023.pngPK)K>$XZ;5 `content.xmlPK)K>#LĂ layout-cachePK(K>=<1 ҝmanifest.rdfPK)K>E2عz styles.xmlPK)K>'mmmeta.xmlPK)K>C&<[\sThumbnails/thumbnail.pngPK(K>'Configurations2/accelerator/current.xmlPK)K><Configurations2/progressbar/PK)K>vConfigurations2/floater/PK)K>Configurations2/popupmenu/PK)K>Configurations2/menubar/PK)K>Configurations2/toolbar/PK)K>PConfigurations2/images/Bitmaps/PK)K>Configurations2/statusbar/PK)K>ʄTP& settings.xmlPK)K>Dwfl META-INF/manifest.xmlPKfluidsynth-1.1.9/doc/FluidSynth-LADSPA.pdf000066400000000000000000003141061322272076000202140ustar00rootroot00000000000000%PDF-1.2 %쏢 6 0 obj <> stream xX{C)dK,k[Gq\"#@mrv\&}B4_'$yfg~۝ݝ5t("CO6೩lꚍL cm]GQcMYW $:xŒcEgu]Y 42"cD@celn`#t^YYBzuum|CM$BsФCeRml?A7{uyҖ礥lp^K;kh6PUp,WpWm)Ӏ9s4S0?s䉟3#nM2 FzXo/1Of\>QtdX$Tp.>?A`sdž&_xg2zx,6F^?h s@z#أ^|oR䗗<&ZZJG2; `-޹Яp&sdt$E W3ɔ"S2s<֯!t܊iX6RQ*]dPFGc#/v2E: sX9/!j"mSKm$▹X%Fe޿ou5?cH;VPSW.0<&ҋ}0 ݱFť L ruJ?7Ro> -ҋK6y?jrQF(CTe KLk%0oMؔ ԙN9a;yZ/İ {':R,,ɒ5©?&6(K$ʹXqw"8%@b[N]ibΝzG;dzv>g9펟;ufO|po[֟6 0-~ƒ Xr\~`ƇϾ<:{,=8S01%wy2n:6 )j7Gw>D:M"3O ď~YbxÃaׅUaf>^h4՟ ;"V5 Ŀ H+0anh|p(,&˥fтBtjEOLAGİ(o0Ɠ6 aYUbw-̧a0o""Y` dkP4䰦i50ÆpQ4vlCmO"#%Ydz}ٲY5k#Ͱa+k~-iۓܤΏ`{IiFu%/ijRDx.w-`QQޕhyiwVE_,̯+c{ Rt4Zxa E~[%(Ͻ,\Egz}}tۋWg,mW Ӯ@ ZT [&>7R%q$:R_zԿo6d;- ew{Bfe ֔ZFd0~InEx~|kߖWٗ߮%+ 6pe;, p ʏb-endstream endobj 7 0 obj 2167 endobj 176 0 obj <> endobj 177 0 obj <> endobj 179 0 obj <> stream xYsr'_i$,K%x'رqR1`#I].e )!gfwrEa{{M\ی==ԑkb2#}V_m /nlLm.o~pWfϭ?>XnXeLZ~*XyL o1E_Įݟ)ַa!䘎\[omUDɥڙ6Zŋ|<O3(,iAsZ=͖==hBW&Vd]#;crQC3,'1lRG [')oߌ-0s?3`Y(6|r7I #d0[6L[eė|'^*IT=!,3d6 #?s1\,1`S>EA-ƺC/oZrk8v/-HE 㙾F¦/¦ϐH–F"mZVĦ6DҴ(DR*gj`IZfa$ <ߦM#6m!(vCH$"x/6)A4Ei8NH$x Dx4Ep*y6Ue4EH;"<&q"6)ji-#VP$ϳĎIZXPQ$$f&! 4h.( llP/(]^y bx@@Uk7+YD^-4s-\KQJy] YWgM O|)-kRi9}QON1YGdBL.cKob;MHNcNEyL.qH.qH+f@n.GV891}fvڗ"8 S.o=U6E*L2QMY>}ڦ$| x\ud:3&C"y|w COOéS>b`sJ0$i-yHp\5@S :t^x놾}R|n -0-aPw'#Ұp0QOA/sx&ţ1u6X>Cfg<<ߦ|n@XpE] s_V3ggé2|-$\ឰ-%'N>g:v~'-MX w`H#PGP<^NVѬCp&:B2Z \xP'KݵNL0$Uœv1W?Njsz .:BjҜ-irnzB=2^R7d$1%pu )n{0A8XT'Uv\J@,yFp9*/jd}X< d_=~'(e F[zV urtv"8 ]< %+V*; M$oD`/r  l/LL =T#&A^*{r˰Kr y82W 9/eF/xpcCVOhC]}uXk[S_@ L\uGՁHG&U^-ʵ>KlѤ9s{<v ;2*\iD=fAs,'V|nXuRS!DN(Hܓ'N:3<:2461:Rj¿ t3j@UGۏo*WtL?cczB?F% z']|p =sp٩s-\=4c!@gC脜#޼Cp=:Pb5/L#Bb؂$=\8P82!p ~sz2<8ʑxZŬ8K-;>jPZԠAvo#W<к0//j&8ӋӋcKtY6YCBفqzc9=Ց/Hvf<JW&ўˋ3T<= Uy1u6wme~A*z>w`ؤ2h~TN֪գL9UZTiIC!7jfP"<@.Hݟj+a-mݕowɖמ,ܫ}}޿ӗݕ]}woԎ{{o[_2ﶃڇɗj{M2L__UƓWnϳ7CEƚRWHo;jiQ'TR6*)$SaݕvWޯ>cw؇ɏk8FVdItrn`*]Jt)_:-endstream endobj 180 0 obj 2647 endobj 248 0 obj <> endobj 250 0 obj <> stream xXSwGI@H1l`sWrR{ڌ;q)uڷ{_eH'Ì}oyoԑ?ѳK13y~#ҘLjoZ# ͪcDn"$h)װ&o|LqLGG93Zb,^"/T/l;-fRY U=2!ɶcvupVv #i\J+;Y9D,6jabV=f6eUa"C^H {oWX.4<&2D lpIyl B:N[ cP=(-Ҥ O0uٵΏ8wfU̦Xʹ8fW,a|*9Ah,NM`1L+8iͦ2rZ4Ծ/fl[zH0MINxs>f]tS`pAY4dYf_ s|Т6Y3~ ݮ|IMPZ HNiN4V,qv|=͛o.sf\WfJ\Z&(Rf"`͔ޤVɛwg)*Vepyf+|u z]kQf:3;pRц" Nؖ>\  X'+M7@(K_@|jj#X;C]1 37e0tF$v\.C?HvVOTp}t73^gejڼaNZ iB!e+L$}8*Sp XS݅B>9~(x̡aޮdW|xEI;Ll2Ó=>b<$.Bdž{;XS/2 2+xF.ftAw扐4]LOfe\8nc1%sZ^BIUiAV:NM?Q3hMvMlɦ.͵nb٦El71{T䑷sʸIiWI2r :Gr {#"R9r NJo4 PS5e0t2iYS@r@=2T&$ᕫ!l"A[K݆tg։|Ƒ y*13i19 3%=t>9X`iUe*B#75T}2]hYL4Vb@o^ *>(/vA&K t;Ek_oyG`Ťx+ntओi?iճvb=FL\`75s@ld)V]iʪ*uW WPKb bow GZhkCHpLq`6f?gW7^&*&Gg}rCuk2Ŵ̠n3VgQ/gg1 U1q1,TM oR0lA׵үL> ]+y봼Fpdd ^&?{DnPi7AdosĊ_ rT.iuF:M#-޽ds:3=gH\蝬oO \߼bv,F 0=Hwt\Éz8ChN?i*i_Xׁ935" gzs_/?! b }=97Qi0# /?8~\Y+8b?>3)OO&W~}pr#-r釧kaBfkj u.[n8Y8db2lkaBF jendstream endobj 251 0 obj 2356 endobj 282 0 obj <> endobj 284 0 obj <> stream xXSNEheI豰bAvlAd#W$"fS'㤃 رƯp՞s﮴B0#ڻUJAJ4Qw|Ca]ଟfK=@xH az?tj\d\dhoOCj2{7Pwxj%8~D n^3ZP"㹦tB-IͲ܂`v׌cJMms }G"ZT,gd>6GmHA;DHYS:fd`Zt0!P2LCӴ'S=lۘ p~hļ8-Ok0./#QGiVn@d:6Cj|rjXuH$P=11e݂፵K"-)zGjp+dВ66JL8 =du/fHVS\?VV)f=QtOź$:A#썆/Dzѡhm5ٷZ}, M:c_9 #ܬQ}\kv< .j\~ )(r?nCLY9Mw{h*Xjj3~[p6,v lTEѯq76ךQ,"7^JG(EqnFG[=tsW.gpy g1_0.PשDMG$E,+%0JU@F iao#}AEFN/}-zӇ(R"Dw)?ap\kuNt?CNh|LZ,vzJ ܀|QEu_E FtPz0сh_#JW`Fǣ Mӌ0_. I^WH4(.a{>+3v!/) J@P5(4 jG5MGsy. Ó~s}۫l/n/oiz?2Xbۀe.Ex*imnWg/MmOYif곉ǻڞiê/=쬽} l{ ׄχV)?tx7McWyf<Ԝe`d k^G$bMļce{ ,{~qg̛gO*iR~J*fyMeiww!8~w{]e{d ?qM x=Qnv&Ϧendstream endobj 285 0 obj 2692 endobj 309 0 obj <> endobj 313 0 obj <> stream xXOWDaf㱹`Cl?ӉK7! BڒRo%QWI[U_ssӭv{=wΡQ3P%I| .x 2׉J\,QFF/_"']i<&&QEV'T& g'|njXFL!*):T+dF uN*o%KndMeTS-WԚi2Kjp#uB×~%N%.PImF ư0 eWERz%s& uCq5eS%֓|vVSS;HI(Ϧ4bWSjg)V_TL.MfBNS3+Qe#l{LmNZQ'l;0H6C+QCvmvǹc3ʊq#`b"L;J.e`&ٳ{W),G1$9eZ 9!G<D2)BL ӝx SҔ +G%Ĕyus79A5FTZ1uTi@q8: !]VqY,|LEbdžHLMPaOs:--.K-R(w%aRBba{_6m}Xts嗢>~d@4Wd@R&{u{IxLb HWn& ݁:_өwZtpT+&Vf$ylNKef/K]g~\c aqH$!e9v]Vea^YbTfr9ȁm:2L{c P F*%AU Vy#t`la*(@Z76'͉^Y\Za5Q˻D;&X) m!Ѣ J_4P(hHA/l}xzN(v;cpu(.PʣMw0V! ,XCDBB.;#/r&k*rs#hx&+= ͜>/&Pn $ؘEnVaP"@΁uk V X{h&tCf㜧w,=8,Mေ7r]A悱ŀ(V6ek=m P3#9ݔ٘h%7vΞ!Lo,%||7a9K,+l AL=>P%jCظ]/ ϓHmen+r@xv"V 3]{3pҰ9^WilۀD XLw(Sa{M%0]an F</-gQhnE_E3y`/~no;o_j={}z_nwo_sɓσ_= Qj? `ؽy(Wӥa#K?endstream endobj 314 0 obj 2215 endobj 383 0 obj <> endobj 385 0 obj <> stream xWkSHŎ!S,k$KHXcwe2L2< *B YU[dgw+L_ۭj^Si=UsM'}nr@Y東7X3xN\4 ޻q1ct2#e)m(dEzydB()CZN 7x.8747 q& :2ahn-bVVKߑw~] WUԥB4 }!LaS)̉/)! fTlj[/}u`E݀8>IhÝID2'Ě-$Y!y)I "l;H qz\t65F[\M(Y!9\BIcG` %< ܌vAucV&+_O;S͚qJjt6l&k19"Q<_]{Ъܘ46J1-[nGL}~4S5 udJ&ļ4Ć3m)|F?{3|/WA6be_Z N3R{#cԠs. wqu y`[W~,;%O VZ2׳Z˖ul]FxVs(L|+-hb@}E<%q*?Cys Ng>9]9#䄏ck[ؼ?K+!lUie>g&_0@4pQĩɄө/֎3ZM3 3xzEwOfxPFd8Y<ټ<6SYռs/ZZp I2; 8̺ď}(8+%~'sY [8agW,e\P!+!Jޕ7́x Xq9L]wdaU5x@ч=FJrdqbpƕTle2؝z=$ 05g9gHEVOysv9,4nWI{j1l'֍v&@puY}ڽ\t/(pəj40M GQE<{=Uԋ>ˬ\ETW!A$z `YᲅtQ \5OӆEnJlWˏmˑDܕc6:? ;-En7k{0$> Hݖ%lW/TT7TZG B*5spz ldV~DbqtQ/>G``>Yd8@vXS#oE k…~ޑ3[97w8'7h:ݭ=\"דߺ /_u:/$aOmun2pudLưnUnt0s +SI=u/4\dVO풔$Mzkz Z-[1TS薺Bom+cnA1ݰ!?*}hchDX;.`Tj 탏_0nE޸MU+5"#Y3 BH0 U] > endobj 5 0 obj <> /Contents 6 0 R >> endobj 178 0 obj <> /Contents 179 0 R >> endobj 249 0 obj <> /Contents 250 0 R >> endobj 283 0 obj <> /Contents 284 0 R >> endobj 312 0 obj <> /Contents 313 0 R >> endobj 384 0 obj <> /Contents 385 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 5 0 R 178 0 R 249 0 R 283 0 R 312 0 R 384 0 R ] /Count 6 >> endobj 1 0 obj <> endobj 4 0 obj <> endobj 14 0 obj <> endobj 16 0 obj <> stream 0 0 0 0 59 85 d1 59 0 0 85 0 0 cm BI /IM true /W 59 /H 85 /BPC 1 /F /CCF /DP <> ID 6W v\Appz.דPTZ4d3@Ŀ![p..,",26+@i0pl}t/5_ׇ] ^vu EO . g@π EI endstream endobj 17 0 obj <> stream 0 0 0 29 67 85 d1 67 0 0 56 0 29 cm BI /IM true /W 67 /H 56 /BPC 1 /F /CCF /DP <> ID &/ a AD~?P=5rp.D@ EI endstream endobj 18 0 obj <> stream 71 0 0 0 0 0 d1 endstream endobj 19 0 obj <> stream 0 0 0 29 106 83 d1 106 0 0 54 0 29 cm BI /IM true /W 106 /H 54 /BPC 1 /F /CCF /DP <> ID &M(Ђ?KtMvE`L41A_kY d00H  EI endstream endobj 20 0 obj <> stream 75 0 0 0 0 0 d1 endstream endobj 21 0 obj <> stream 113 0 0 0 0 0 d1 endstream endobj 22 0 obj <> stream 0 0 0 28 61 85 d1 61 0 0 57 0 28 cm BI /IM true /W 61 /H 57 /BPC 1 /F /CCF /DP <> ID &@i|A 0E8p }?a0~?w y2j2&x@'~v X0Y5 EI endstream endobj 23 0 obj <> stream 111 0 0 0 0 0 d1 endstream endobj 24 0 obj <> stream 0 0 0 29 48 83 d1 48 0 0 54 0 29 cm BI /IM true /W 48 /H 54 /BPC 1 /F /CCF /DP <> ID "At  Ԛh  EI endstream endobj 25 0 obj <> stream 65 0 0 0 0 0 d1 endstream endobj 26 0 obj <> stream 0 0 0 30 66 107 d1 66 0 0 77 0 30 cm BI /IM true /W 66 /H 77 /BPC 1 /F /CCF /DP <> ID &$xA<' rj{ uoX~_^}o Mרo+.   EI endstream endobj 27 0 obj <> stream 54 0 0 0 0 0 d1 endstream endobj 28 0 obj <> stream 0 0 0 0 65 69 d1 65 0 0 69 0 0 cm BI /IM true /W 65 /H 69 /BPC 1 /F /CCF /DP <> ID &?ek _ k_޽޻'dG EI endstream endobj 29 0 obj <> stream 0 0 0 -1 50 69 d1 50 0 0 70 0 -1 cm BI /IM true /W 50 /H 70 /BPC 1 /F /CCF /DP <> ID &Nis4_` ޼ɨ EI endstream endobj 30 0 obj <> stream 0 0 0 2 22 69 d1 22 0 0 67 0 2 cm BI /IM true /W 22 /H 67 /BPC 1 /F /CCF /DP <> ID jDkXQ=?0 EI endstream endobj 31 0 obj <> stream 55 0 0 0 0 0 d1 endstream endobj 32 0 obj <> stream 0 0 0 23 32 71 d1 32 0 0 48 0 23 cm BI /IM true /W 32 /H 48 /BPC 1 /F /CCF /DP <> ID 0!_ m8 7_ PU/BX%Z8PD3kkapboN<@ EI endstream endobj 33 0 obj <> stream 29 0 0 0 0 0 d1 endstream endobj 34 0 obj <> stream 28 0 0 0 0 0 d1 endstream endobj 35 0 obj <> stream 0 0 0 23 47 71 d1 47 0 0 48 0 23 cm BI /IM true /W 47 /H 48 /BPC 1 /F /CCF /DP <> ID &IϟFm<,zA矠c7\?~ |ʿ!?w__A K A~A EI endstream endobj 36 0 obj <> stream 72 0 0 0 0 0 d1 endstream endobj 37 0 obj <> stream 82 0 0 0 0 0 d1 endstream endobj 38 0 obj <> stream 39 0 0 0 0 0 d1 endstream endobj 39 0 obj <> stream 0 0 0 23 45 71 d1 45 0 0 48 0 23 cm BI /IM true /W 45 /H 48 /BPC 1 /F /CCF /DP <> ID &@13PzMAXz Ow&_K_ m.ް !~( EI endstream endobj 40 0 obj <> stream 0 0 0 24 34 69 d1 34 0 0 45 0 24 cm BI /IM true /W 34 /H 45 /BPC 1 /F /CCF /DP <> ID *8k |a>- `IC  EI endstream endobj 41 0 obj <> stream 50 0 0 0 0 0 d1 endstream endobj 42 0 obj <> stream 0 0 0 8 32 71 d1 32 0 0 63 0 8 cm BI /IM true /W 32 /H 63 /BPC 1 /F /CCF /DP <> ID &A|fo_?$$}  EI endstream endobj 43 0 obj <> stream 0 0 0 -1 49 71 d1 49 0 0 72 0 -1 cm BI /IM true /W 49 /H 72 /BPC 1 /F /CCF /DP <> ID &8c97>(|-| ZA~_ .a]x1^ _eҜ@ EI endstream endobj 44 0 obj <> stream 73 0 0 0 0 0 d1 endstream endobj 45 0 obj <> stream 0 0 0 23 39 71 d1 39 0 0 48 0 23 cm BI /IM true /W 39 /H 48 /BPC 1 /F /CCF /DP <> ID &8DFb0Iޯp%h j_ׇ_h,=a a_' EI endstream endobj 46 0 obj <> stream 45 0 0 0 0 0 d1 endstream endobj 47 0 obj <> stream 0 0 0 23 38 71 d1 38 0 0 48 0 23 cm BI /IM true /W 38 /H 48 /BPC 1 /F /CCF /DP <> ID &8joHxA7}&_}u!ɮ#=|0ݬ0N@ EI endstream endobj 48 0 obj <> stream 40 0 0 0 0 0 d1 endstream endobj 49 0 obj <> stream 43 0 0 0 0 0 d1 endstream endobj 50 0 obj <> stream 0 0 0 24 49 89 d1 49 0 0 65 0 24 cm BI /IM true /W 49 /H 65 /BPC 1 /F /CCF /DP <> ID /85".G? O}x_KvX xX1 MG  EI endstream endobj 51 0 obj <> stream 27 0 0 0 0 0 d1 endstream endobj 52 0 obj <> stream 0 0 0 24 50 69 d1 50 0 0 45 0 24 cm BI /IM true /W 50 /H 45 /BPC 1 /F /CCF /DP <> ID &Nis4-[ BQ@ EI endstream endobj 53 0 obj <> stream 86 0 0 0 0 0 d1 endstream endobj 54 0 obj <> stream 0 0 0 -2 34 69 d1 34 0 0 71 0 -2 cm BI /IM true /W 34 /H 71 /BPC 1 /F /CCF /DP <> ID &gfa> EI endstream endobj 55 0 obj <> stream 62 0 0 0 0 0 d1 endstream endobj 56 0 obj <> stream 78 0 0 0 0 0 d1 endstream endobj 57 0 obj <> stream 56 0 0 0 0 0 d1 endstream endobj 58 0 obj <> stream 49 0 0 0 0 0 d1 endstream endobj 59 0 obj <> stream 0 0 0 0 32 69 d1 32 0 0 69 0 0 cm BI /IM true /W 32 /H 69 /BPC 1 /F /CCF /DP <> ID jă @ EI endstream endobj 60 0 obj <> stream 63 0 0 0 0 0 d1 endstream endobj 61 0 obj <> stream 36 0 0 0 0 0 d1 endstream endobj 62 0 obj <> stream 0 0 0 26 67 71 d1 67 0 0 45 0 26 cm BI /IM true /W 67 /H 45 /BPC 1 /F /CCF /DP <> ID &g0?_AtդMx}PT'q:@ EI endstream endobj 63 0 obj <> stream 0 0 0 24 50 71 d1 50 0 0 47 0 24 cm BI /IM true /W 50 /H 47 /BPC 1 /F /CCF /DP <> ID &8cAΡ 9A,d_Qh EI endstream endobj 64 0 obj <> stream 0 0 0 26 48 90 d1 48 0 0 64 0 26 cm BI /IM true /W 48 /H 64 /BPC 1 /F /CCF /DP <> ID &ftټ$Èwa~/0?M ߿_M%A@ EI endstream endobj 65 0 obj <> stream 53 0 0 0 0 0 d1 endstream endobj 66 0 obj <> stream 51 0 0 0 0 0 d1 endstream endobj 67 0 obj <> stream 0 0 0 58 13 89 d1 13 0 0 31 0 58 cm BI /IM true /W 13 /H 31 /BPC 1 /F /CCF /DP <> ID &~˾c  EI endstream endobj 68 0 obj <> stream 61 0 0 0 0 0 d1 endstream endobj 69 0 obj <> stream 41 0 0 0 0 0 d1 endstream endobj 70 0 obj <> stream 88 0 0 0 0 0 d1 endstream endobj 71 0 obj <> stream 0 0 0 -1 22 69 d1 22 0 0 70 0 -1 cm BI /IM true /W 22 /H 70 /BPC 1 /F /CCF /DP <> ID jDkɨ EI endstream endobj 72 0 obj <> stream 0 0 0 0 55 69 d1 55 0 0 69 0 0 cm BI /IM true /W 55 /H 69 /BPC 1 /F /CCF /DP <> ID 5v7Jo  EI endstream endobj 73 0 obj <> stream 89 0 0 0 0 0 d1 endstream endobj 74 0 obj <> stream 0 0 0 -3 67 69 d1 67 0 0 72 0 -3 cm BI /IM true /W 67 /H 72 /BPC 1 /F /CCF /DP <> ID %"zoo_߽z_=vv_=ǵk@ EI endstream endobj 75 0 obj <> stream 0 0 0 0 66 69 d1 66 0 0 69 0 0 cm BI /IM true /W 66 /H 69 /BPC 1 /F /CCF /DP <> ID %`\MD$7῿o~ׯ^_^L@ EI endstream endobj 76 0 obj <> stream 74 0 0 0 0 0 d1 endstream endobj 77 0 obj <> stream 0 0 0 -2 46 72 d1 46 0 0 74 0 -2 cm BI /IM true /W 46 /H 74 /BPC 1 /F /CCF /DP <> ID &`3>@l}?O¿ ?_,Ak !Arx. a. ^ D h/GP~D2㯯x_k#; ^' EI endstream endobj 78 0 obj <> stream 77 0 0 0 0 0 d1 endstream endobj 79 0 obj <> stream 0 0 0 0 59 69 d1 59 0 0 69 0 0 cm BI /IM true /W 59 /H 69 /BPC 1 /F /CCF /DP <> ID a5ֿ `|0~߿K]0ADT((.  EI endstream endobj 80 0 obj <> stream 60 0 0 0 0 0 d1 endstream endobj 81 0 obj <> stream 0 0 0 23 46 91 d1 46 0 0 68 0 23 cm BI /IM true /W 46 /H 68 /BPC 1 /F /CCF /DP <> ID &0@0|'M5L/2+Z 0z3_䏶?&  v׆a! EI endstream endobj 82 0 obj <> stream 0 0 0 -13 27 33 d1 27 0 0 46 0 -13 cm BI /IM true /W 27 /H 46 /BPC 1 /F /CCF /DP <> ID &Mʿ|< EI endstream endobj 83 0 obj <> stream 42 0 0 0 0 0 d1 endstream endobj 84 0 obj <> stream 0 0 0 58 12 69 d1 12 0 0 11 0 58 cm BI /IM true /W 12 /H 11 /BPC 1 /F /CCF /DP <> ID &ۂ!MPX0 EI endstream endobj 85 0 obj <> stream 47 0 0 0 0 0 d1 endstream endobj 86 0 obj <> stream 0 0 0 26 50 69 d1 50 0 0 43 0 26 cm BI /IM true /W 50 /H 43 /BPC 1 /F /CCF /DP <> ID #C(;m/]t0]Ktk=yOVoI׬<-d!1 EI endstream endobj 87 0 obj <> stream 91 0 0 0 0 0 d1 endstream endobj 88 0 obj <> stream 38 0 0 0 0 0 d1 endstream endobj 89 0 obj <> stream 52 0 0 0 0 0 d1 endstream endobj 90 0 obj <> stream 95 0 0 0 0 0 d1 endstream endobj 91 0 obj <> stream 48 0 0 0 0 0 d1 endstream endobj 92 0 obj <> stream 87 0 0 0 0 0 d1 endstream endobj 93 0 obj <> stream 81 0 0 0 0 0 d1 endstream endobj 94 0 obj <> stream 0 0 0 -1 13 30 d1 13 0 0 31 0 -1 cm BI /IM true /W 13 /H 31 /BPC 1 /F /CCF /DP <> ID &o1ɪ a@@ EI endstream endobj 95 0 obj <> stream 79 0 0 0 0 0 d1 endstream endobj 96 0 obj <> stream 64 0 0 0 0 0 d1 endstream endobj 97 0 obj <> stream 84 0 0 0 0 0 d1 endstream endobj 98 0 obj <> stream 0 0 0 24 78 69 d1 78 0 0 45 0 24 cm BI /IM true /W 78 /H 45 /BPC 1 /F /CCF /DP <> ID ȃ?jDA80PV /a.fo66L4Q / EI endstream endobj 99 0 obj <> stream 83 0 0 0 0 0 d1 endstream endobj 100 0 obj <> stream 85 0 0 0 0 0 d1 endstream endobj 101 0 obj <> stream 76 0 0 0 0 0 d1 endstream endobj 102 0 obj <> stream 114 0 0 0 0 0 d1 endstream endobj 103 0 obj <> stream 31 0 0 0 0 0 d1 endstream endobj 104 0 obj <> stream 68 0 0 0 0 0 d1 endstream endobj 105 0 obj <> stream 26 0 0 0 0 0 d1 endstream endobj 106 0 obj <> stream 0 0 0 -1 49 71 d1 49 0 0 72 0 -1 cm BI /IM true /W 49 /H 72 /BPC 1 /F /CCF /DP <> ID &+ao7//u .yi{^ɨ EI endstream endobj 107 0 obj <> stream 80 0 0 0 0 0 d1 endstream endobj 108 0 obj <> stream 58 0 0 0 0 0 d1 endstream endobj 109 0 obj <> stream 44 0 0 0 0 0 d1 endstream endobj 110 0 obj <> stream 93 0 0 0 0 0 d1 endstream endobj 111 0 obj <> stream 37 0 0 0 0 0 d1 endstream endobj 112 0 obj <> stream 92 0 0 0 0 0 d1 endstream endobj 113 0 obj <> stream 70 0 0 0 0 0 d1 endstream endobj 114 0 obj <> stream 46 0 0 0 0 0 d1 endstream endobj 115 0 obj <> stream 90 0 0 0 0 0 d1 endstream endobj 116 0 obj <> stream 0 0 0 -2 62 69 d1 62 0 0 71 0 -2 cm BI /IM true /W 62 /H 71 /BPC 1 /F /CCF /DP <> ID &"OFri$`&0# a0&<0. 8 EI endstream endobj 117 0 obj <> stream 0 0 0 -2 51 69 d1 51 0 0 71 0 -2 cm BI /IM true /W 51 /H 71 /BPC 1 /F /CCF /DP <> ID &/}QLG\?o  ` EI endstream endobj 118 0 obj <> stream 105 0 0 0 0 0 d1 endstream endobj 119 0 obj <> stream 57 0 0 0 0 0 d1 endstream endobj 120 0 obj <> stream 0 0 0 -6 24 93 d1 24 0 0 99 0 -6 cm BI /IM true /W 24 /H 99 /BPC 1 /F /CCF /DP <> ID &XZZ -p]~z/Z_ /Mw|= ߀ EI endstream endobj 121 0 obj <> stream 0 0 0 -1 13 30 d1 13 0 0 31 0 -1 cm BI /IM true /W 13 /H 31 /BPC 1 /F /CCF /DP <> ID &m1|{ EI endstream endobj 122 0 obj <> stream 0 0 0 -6 24 93 d1 24 0 0 99 0 -6 cm BI /IM true /W 24 /H 99 /BPC 1 /F /CCF /DP <> ID &h{{{>{ZX_^Akֽa.Z]iu EI endstream endobj 123 0 obj <> stream 24 0 0 0 0 0 d1 endstream endobj 124 0 obj <> stream 0 0 0 -1 30 83 d1 30 0 0 84 0 -1 cm BI /IM true /W 30 /H 84 /BPC 1 /F /CCF /DP <> ID &D\_A?_[ ( EI endstream endobj 125 0 obj <> stream 0 0 0 29 61 109 d1 61 0 0 80 0 29 cm BI /IM true /W 61 /H 80 /BPC 1 /F /CCF /DP <> ID &!<E8a7z_]d .-E>N ȡq{:@azMڗߧvv G)`h  EI endstream endobj 126 0 obj <> stream 0 0 0 29 67 83 d1 67 0 0 54 0 29 cm BI /IM true /W 67 /H 54 /BPC 1 /F /CCF /DP <> ID &"hA?(оd* EI endstream endobj 127 0 obj <> stream 0 0 0 0 31 83 d1 31 0 0 83 0 0 cm BI /IM true /W 31 /H 83 /BPC 1 /F /CCF /DP <> ID &D@@ EI endstream endobj 128 0 obj <> stream 66 0 0 0 0 0 d1 endstream endobj 129 0 obj <> stream 0 0 0 28 60 85 d1 60 0 0 57 0 28 cm BI /IM true /W 60 /H 57 /BPC 1 /F /CCF /DP <> ID & >@ x zxFC`=7 ɯx\5zz/kaK`ȺAF EI endstream endobj 130 0 obj <> stream 0 0 0 7 43 85 d1 43 0 0 78 0 7 cm BI /IM true /W 43 /H 78 /BPC 1 /F /CCF /DP <> ID &ܠ' <.}&޾!ā5>3? EI endstream endobj 131 0 obj <> stream 0 0 0 65 20 106 d1 20 0 0 41 0 65 cm BI /IM true /W 20 /H 41 /BPC 1 /F /CCF /DP <> ID &`=>~KCGԚXa@@ EI endstream endobj 132 0 obj <> stream 112 0 0 0 0 0 d1 endstream endobj 133 0 obj <> stream 0 0 0 1 43 83 d1 43 0 0 82 0 1 cm BI /IM true /W 43 /H 82 /BPC 1 /F /CCF /DP <> ID ?ā EI endstream endobj 134 0 obj <> stream 0 0 0 30 92 83 d1 92 0 0 53 0 30 cm BI /IM true /W 92 /H 53 /BPC 1 /F /CCF /DP <> ID &p3V  GO o>ao}[[Z__NZ[TFH  EI endstream endobj 135 0 obj <> stream 99 0 0 0 0 0 d1 endstream endobj 136 0 obj <> stream 0 0 0 28 43 85 d1 43 0 0 57 0 28 cm BI /IM true /W 43 /H 57 /BPC 1 /F /CCF /DP <> ID &R?K>/jAp/׮Ay $Z kPe8aAG_k.%  EI endstream endobj 137 0 obj <> stream 69 0 0 0 0 0 d1 endstream endobj 138 0 obj <> stream 0 0 0 0 67 83 d1 67 0 0 83 0 0 cm BI /IM true /W 67 /H 83 /BPC 1 /F /CCF /DP <> ID &"hAyh_ W`^?P?"  EI endstream endobj 139 0 obj <> stream 0 0 0 0 97 72 d1 97 0 0 72 0 0 cm BI /IM true /W 97 /H 72 /BPC 1 /F /CCF /DP <> ID &`no[\,?]oO,i_^_^0P?k]7OO0&_~o =8o EI endstream endobj 140 0 obj <> stream 102 0 0 0 0 0 d1 endstream endobj 141 0 obj <> stream 94 0 0 0 0 0 d1 endstream endobj 142 0 obj <> stream 35 0 0 0 0 0 d1 endstream endobj 143 0 obj <> stream 0 0 0 24 49 89 d1 49 0 0 65 0 24 cm BI /IM true /W 49 /H 65 /BPC 1 /F /CCF /DP <> ID &AsFcߠ~L}>}_5~_\0ha/z'  EI endstream endobj 144 0 obj <> stream 0 0 0 26 12 69 d1 12 0 0 43 0 26 cm BI /IM true /W 12 /H 43 /BPC 1 /F /CCF /DP <> ID &ۂ!MPX0˼qj EI endstream endobj 145 0 obj <> stream 0 0 0 0 67 69 d1 67 0 0 69 0 0 cm BI /IM true /W 67 /H 69 /BPC 1 /F /CCF /DP <> ID !Jjd0k{^_/BK~K_/ _/ _ KƟAA>  EI endstream endobj 146 0 obj <> stream 0 0 0 26 48 71 d1 48 0 0 45 0 26 cm BI /IM true /W 48 /H 45 /BPC 1 /F /CCF /DP <> ID &75}o߅XoOzi@ EI endstream endobj 147 0 obj <> stream 0 0 0 -1 49 69 d1 49 0 0 70 0 -1 cm BI /IM true /W 49 /H 70 /BPC 1 /F /CCF /DP <> ID R' axA}z}zzAz_v ῿o?~O  EI endstream endobj 148 0 obj <> stream 0 0 0 0 22 40 d1 22 0 0 40 0 0 cm BI /IM true /W 22 /H 40 /BPC 1 /F /CCF /DP <> ID fc>j\? EI endstream endobj 149 0 obj <> stream 0 0 0 30 38 69 d1 38 0 0 39 0 30 cm BI /IM true /W 38 /H 39 /BPC 1 /F /CCF /DP <> ID &8e{M~d?/AG^z ^+ad! EI endstream endobj 150 0 obj <> stream 0 0 0 18 26 69 d1 26 0 0 51 0 18 cm BI /IM true /W 26 /H 51 /BPC 1 /F /CCF /DP <> ID &y Np  EI endstream endobj 151 0 obj <> stream 0 0 0 12 42 67 d1 42 0 0 55 0 12 cm BI /IM true /W 42 /H 55 /BPC 1 /F /CCF /DP <> ID #jfi+^/?R@ EI endstream endobj 152 0 obj <> stream 33 0 0 0 0 0 d1 endstream endobj 153 0 obj <> stream 0 0 0 30 32 69 d1 32 0 0 39 0 30 cm BI /IM true /W 32 /H 39 /BPC 1 /F /CCF /DP <> ID &>ԡ }z /_1,7[kkf@ EI endstream endobj 154 0 obj <> stream 0 0 0 12 18 67 d1 18 0 0 55 0 12 cm BI /IM true /W 18 /H 55 /BPC 1 /F /CCF /DP <> ID c5.?.? EI endstream endobj 155 0 obj <> stream 0 0 0 31 67 67 d1 67 0 0 36 0 31 cm BI /IM true /W 67 /H 36 /BPC 1 /F /CCF /DP <> ID # ?ML0h4;M5kßA֌]LBL)5)1H EI endstream endobj 156 0 obj <> stream 0 0 0 30 37 69 d1 37 0 0 39 0 30 cm BI /IM true /W 37 /H 39 /BPC 1 /F /CCF /DP <> ID &<#A&Ao,>jko[ %0P EI endstream endobj 157 0 obj <> stream 0 0 0 10 29 67 d1 29 0 0 57 0 10 cm BI /IM true /W 29 /H 57 /BPC 1 /F /CCF /DP <> ID &J_Qy5!#o+@ EI endstream endobj 158 0 obj <> stream 0 0 0 32 58 69 d1 58 0 0 37 0 32 cm BI /IM true /W 58 /H 37 /BPC 1 /F /CCF /DP <> ID & 1444?w[ |'& ? kO ({?A?@ EI endstream endobj 159 0 obj <> stream 0 0 0 31 30 67 d1 30 0 0 36 0 31 cm BI /IM true /W 30 /H 36 /BPC 1 /F /CCF /DP <> ID "y53@^=}cԡ EI endstream endobj 160 0 obj <> stream 34 0 0 0 0 0 d1 endstream endobj 161 0 obj <> stream 0 0 0 31 42 67 d1 42 0 0 36 0 31 cm BI /IM true /W 42 /H 36 /BPC 1 /F /CCF /DP <> ID #jfik!¶0P EI endstream endobj 162 0 obj <> stream 0 0 0 31 38 85 d1 38 0 0 54 0 31 cm BI /IM true /W 38 /H 54 /BPC 1 /F /CCF /DP <> ID &`@@G&SO& 7!l-xZ 3]spmz aix`#$A EI endstream endobj 163 0 obj <> stream 0 0 0 57 11 83 d1 11 0 0 26 0 57 cm BI /IM true /W 11 /H 26 /BPC 1 /F /CCF /DP <> ID & =*=rj@@ EI endstream endobj 164 0 obj <> stream 0 0 0 30 28 69 d1 28 0 0 39 0 30 cm BI /IM true /W 28 /H 39 /BPC 1 /F /CCF /DP <> ID 02q}& nX(" DOLFkN@D1zɪk\6pa<@ EI endstream endobj 165 0 obj <> stream 0 0 0 31 42 83 d1 42 0 0 52 0 31 cm BI /IM true /W 42 /H 52 /BPC 1 /F /CCF /DP <> ID #GS4 _u Ѳ߿W^봿-D5[ MJ @ EI endstream endobj 166 0 obj <> stream 0 0 0 12 19 67 d1 19 0 0 55 0 12 cm BI /IM true /W 19 /H 55 /BPC 1 /F /CCF /DP <> ID ɩ` EI endstream endobj 167 0 obj <> stream 0 0 0 30 32 69 d1 32 0 0 39 0 30 cm BI /IM true /W 32 /H 39 /BPC 1 /F /CCF /DP <> ID &>ԡx,= oH?Oº/_& vOu!a0P EI endstream endobj 168 0 obj <> stream 0 0 0 31 42 69 d1 42 0 0 38 0 31 cm BI /IM true /W 42 /H 38 /BPC 1 /F /CCF /DP <> ID &|>7߄AV2?5)  EI endstream endobj 169 0 obj <> stream 0 0 0 12 42 69 d1 42 0 0 57 0 12 cm BI /IM true /W 42 /H 57 /BPC 1 /F /CCF /DP <> ID &>_! >H13: }/ ɪ]^\18ay Sr f@ EI endstream endobj 170 0 obj <> stream 0 0 0 12 52 69 d1 52 0 0 57 0 12 cm BI /IM true /W 52 /H 57 /BPC 1 /F /CCF /DP <> ID & |<a!oo}oxW_zo [Xak G!p` p EI endstream endobj 171 0 obj <> stream 0 0 0 13 61 69 d1 61 0 0 56 0 13 cm BI /IM true /W 61 /H 56 /BPC 1 /F /CCF /DP <> ID &`3~?߿ 7' }>׬?z8aN  EI endstream endobj 172 0 obj <> stream 0 0 0 12 37 69 d1 37 0 0 57 0 12 cm BI /IM true /W 37 /H 57 /BPC 1 /F /CCF /DP <> ID &Rpf#a8 \.Ad " XK @G `]1omv `Q EI endstream endobj 173 0 obj <> stream 67 0 0 0 0 0 d1 endstream endobj 174 0 obj <> stream 0 0 0 32 41 84 d1 41 0 0 52 0 32 cm BI /IM true /W 41 /H 52 /BPC 1 /F /CCF /DP <> ID &x ŷܚ@}?M  EI endstream endobj 175 0 obj <> stream 0 0 0 0 34 67 d1 34 0 0 67 0 0 cm BI /IM true /W 34 /H 67 /BPC 1 /F /CCF /DP <> ID &l/MB  EI endstream endobj 182 0 obj <> stream 0 0 0 1 70 83 d1 70 0 0 82 0 1 cm BI /IM true /W 70 /H 82 /BPC 1 /F /CCF /DP <> ID &w Q ?AQg@ EI endstream endobj 183 0 obj <> stream 118 0 0 0 0 0 d1 endstream endobj 184 0 obj <> stream 0 0 0 0 92 83 d1 92 0 0 83 0 0 cm BI /IM true /W 92 /H 83 /BPC 1 /F /CCF /DP <> ID &B̂Xua߽v Wz/kkp]]t .? kkkk / EI endstream endobj 185 0 obj <> stream 0 0 0 1 91 83 d1 91 0 0 82 0 1 cm BI /IM true /W 91 /H 82 /BPC 1 /F /CCF /DP <> ID &VM/4aA* \o?7_z_zz^ׄ` FVp\ @ EI endstream endobj 186 0 obj <> stream 0 0 0 1 81 83 d1 81 0 0 82 0 1 cm BI /IM true /W 81 /H 82 /BPC 1 /F /CCF /DP <> ID %PsP)BFUC|Geῇ_ }z ^AAx(Rhe( EI endstream endobj 187 0 obj <> stream 0 0 0 0 65 83 d1 65 0 0 83 0 0 cm BI /IM true /W 65 /H 83 /BPC 1 /F /CCF /DP <> ID eMD$}~/ //KK]\/?a??aD.  EI endstream endobj 188 0 obj <> stream 0 0 0 0 57 69 d1 57 0 0 69 0 0 cm BI /IM true /W 57 /H 69 /BPC 1 /F /CCF /DP <> ID #`cj!4"?N }tl# EI endstream endobj 189 0 obj <> stream 108 0 0 0 0 0 d1 endstream endobj 190 0 obj <> stream 0 0 0 44 27 50 d1 27 0 0 6 0 44 cm BI /IM true /W 27 /H 6 /BPC 1 /F /CCF /DP <> ID @ EI endstream endobj 191 0 obj <> stream 0 0 0 0 66 85 d1 66 0 0 85 0 0 cm BI /IM true /W 66 /H 85 /BPC 1 /F /CCF /DP <> ID &9@_ xA@P T2 tA^ᆻaw 'k"7#ra? @@ EI endstream endobj 192 0 obj <> stream 0 0 0 28 54 85 d1 54 0 0 57 0 28 cm BI /IM true /W 54 /H 57 /BPC 1 /F /CCF /DP <> ID & 0z#| 7}&5M?IC:_k.a-a`0  EI endstream endobj 193 0 obj <> stream 0 0 0 0 20 41 d1 20 0 0 41 0 0 cm BI /IM true /W 20 /H 41 /BPC 1 /F /CCF /DP <> ID &>Pz}^?]BAc&@ EI endstream endobj 194 0 obj <> stream 0 0 0 0 66 85 d1 66 0 0 85 0 0 cm BI /IM true /W 66 /H 85 /BPC 1 /F /CCF /DP <> ID &P DA'  7_~K0\y ?P EI endstream endobj 195 0 obj <> stream 145 0 0 0 0 0 d1 endstream endobj 196 0 obj <> stream 0 0 0 30 66 83 d1 66 0 0 53 0 30 cm BI /IM true /W 66 /H 53 /BPC 1 /F /CCF /DP <> ID *)(:iauim[ /[Xk KF~70o[7 ? EI endstream endobj 197 0 obj <> stream 0 0 0 0 21 41 d1 21 0 0 41 0 0 cm BI /IM true /W 21 /H 41 /BPC 1 /F /CCF /DP <> ID &`}/}RkXa@@ EI endstream endobj 198 0 obj <> stream 0 0 0 24 50 89 d1 50 0 0 65 0 24 cm BI /IM true /W 50 /H 65 /BPC 1 /F /CCF /DP <> ID 3i%|L}?-wo^5n~ /zئ!aA EI endstream endobj 199 0 obj <> stream 0 0 0 -1 22 71 d1 22 0 0 72 0 -1 cm BI /IM true /W 22 /H 72 /BPC 1 /F /CCF /DP <> ID &|'x#)wy5_~߿*C  EI endstream endobj 200 0 obj <> stream 0 0 0 24 48 71 d1 48 0 0 47 0 24 cm BI /IM true /W 48 /H 47 /BPC 1 /F /CCF /DP <> ID & 0@h(v͍~MV?o~`z}k~  EI endstream endobj 201 0 obj <> stream 0 0 0 24 44 90 d1 44 0 0 66 0 24 cm BI /IM true /W 44 /H 66 /BPC 1 /F /CCF /DP <> ID &i =~A1?;~ o?8ww~o[ P2 EI endstream endobj 202 0 obj <> stream 0 0 0 3 25 71 d1 25 0 0 68 0 3 cm BI /IM true /W 25 /H 68 /BPC 1 /F /CCF /DP <> ID & A>MV~X0_/@^@ EI endstream endobj 203 0 obj <> stream 0 0 0 24 50 71 d1 50 0 0 47 0 24 cm BI /IM true /W 50 /H 47 /BPC 1 /F /CCF /DP <> ID &l  G}aB߇6?;_> stream 30 0 0 0 0 0 d1 endstream endobj 205 0 obj <> stream 0 0 0 24 34 71 d1 34 0 0 47 0 24 cm BI /IM true /W 34 /H 47 /BPC 1 /F /CCF /DP <> ID &> z7s0&{ t]-xXK:tA/p0KXb`  EI endstream endobj 206 0 obj <> stream 96 0 0 0 0 0 d1 endstream endobj 207 0 obj <> stream 0 0 0 24 40 71 d1 40 0 0 47 0 24 cm BI /IM true /W 40 /H 47 /BPC 1 /F /CCF /DP <> ID & g:G=oH>վoX?]  ) EI endstream endobj 208 0 obj <> stream 0 0 0 -1 46 71 d1 46 0 0 72 0 -1 cm BI /IM true /W 46 /H 72 /BPC 1 /F /CCF /DP <> ID &<@}sz_5Ww ;]n5mp>CGxf EI endstream endobj 209 0 obj <> stream 0 0 0 24 36 71 d1 36 0 0 47 0 24 cm BI /IM true /W 36 /H 47 /BPC 1 /F /CCF /DP <> ID & jGHz})ʀ8 *<o߷ Xz) EI endstream endobj 210 0 obj <> stream 0 0 0 -13 31 33 d1 31 0 0 46 0 -13 cm BI /IM true /W 31 /H 46 /BPC 1 /F /CCF /DP <> ID {7v߷`=?bII^ml0K+€ EI endstream endobj 211 0 obj <> stream 0 0 0 24 40 71 d1 40 0 0 47 0 24 cm BI /IM true /W 40 /H 47 /BPC 1 /F /CCF /DP <> ID &l~6_dp?}pn~ OS @ EI endstream endobj 212 0 obj <> stream 0 0 0 6 30 71 d1 30 0 0 65 0 6 cm BI /IM true /W 30 /H 65 /BPC 1 /F /CCF /DP <> ID & |=0[߿;"y5_!g} EI endstream endobj 213 0 obj <> stream 0 0 0 0 61 69 d1 61 0 0 69 0 0 cm BI /IM true /W 61 /H 69 /BPC 1 /F /CCF /DP <> ID RH h?~_/K_ q' 0x]0D(P@ EI endstream endobj 214 0 obj <> stream 0 0 0 0 39 38 d1 39 0 0 38 0 0 cm BI /IM true /W 39 /H 38 /BPC 1 /F /CCF /DP <> ID &f?z k- EI endstream endobj 215 0 obj <> stream 0 0 0 -24 102 45 d1 102 0 0 69 0 -24 cm BI /IM true /W 102 /H 69 /BPC 1 /F /CCF /DP <> ID )`Ɵ jHk? 0AM?[ 7Io-O ? EI endstream endobj 216 0 obj <> stream 97 0 0 0 0 0 d1 endstream endobj 217 0 obj <> stream 0 0 0 0 51 45 d1 51 0 0 45 0 0 cm BI /IM true /W 51 /H 45 /BPC 1 /F /CCF /DP <> ID &*p@փZ:@¤QOk! E~~x@ح@ EI endstream endobj 218 0 obj <> stream 110 0 0 0 0 0 d1 endstream endobj 219 0 obj <> stream 0 0 0 0 38 45 d1 38 0 0 45 0 0 cm BI /IM true /W 38 /H 45 /BPC 1 /F /CCF /DP <> ID 8! 8fP=<+o)5 2&@%XPR4) 5k] 8dw@ EI endstream endobj 220 0 obj <> stream 0 0 0 -19 36 45 d1 36 0 0 64 0 -19 cm BI /IM true /W 36 /H 64 /BPC 1 /F /CCF /DP <> ID &@O oP\I;jt EI endstream endobj 221 0 obj <> stream 0 0 0 0 45 45 d1 45 0 0 45 0 0 cm BI /IM true /W 45 /H 45 /BPC 1 /F /CCF /DP <> ID &A<,1z: D z' kK? 6&oa] b `@ EI endstream endobj 222 0 obj <> stream 0 0 0 0 42 45 d1 42 0 0 45 0 0 cm BI /IM true /W 42 /H 45 /BPC 1 /F /CCF /DP <> ID &ɨ`<~h?FbPg@ EI endstream endobj 223 0 obj <> stream 0 0 0 -24 60 45 d1 60 0 0 69 0 -24 cm BI /IM true /W 60 /H 69 /BPC 1 /F /CCF /DP <> ID &N @~i0? EI endstream endobj 224 0 obj <> stream 0 0 0 -25 52 47 d1 52 0 0 72 0 -25 cm BI /IM true /W 52 /H 72 /BPC 1 /F /CCF /DP <> ID &.t?@`| 4xO7 ` 3X.K, Z2dfWK^u_p!l{#K> 't@ EI endstream endobj 225 0 obj <> stream 0 0 0 0 55 65 d1 55 0 0 65 0 0 cm BI /IM true /W 55 /H 65 /BPC 1 /F /CCF /DP <> ID &h |8O}&>WOO?C oJ>o S) EI endstream endobj 226 0 obj <> stream 0 0 0 0 59 45 d1 59 0 0 45 0 0 cm BI /IM true /W 59 /H 45 /BPC 1 /F /CCF /DP <> ID )R@g$ :¶߰av+k MBA  EI endstream endobj 227 0 obj <> stream 59 0 0 0 0 0 d1 endstream endobj 228 0 obj <> stream 0 0 0 -25 59 45 d1 59 0 0 70 0 -25 cm BI /IM true /W 59 /H 70 /BPC 1 /F /CCF /DP <> ID )R@g$ $&a}/ ,@ EI endstream endobj 229 0 obj <> stream 0 0 0 -24 83 47 d1 83 0 0 71 0 -24 cm BI /IM true /W 83 /H 71 /BPC 1 /F /CCF /DP <> ID &CȰhzz5O&Nb? .>4k ?o~~ /PPR EI endstream endobj 230 0 obj <> stream 0 0 0 -25 75 47 d1 75 0 0 72 0 -25 cm BI /IM true /W 75 /H 72 /BPC 1 /F /CCF /DP <> ID &j ߐ0O!K<"0 |} = oI& >[ _zak-K  xXb `* EI endstream endobj 231 0 obj <> stream 0 0 0 0 59 45 d1 59 0 0 45 0 0 cm BI /IM true /W 59 /H 45 /BPC 1 /F /CCF /DP <> ID &\.A:< Wc!ɨI  EI endstream endobj 232 0 obj <> stream 0 0 0 0 71 72 d1 71 0 0 72 0 0 cm BI /IM true /W 71 /H 72 /BPC 1 /F /CCF /DP <> ID &j!OC@xDp|8@z o 7A}XzO[P_ k?`\ d|1' \ \@ EI endstream endobj 233 0 obj <> stream 0 0 0 25 50 70 d1 50 0 0 45 0 25 cm BI /IM true /W 50 /H 45 /BPC 1 /F /CCF /DP <> ID &Ap +& }o5kAm a[ !3 EI endstream endobj 234 0 obj <> stream 0 0 0 0 43 45 d1 43 0 0 45 0 0 cm BI /IM true /W 43 /H 45 /BPC 1 /F /CCF /DP <> ID &A <,'u> ҽoOP_!k_?א ~o l,΁|@ EI endstream endobj 235 0 obj <> stream 0 0 0 -26 26 45 d1 26 0 0 71 0 -26 cm BI /IM true /W 26 /H 71 /BPC 1 /F /CCF /DP <> ID 5_ ?B'` @ EI endstream endobj 236 0 obj <> stream 100 0 0 0 0 0 d1 endstream endobj 237 0 obj <> stream 0 0 0 -25 27 45 d1 27 0 0 70 0 -25 cm BI /IM true /W 27 /H 70 /BPC 1 /F /CCF /DP <> ID 5?X5  EI endstream endobj 238 0 obj <> stream 0 0 0 24 36 71 d1 36 0 0 47 0 24 cm BI /IM true /W 36 /H 47 /BPC 1 /F /CCF /DP <> ID & j|a >޷ ۿol ް,|@ EI endstream endobj 239 0 obj <> stream 0 0 0 24 42 71 d1 42 0 0 47 0 24 cm BI /IM true /W 42 /H 47 /BPC 1 /F /CCF /DP <> ID &> stream 0 0 0 0 28 40 d1 28 0 0 40 0 0 cm BI /IM true /W 28 /H 40 /BPC 1 /F /CCF /DP <> ID &0 0;aaoÚ:}7 { o[ %f@ EI endstream endobj 241 0 obj <> stream 0 0 0 13 25 67 d1 25 0 0 54 0 13 cm BI /IM true /W 25 /H 54 /BPC 1 /F /CCF /DP <> ID e7@ EI endstream endobj 242 0 obj <> stream 0 0 0 12 42 69 d1 42 0 0 57 0 12 cm BI /IM true /W 42 /H 57 /BPC 1 /F /CCF /DP <> ID &Ac#]d > ~ip^/ ԰ EI endstream endobj 243 0 obj <> stream 0 0 0 7 20 88 d1 20 0 0 81 0 7 cm BI /IM true /W 20 /H 81 /BPC 1 /F /CCF /DP <> ID &ƾZi~]x_^_MW?=n EI endstream endobj 244 0 obj <> stream 0 0 0 7 20 88 d1 20 0 0 81 0 7 cm BI /IM true /W 20 /H 81 /BPC 1 /F /CCF /DP <> ID &/a߾__K~ZzAikP EI endstream endobj 245 0 obj <> stream 0 0 0 10 52 67 d1 52 0 0 57 0 10 cm BI /IM true /W 52 /H 57 /BPC 1 /F /CCF /DP <> ID &d@%BM.|1R?A;6ba@ EI endstream endobj 246 0 obj <> stream 0 0 0 57 10 67 d1 10 0 0 10 0 57 cm BI /IM true /W 10 /H 10 /BPC 1 /F /CCF /DP <> ID &Zq~MR EI endstream endobj 247 0 obj <> stream 0 0 0 0 41 67 d1 41 0 0 67 0 0 cm BI /IM true /W 41 /H 67 /BPC 1 /F /CCF /DP <> ID &=Qa{7߶>{7.@ vvK0\ !@ EI endstream endobj 253 0 obj <> stream 0 0 0 0 64 72 d1 64 0 0 72 0 0 cm BI /IM true /W 64 /H 72 /BPC 1 /F /CCF /DP <> ID &h!F x]x~~wۯoc  EI endstream endobj 254 0 obj <> stream 0 0 0 0 67 72 d1 67 0 0 72 0 0 cm BI /IM true /W 67 /H 72 /BPC 1 /F /CCF /DP <> ID &PRU<"qxAh&ҽ~xW?޿W8 7 EI endstream endobj 255 0 obj <> stream 106 0 0 0 0 0 d1 endstream endobj 256 0 obj <> stream 0 0 0 24 75 71 d1 75 0 0 47 0 24 cm BI /IM true /W 75 /H 47 /BPC 1 /F /CCF /DP <> ID &l6L r>w}? a~͏UaßO44!A EI endstream endobj 257 0 obj <> stream 0 0 0 24 43 90 d1 43 0 0 66 0 24 cm BI /IM true /W 43 /H 66 /BPC 1 /F /CCF /DP <> ID &3 G@/}xX}06 }?l~z{ @ EI endstream endobj 258 0 obj <> stream 0 0 0 -1 53 66 d1 53 0 0 67 0 -1 cm BI /IM true /W 53 /H 67 /BPC 1 /F /CCF /DP <> ID &HSC @hzx?d ʶ+|5h. c5 0E=A7OFz-:d!d A EI endstream endobj 259 0 obj <> stream 0 0 0 0 57 70 d1 57 0 0 70 0 0 cm BI /IM true /W 57 /H 70 /BPC 1 /F /CCF /DP <> ID & \> CxD>kI~_l.  xeZd1A EI endstream endobj 260 0 obj <> stream 0 0 0 -2 60 72 d1 60 0 0 74 0 -2 cm BI /IM true /W 60 /H 74 /BPC 1 /F /CCF /DP <> ID &C D@c6$Xx A o &}'¿__@am[k0{Da8 EI endstream endobj 261 0 obj <> stream 25 0 0 0 0 0 d1 endstream endobj 262 0 obj <> stream 0 0 0 11 66 77 d1 66 0 0 66 0 11 cm BI /IM true /W 66 /H 66 /BPC 1 /F /CCF /DP <> ID &`~4?dsj  EI endstream endobj 263 0 obj <> stream 0 0 0 -6 39 38 d1 39 0 0 44 0 -6 cm BI /IM true /W 39 /H 44 /BPC 1 /F /CCF /DP <> ID &Ab3^{ / 7KoA`  |@ EI endstream endobj 264 0 obj <> stream 0 0 0 -6 39 95 d1 39 0 0 101 0 -6 cm BI /IM true /W 39 /H 101 /BPC 1 /F /CCF /DP <> ID &}?߿7~ ߿~߿? EI endstream endobj 265 0 obj <> stream 0 0 0 -1 71 89 d1 71 0 0 90 0 -1 cm BI /IM true /W 71 /H 90 /BPC 1 /F /CCF /DP <> ID &4zh?~C+cw~pM@@ EI endstream endobj 266 0 obj <> stream 0 0 0 -1 32 30 d1 32 0 0 31 0 -1 cm BI /IM true /W 32 /H 31 /BPC 1 /F /CCF /DP <> ID &l0m?_ ~~m  EI endstream endobj 267 0 obj <> stream 0 0 0 2 43 72 d1 43 0 0 70 0 2 cm BI /IM true /W 43 /H 70 /BPC 1 /F /CCF /DP <> ID &1j#3AzM&} L?[w__]kvz\]-4@ EI endstream endobj 268 0 obj <> stream 0 0 0 -1 32 30 d1 32 0 0 31 0 -1 cm BI /IM true /W 32 /H 31 /BPC 1 /F /CCF /DP <> ID &\o{ ~߻Kƿ_&k0A EI endstream endobj 269 0 obj <> stream 0 0 0 2 43 72 d1 43 0 0 70 0 2 cm BI /IM true /W 43 /H 70 /BPC 1 /F /CCF /DP <> ID &bB Fz/I ~ %렺,.Ap <|<0%[` ج0[  EI endstream endobj 270 0 obj <> stream 0 0 0 0 45 69 d1 45 0 0 69 0 0 cm BI /IM true /W 45 /H 69 /BPC 1 /F /CCF /DP <> ID & `57A|0a EI endstream endobj 271 0 obj <> stream 0 0 0 -13 31 35 d1 31 0 0 48 0 -13 cm BI /IM true /W 31 /H 48 /BPC 1 /F /CCF /DP <> ID & xG~wo]?Y^Py{͗0K!~8P EI endstream endobj 272 0 obj <> stream 0 0 0 0 78 69 d1 78 0 0 69 0 0 cm BI /IM true /W 78 /H 69 /BPC 1 /F /CCF /DP <> ID *aVA8p)3z7 ?oo qBPT3 EI endstream endobj 273 0 obj <> stream 0 0 0 0 92 45 d1 92 0 0 45 0 0 cm BI /IM true /W 92 /H 45 /BPC 1 /F /CCF /DP <> ID )?QIc;& oa~40ǃAaIHd0H?@ EI endstream endobj 274 0 obj <> stream 0 0 0 28 52 85 d1 52 0 0 57 0 28 cm BI /IM true /W 52 /H 57 /BPC 1 /F /CCF /DP <> ID & 0<"0G&x[>CLQ~> aiq0Y 0gP EI endstream endobj 275 0 obj <> stream 119 0 0 0 0 0 d1 endstream endobj 276 0 obj <> stream 0 0 0 -2 51 69 d1 51 0 0 71 0 -2 cm BI /IM true /W 51 /H 71 /BPC 1 /F /CCF /DP <> ID &/},Gf??O K ,t@@ EI endstream endobj 277 0 obj <> stream 0 0 0 0 57 65 d1 57 0 0 65 0 0 cm BI /IM true /W 57 /H 65 /BPC 1 /F /CCF /DP <> ID )&!2<>D  M>}ׯp (a( MBH EI endstream endobj 278 0 obj <> stream 0 0 0 0 30 42 d1 30 0 0 42 0 0 cm BI /IM true /W 30 /H 42 /BPC 1 /F /CCF /DP <> ID &48(zނa]vׅ dD0xoqzWX1_  EI endstream endobj 279 0 obj <> stream 0 0 0 32 10 67 d1 10 0 0 35 0 32 cm BI /IM true /W 10 /H 35 /BPC 1 /F /CCF /DP <> ID &Zq~MR?i EI endstream endobj 280 0 obj <> stream 0 0 0 32 41 69 d1 41 0 0 37 0 32 cm BI /IM true /W 41 /H 37 /BPC 1 /F /CCF /DP <> ID &g@O [ 7׫[0  EI endstream endobj 281 0 obj <> stream 0 0 0 32 41 67 d1 41 0 0 35 0 32 cm BI /IM true /W 41 /H 35 /BPC 1 /F /CCF /DP <> ID :MZe`z][~K 0[h-pњ}>z $>h@ EI endstream endobj 287 0 obj <> stream 0 0 0 -21 40 45 d1 40 0 0 66 0 -21 cm BI /IM true /W 40 /H 66 /BPC 1 /F /CCF /DP <> ID &?j\} EI endstream endobj 288 0 obj <> stream 0 0 0 0 16 45 d1 16 0 0 45 0 0 cm BI /IM true /W 16 /H 45 /BPC 1 /F /CCF /DP <> ID &} _/_?5K_ EI endstream endobj 289 0 obj <> stream 0 0 0 -21 46 45 d1 46 0 0 66 0 -21 cm BI /IM true /W 46 /H 66 /BPC 1 /F /CCF /DP <> ID &7 C9}rY}?a Xd ,X2  EI endstream endobj 290 0 obj <> stream 0 0 0 -21 48 47 d1 48 0 0 68 0 -21 cm BI /IM true /W 48 /H 68 /BPC 1 /F /CCF /DP <> ID & < xOGA_4 tӾaaZX\p\a{ywNXa/Kadž   EI endstream endobj 291 0 obj <> stream 98 0 0 0 0 0 d1 endstream endobj 292 0 obj <> stream 0 0 0 -14 33 33 d1 33 0 0 47 0 -14 cm BI /IM true /W 33 /H 47 /BPC 1 /F /CCF /DP <> ID &M7͆?߷??  EI endstream endobj 293 0 obj <> stream 0 0 0 -21 50 45 d1 50 0 0 66 0 -21 cm BI /IM true /W 50 /H 66 /BPC 1 /F /CCF /DP <> ID &346&~߿῿o~?}  EI endstream endobj 294 0 obj <> stream 0 0 0 -7 88 47 d1 88 0 0 54 0 -7 cm BI /IM true /W 88 /H 54 /BPC 1 /F /CCF /DP <> ID &-_az-ta--pBOkN{ﷷ EI endstream endobj 295 0 obj <> stream 0 0 0 17 88 71 d1 88 0 0 54 0 17 cm BI /IM true /W 88 /H 54 /BPC 1 /F /CCF /DP <> ID &p6{ϱ,]u Z  EI endstream endobj 296 0 obj <> stream 0 0 0 -2 47 90 d1 47 0 0 92 0 -2 cm BI /IM true /W 47 /H 92 /BPC 1 /F /CCF /DP <> ID &|4?8py5:N|^ǵ@ EI endstream endobj 297 0 obj <> stream 0 0 0 -21 46 47 d1 46 0 0 68 0 -21 cm BI /IM true /W 46 /H 68 /BPC 1 /F /CCF /DP <> ID &@g"01=$ &7Ç  \ \W^ qlʰc>|> x EI endstream endobj 298 0 obj <> stream 0 0 0 0 84 69 d1 84 0 0 69 0 0 cm BI /IM true /W 84 /H 69 /BPC 1 /F /CCF /DP <> ID !OɩCb0`i?OOoOMM?c!2\7? EI endstream endobj 299 0 obj <> stream 0 0 0 -2 66 72 d1 66 0 0 74 0 -2 cm BI /IM true /W 66 /H 74 /BPC 1 /F /CCF /DP <> ID &C B%0 <,< @oo }o}o [}o_p__Avia q  EI endstream endobj 300 0 obj <> stream 0 0 0 -21 48 47 d1 48 0 0 68 0 -21 cm BI /IM true /W 48 /H 68 /BPC 1 /F /CCF /DP <> ID & |a>J,=/Kip\|ꗆ p ?d?~l-cXa\xk a EI endstream endobj 301 0 obj <> stream 0 0 0 -24 51 47 d1 51 0 0 71 0 -24 cm BI /IM true /W 51 /H 71 /BPC 1 /F /CCF /DP <> ID &zz{V7~߷þ/}@jg_ EI endstream endobj 302 0 obj <> stream 0 0 0 -21 48 47 d1 48 0 0 68 0 -21 cm BI /IM true /W 48 /H 68 /BPC 1 /F /CCF /DP <> ID &=CA7}aޗ^k/` .XxXc_ւh-t>W|'௄|/]m- qaD@ EI endstream endobj 303 0 obj <> stream 0 0 0 0 81 82 d1 81 0 0 82 0 0 cm BI /IM true /W 81 /H 82 /BPC 1 /F /CCF /DP <> ID ; 55_5U?4g_ep?/O}/}@ EI endstream endobj 304 0 obj <> stream 0 0 0 29 66 106 d1 66 0 0 77 0 29 cm BI /IM true /W 66 /H 77 /BPC 1 /F /CCF /DP <> ID #AQDxA |C  >o ?_]iD%†B)5 !Q EI endstream endobj 305 0 obj <> stream 0 0 0 26 38 69 d1 38 0 0 43 0 26 cm BI /IM true /W 38 /H 43 /BPC 1 /F /CCF /DP <> ID _'2jþ۷ ?}<7Eaoÿm @ EI endstream endobj 306 0 obj <> stream 0 0 0 0 69 69 d1 69 0 0 69 0 0 cm BI /IM true /W 69 /H 69 /BPC 1 /F /CCF /DP <> ID aj! 2Ei_Kz__/A}.ׇ]k0^ׇo@K EI endstream endobj 307 0 obj <> stream 0 0 0 0 30 41 d1 30 0 0 41 0 0 cm BI /IM true /W 30 /H 41 /BPC 1 /F /CCF /DP <> ID &1= 7'ɯ EI endstream endobj 308 0 obj <> stream 0 0 0 14 56 68 d1 56 0 0 54 0 14 cm BI /IM true /W 56 /H 54 /BPC 1 /F /CCF /DP <> ID & AAjp> stream 0 0 0 0 43 55 d1 43 0 0 55 0 0 cm BI /IM true /W 43 /H 55 /BPC 1 /F /CCF /DP <> ID &ܠ$ >[(^/5K! EI endstream endobj 315 0 obj <> stream 0 0 0 12 48 55 d1 48 0 0 43 0 12 cm BI /IM true /W 48 /H 43 /BPC 1 /F /CCF /DP <> ID &" a@P # :'cG4'4GpAix_kj EI endstream endobj 316 0 obj <> stream 0 0 0 -8 38 55 d1 38 0 0 63 0 -8 cm BI /IM true /W 38 /H 63 /BPC 1 /F /CCF /DP <> ID &EjAy5^? EI endstream endobj 317 0 obj <> stream 0 0 0 -6 42 55 d1 42 0 0 61 0 -6 cm BI /IM true /W 42 /H 61 /BPC 1 /F /CCF /DP <> ID &kP EI endstream endobj 318 0 obj <> stream 0 0 0 21 41 29 d1 41 0 0 8 0 21 cm BI /IM true /W 41 /H 8 /BPC 1 /F /CCF /DP <> ID &j  EI endstream endobj 319 0 obj <> stream 104 0 0 0 0 0 d1 endstream endobj 320 0 obj <> stream 0 0 0 -6 41 55 d1 41 0 0 61 0 -6 cm BI /IM true /W 41 /H 61 /BPC 1 /F /CCF /DP <> ID &*&bDc  EI endstream endobj 321 0 obj <> stream 0 0 0 -6 48 55 d1 48 0 0 61 0 -6 cm BI /IM true /W 48 /H 61 /BPC 1 /F /CCF /DP <> ID &(ɪ@^?A3Oa׏?T EI endstream endobj 322 0 obj <> stream 103 0 0 0 0 0 d1 endstream endobj 323 0 obj <> stream 0 0 0 12 48 55 d1 48 0 0 43 0 12 cm BI /IM true /W 48 /H 43 /BPC 1 /F /CCF /DP <> ID &.8|."x31.K/mp~O?~a_Oa?  EI endstream endobj 324 0 obj <> stream 0 0 0 12 55 55 d1 55 0 0 43 0 12 cm BI /IM true /W 55 /H 43 /BPC 1 /F /CCF /DP <> ID &];#g/Ça0g.HG[Mt @@ EI endstream endobj 325 0 obj <> stream 0 0 0 12 47 77 d1 47 0 0 65 0 12 cm BI /IM true /W 47 /H 65 /BPC 1 /F /CCF /DP <> ID &j?$7 ?}? /\? (:&O EI endstream endobj 326 0 obj <> stream 0 0 0 12 43 55 d1 43 0 0 43 0 12 cm BI /IM true /W 43 /H 43 /BPC 1 /F /CCF /DP <> ID &C0" 5Z φ_uXa~ EI endstream endobj 327 0 obj <> stream 0 0 0 -8 43 57 d1 43 0 0 65 0 -8 cm BI /IM true /W 43 /H 65 /BPC 1 /F /CCF /DP <> ID &@OO}N' o`.C`̂UxK\-  Q@fׅZ/$x^24{O EI endstream endobj 328 0 obj <> stream 0 0 0 12 40 55 d1 40 0 0 43 0 12 cm BI /IM true /W 40 /H 43 /BPC 1 /F /CCF /DP <> ID & ,7p[O5QɪCdp 'c A EI endstream endobj 329 0 obj <> stream 0 0 0 12 46 55 d1 46 0 0 43 0 12 cm BI /IM true /W 46 /H 43 /BPC 1 /F /CCF /DP <> ID & MRyia?h(cX5&  EI endstream endobj 330 0 obj <> stream 0 0 0 -15 8 64 d1 8 0 0 79 0 -15 cm BI /IM true /W 8 /H 79 /BPC 1 /F /CCF /DP <> ID &P EI endstream endobj 331 0 obj <> stream 120 0 0 0 0 0 d1 endstream endobj 332 0 obj <> stream 0 0 0 12 50 55 d1 50 0 0 43 0 12 cm BI /IM true /W 50 /H 43 /BPC 1 /F /CCF /DP <> ID &k.% ;k EI endstream endobj 333 0 obj <> stream 0 0 0 12 50 55 d1 50 0 0 43 0 12 cm BI /IM true /W 50 /H 43 /BPC 1 /F /CCF /DP <> ID &@A'CuuDHMc_T EI endstream endobj 334 0 obj <> stream 0 0 0 12 39 55 d1 39 0 0 43 0 12 cm BI /IM true /W 39 /H 43 /BPC 1 /F /CCF /DP <> ID &ا@"\Q>u Az| ֤%d  fBa," :q4 .ɪ|>B]@ EI endstream endobj 335 0 obj <> stream 0 0 0 12 48 77 d1 48 0 0 65 0 12 cm BI /IM true /W 48 /H 65 /BPC 1 /F /CCF /DP <> ID &fTCl> ?A{_7~}|% "_rj( EI endstream endobj 336 0 obj <> stream 0 0 0 12 50 55 d1 50 0 0 43 0 12 cm BI /IM true /W 50 /H 43 /BPC 1 /F /CCF /DP <> ID &%TYd\xRj2p`@ EI endstream endobj 337 0 obj <> stream 0 0 0 -6 50 55 d1 50 0 0 61 0 -6 cm BI /IM true /W 50 /H 61 /BPC 1 /F /CCF /DP <> ID &%TYd^+^d?( EI endstream endobj 338 0 obj <> stream 0 0 0 -6 48 55 d1 48 0 0 61 0 -6 cm BI /IM true /W 48 /H 61 /BPC 1 /F /CCF /DP <> ID &jad/'-L'8}&@ EI endstream endobj 339 0 obj <> stream 101 0 0 0 0 0 d1 endstream endobj 340 0 obj <> stream 0 0 0 12 43 55 d1 43 0 0 43 0 12 cm BI /IM true /W 43 /H 43 /BPC 1 /F /CCF /DP <> ID & 1<x@E z>o}oOiv0K %_ P EI endstream endobj 341 0 obj <> stream 0 0 0 -6 48 55 d1 48 0 0 61 0 -6 cm BI /IM true /W 48 /H 61 /BPC 1 /F /CCF /DP <> ID &qV4=a %O|'?_&av׵0\1% / _ڀ EI endstream endobj 342 0 obj <> stream 0 0 0 -6 46 55 d1 46 0 0 61 0 -6 cm BI /IM true /W 46 /H 61 /BPC 1 /F /CCF /DP <> ID &㾤r9A0OT EI endstream endobj 343 0 obj <> stream 0 0 0 2 26 90 d1 26 0 0 88 0 2 cm BI /IM true /W 26 /H 88 /BPC 1 /F /CCF /DP <> ID &4 0d.d%_'2@k @ EI endstream endobj 344 0 obj <> stream 0 0 0 -13 31 35 d1 31 0 0 48 0 -13 cm BI /IM true /W 31 /H 48 /BPC 1 /F /CCF /DP <> ID & #G@nˊ]\|Ƽ3@N >|#0 EI endstream endobj 345 0 obj <> stream 0 0 0 0 68 69 d1 68 0 0 69 0 0 cm BI /IM true /W 68 /H 69 /BPC 1 /F /CCF /DP <> ID &j>@ƞo?Ck|'w__   EI endstream endobj 346 0 obj <> stream 0 0 0 24 55 69 d1 55 0 0 45 0 24 cm BI /IM true /W 55 /H 45 /BPC 1 /F /CCF /DP <> ID "|3t^_Xm-׆p+k h.OI}}>_  @ EI endstream endobj 347 0 obj <> stream 0 0 0 -25 79 45 d1 79 0 0 70 0 -25 cm BI /IM true /W 79 /H 70 /BPC 1 /F /CCF /DP <> ID $?ɨC@5]k___>k__v]z޸a EI endstream endobj 348 0 obj <> stream 0 0 0 0 78 45 d1 78 0 0 45 0 0 cm BI /IM true /W 78 /H 45 /BPC 1 /F /CCF /DP <> ID &8 AAD57}0-mX}X{N׾Xq#" !N@ EI endstream endobj 349 0 obj <> stream 0 0 0 -3 12 69 d1 12 0 0 72 0 -3 cm BI /IM true /W 12 /H 72 /BPC 1 /F /CCF /DP <> ID &ۂ!MPX0˿?'MPX0 EI endstream endobj 350 0 obj <> stream 0 0 0 57 41 65 d1 41 0 0 8 0 57 cm BI /IM true /W 41 /H 8 /BPC 1 /F /CCF /DP <> ID &j  EI endstream endobj 351 0 obj <> stream 0 0 0 42 14 55 d1 14 0 0 13 0 42 cm BI /IM true /W 14 /H 13 /BPC 1 /F /CCF /DP <> ID &_- EI endstream endobj 352 0 obj <> stream 116 0 0 0 0 0 d1 endstream endobj 353 0 obj <> stream 0 0 0 -15 41 64 d1 41 0 0 79 0 -15 cm BI /IM true /W 41 /H 79 /BPC 1 /F /CCF /DP <> ID &ү~߼?~ ?߽0~oP EI endstream endobj 354 0 obj <> stream 0 0 0 -15 41 64 d1 41 0 0 79 0 -15 cm BI /IM true /W 41 /H 79 /BPC 1 /F /CCF /DP <> ID &kAZZk___ _ֽk~,/Z/  EI endstream endobj 355 0 obj <> stream 0 0 0 0 37 61 d1 37 0 0 61 0 0 cm BI /IM true /W 37 /H 61 /BPC 1 /F /CCF /DP <> ID &EjC)ɪP EI endstream endobj 356 0 obj <> stream 0 0 0 -8 35 55 d1 35 0 0 63 0 -8 cm BI /IM true /W 35 /H 63 /BPC 1 /F /CCF /DP <> ID &> stream 0 0 0 0 41 51 d1 41 0 0 51 0 0 cm BI /IM true /W 41 /H 51 /BPC 1 /F /CCF /DP <> ID & a,.Aa-p@XAa=0a>`=`j  EI endstream endobj 358 0 obj <> stream 0 0 0 -6 45 55 d1 45 0 0 61 0 -6 cm BI /IM true /W 45 /H 61 /BPC 1 /F /CCF /DP <> ID &ȾAG& EI endstream endobj 359 0 obj <> stream 0 0 0 -8 43 55 d1 43 0 0 63 0 -8 cm BI /IM true /W 43 /H 63 /BPC 1 /F /CCF /DP <> ID &߃l5>{<7|7{ᇿ6_w. k ,5 !|@ EI endstream endobj 360 0 obj <> stream 0 0 0 -6 50 57 d1 50 0 0 63 0 -6 cm BI /IM true /W 50 /H 63 /BPC 1 /F /CCF /DP <> ID &|RpAd)I||/7 /I$@ EI endstream endobj 361 0 obj <> stream 0 0 0 0 41 65 d1 41 0 0 65 0 0 cm BI /IM true /W 41 /H 65 /BPC 1 /F /CCF /DP <> ID &p" ?0htOXad: EI endstream endobj 362 0 obj <> stream 0 0 0 0 41 51 d1 41 0 0 51 0 0 cm BI /IM true /W 41 /H 51 /BPC 1 /F /CCF /DP <> ID &|0ᇰ<,-,. KX\% uP EI endstream endobj 363 0 obj <> stream 0 0 0 12 50 77 d1 50 0 0 65 0 12 cm BI /IM true /W 50 /H 65 /BPC 1 /F /CCF /DP <> ID &!??H.IנMz^kc ^ P EI endstream endobj 364 0 obj <> stream 0 0 0 -6 45 55 d1 45 0 0 61 0 -6 cm BI /IM true /W 45 /H 61 /BPC 1 /F /CCF /DP <> ID &pO!&D{dIw @@ EI endstream endobj 365 0 obj <> stream 0 0 0 -8 43 57 d1 43 0 0 65 0 -8 cm BI /IM true /W 43 /H 65 /BPC 1 /F /CCF /DP <> ID &1'. 7M}&OM /zw_ 4Ka @ EI endstream endobj 366 0 obj <> stream 0 0 0 -8 43 57 d1 43 0 0 65 0 -8 cm BI /IM true /W 43 /H 65 /BPC 1 /F /CCF /DP <> ID & 1o00Apax5џc\x}kN@ EI endstream endobj 367 0 obj <> stream 0 0 0 -6 47 55 d1 47 0 0 61 0 -6 cm BI /IM true /W 47 /H 61 /BPC 1 /F /CCF /DP <> ID & 1 uAz}. }sY\/ 5J  EI endstream endobj 368 0 obj <> stream 0 0 0 -6 48 55 d1 48 0 0 61 0 -6 cm BI /IM true /W 48 /H 61 /BPC 1 /F /CCF /DP <> ID &Q &&D~^Aviq o~_?( EI endstream endobj 369 0 obj <> stream 0 0 0 -6 43 57 d1 43 0 0 63 0 -6 cm BI /IM true /W 43 /H 63 /BPC 1 /F /CCF /DP <> ID &c( =>Cp釯oI~\>]~0^!u ax&x EI endstream endobj 370 0 obj <> stream 0 0 0 0 46 61 d1 46 0 0 61 0 0 cm BI /IM true /W 46 /H 61 /BPC 1 /F /CCF /DP <> ID &8w&G_^_x_AzxK(V: MPP EI endstream endobj 371 0 obj <> stream 0 0 0 24 57 89 d1 57 0 0 65 0 24 cm BI /IM true /W 57 /H 65 /BPC 1 /F /CCF /DP <> ID &!G#:ԇ O}}&m^_ 0 EI endstream endobj 372 0 obj <> stream 0 0 0 10 49 78 d1 49 0 0 68 0 10 cm BI /IM true /W 49 /H 68 /BPC 1 /F /CCF /DP <> ID &0@;a@L:Y5X]`.+k ɀft,> {0IH=0T ,@5 EI endstream endobj 373 0 obj <> stream 0 0 0 -8 43 57 d1 43 0 0 65 0 -8 cm BI /IM true /W 43 /H 65 /BPC 1 /F /CCF /DP <> ID &c(= Np7.?륮xY .(!~x}>^Ar=5, EI endstream endobj 374 0 obj <> stream 0 0 0 -6 45 55 d1 45 0 0 61 0 -6 cm BI /IM true /W 45 /H 61 /BPC 1 /F /CCF /DP <> ID &T9 Bo0p߃~zׅ(T4  EI endstream endobj 375 0 obj <> stream 0 0 0 0 47 65 d1 47 0 0 65 0 0 cm BI /IM true /W 47 /H 65 /BPC 1 /F /CCF /DP <> ID &Ah dA~(.}}A?ƃ5C+~k]axF.| EI endstream endobj 376 0 obj <> stream 0 0 0 0 47 61 d1 47 0 0 61 0 0 cm BI /IM true /W 47 /H 61 /BPC 1 /F /CCF /DP <> ID &dXA.o/__ }0~~)Rjp EI endstream endobj 377 0 obj <> stream 0 0 0 -25 58 45 d1 58 0 0 70 0 -25 cm BI /IM true /W 58 /H 70 /BPC 1 /F /CCF /DP <> ID )ɨa?4O >qXbC`Oe EI endstream endobj 378 0 obj <> stream 0 0 0 0 69 72 d1 69 0 0 72 0 0 cm BI /IM true /W 69 /H 72 /BPC 1 /F /CCF /DP <> ID &phP@@BzOɨP~x^+@73o__ z }b!ABpi@ EI endstream endobj 379 0 obj <> stream 0 0 0 0 28 42 d1 28 0 0 42 0 0 cm BI /IM true /W 28 /H 42 /BPC 1 /F /CCF /DP <> ID &@G=a?Wo~\/ka/ S  EI endstream endobj 380 0 obj <> stream 0 0 0 13 51 67 d1 51 0 0 54 0 13 cm BI /IM true /W 51 /H 54 /BPC 1 /F /CCF /DP <> ID "@S@`_/_K |߆/ j"H8 EI endstream endobj 381 0 obj <> stream 0 0 0 46 22 52 d1 22 0 0 6 0 46 cm BI /IM true /W 22 /H 6 /BPC 1 /F /CCF /DP <> ID @ EI endstream endobj 382 0 obj <> stream 0 0 0 0 41 70 d1 41 0 0 70 0 0 cm BI /IM true /W 41 /H 70 /BPC 1 /F /CCF /DP <> ID &|W@ ?WCaq]K^a/ / W<@ EI endstream endobj 388 0 obj <> stream 0 0 0 -6 48 55 d1 48 0 0 61 0 -6 cm BI /IM true /W 48 /H 61 /BPC 1 /F /CCF /DP <> ID &>(~MR^8KK___UF|€ EI endstream endobj 389 0 obj <> stream 0 0 0 0 45 65 d1 45 0 0 65 0 0 cm BI /IM true /W 45 /H 65 /BPC 1 /F /CCF /DP <> ID &OOA>x_ ] k?| џӿ ~}[ .;VkO! EI endstream endobj 390 0 obj <> stream 0 0 0 0 73 82 d1 73 0 0 82 0 0 cm BI /IM true /W 73 /H 82 /BPC 1 /F /CCF /DP <> ID &o%__|)~}}( EI endstream endobj 391 0 obj <> stream 0 0 0 -2 43 70 d1 43 0 0 72 0 -2 cm BI /IM true /W 43 /H 72 /BPC 1 /F /CCF /DP <> ID &ova@ EI endstream endobj 392 0 obj <> stream 0 0 0 0 67 69 d1 67 0 0 69 0 0 cm BI /IM true /W 67 /H 69 /BPC 1 /F /CCF /DP <> ID bB@!i' h8 @ EI endstream endobj 393 0 obj <> stream 0 0 0 -2 68 72 d1 68 0 0 74 0 -2 cm BI /IM true /W 68 /H 74 /BPC 1 /F /CCF /DP <> ID &H ? "@ k/I^ _/?!___dck5k>xA`Iy   EI endstream endobj 394 0 obj <> stream 0 0 0 30 66 83 d1 66 0 0 53 0 30 cm BI /IM true /W 66 /H 53 /BPC 1 /F /CCF /DP <> ID &x)?Η[&} }azҸ EI endstream endobj 395 0 obj <> stream 0 0 0 0 69 72 d1 69 0 0 72 0 0 cm BI /IM true /W 69 /H 72 /BPC 1 /F /CCF /DP <> ID &l?|}oa~  [A·oa}B~D8Hn@ EI endstream endobj 396 0 obj <> stream 0 0 0 0 82 85 d1 82 0 0 85 0 0 cm BI /IM true /W 82 /H 85 /BPC 1 /F /CCF /DP <> ID &k jI| )<- ޓ| IW¾s\(xd;k [k~07*~<x0O5y m\@ EI endstream endobj 397 0 obj <> stream 0 0 0 0 43 70 d1 43 0 0 70 0 0 cm BI /IM true /W 43 /H 70 /BPC 1 /F /CCF /DP <> ID &@/!~xF oA}>}&\a_]-4mvAa _XKOA0O7_M~Kl/],X3@`@ EI endstream endobj 398 0 obj <> stream 0 0 0 0 43 70 d1 43 0 0 70 0 0 cm BI /IM true /W 43 /H 70 /BPC 1 /F /CCF /DP <> ID &8/4=oAo?}߯׫V+k(A)5{hʹ a0\0  EI endstream endobj 387 0 obj <>/FontBBox[0 -30 82 85]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 17/Widths[ 0 100 101 0 0 0 52 0 70 0 77 0 37 0 0 0 50 0] >> endobj 310 0 obj <>/FontBBox[0 -57 79 95]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 103/Widths[ 0 0 55 0 57 0 49 0 104 0 50 0 103 0 52 0 47 0 56 0 51 0 53 0 0 48 59 45 54 0 120 90 0 46 0 0 58 0 0 0 0 101 0 0 0 0 71 0 0 0 75 0 0 0 0 0 116 0 39 41 102 0 98 0 0 61 0 97 0 0 0 0 60 96 0 0 0 0 67 0 38 0 0 68 0 0 105 0 0 0 40 0 0 108 106 0 0 82 0 78 0 0 0 0] >> endobj 286 0 obj <>/FontBBox[0 -29 88 95]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 37/Widths[ 0 102 0 58 0 99 61 0 98 62 0 38 0 97 63 0 82 0 79 64 0 0 0 77 0 0 0 0 0 0 112 0 53 0 76 0 0 41] >> endobj 252 0 obj <>/FontBBox[0 -32 92 107]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 49/Widths[ 0 0 0 106 0 54 81 0 82 0 64 0 61 0 25 0 86 0 37 0 51 0 84 0 88 42 0 0 50 83 41 0 0 52 0 0 0 63 97 0 119 0 0 44 45 0 0 0 36 0] >> endobj 181 0 obj <>/FontBBox[0 -57 102 97]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 105/Widths[ 0 118 0 80 0 102 0 70 83 0 73 0 108 0 38 24 0 0 0 111 33 0 145 0 63 0 77 112 0 84 0 58 0 26 0 50 0 49 0 30 0 56 96 0 0 46 0 43 0 45 69 0 48 0 42 0 79 0 0 97 0 110 0 55 0 44 0 0 52 0 0 0 60 0 61 59 0 0 68 0 0 0 0 81 64 0 0 100 0 53 0 86 0 40 0 0 0 0 65 0 39 0 75 0 35 0] >> endobj 15 0 obj <>/FontBBox[0 -65 106 105]/FontMatrix[1 0 0 1 0 0]/FirstChar 0/LastChar 159/Widths[ 0 0 71 0 75 113 0 111 0 65 0 54 0 0 0 55 0 29 28 0 72 82 39 0 0 50 0 0 73 0 45 0 40 43 0 27 0 86 0 62 78 56 49 0 63 36 0 0 0 53 51 0 61 41 88 0 0 89 0 0 74 0 77 0 60 0 0 42 0 47 0 91 38 52 95 48 87 81 0 79 64 84 0 83 85 76 114 31 68 26 0 80 58 44 93 37 92 70 46 90 0 0 105 57 0 0 0 24 0 0 0 0 66 0 0 0 112 0 0 99 0 69 0 0 102 94 35 0 0 0 0 0 0 0 0 0 33 0 0 0 0 0 0 0 34 0 0 0 0 0 0 0 0 0 0 0 0 67 0 0] >> endobj 9 0 obj <> endobj 8 0 obj <> endobj 11 0 obj <> endobj 10 0 obj <> endobj 13 0 obj <> endobj 12 0 obj <> endobj 2 0 obj <>endobj xref 0 400 0000000000 65535 f 0000016182 00000 n 0000096385 00000 n 0000016083 00000 n 0000016230 00000 n 0000015138 00000 n 0000000015 00000 n 0000002252 00000 n 0000096049 00000 n 0000095975 00000 n 0000096182 00000 n 0000096111 00000 n 0000096319 00000 n 0000096240 00000 n 0000016285 00000 n 0000093523 00000 n 0000017523 00000 n 0000017830 00000 n 0000018079 00000 n 0000018143 00000 n 0000018420 00000 n 0000018484 00000 n 0000018549 00000 n 0000018822 00000 n 0000018887 00000 n 0000019115 00000 n 0000019179 00000 n 0000019449 00000 n 0000019513 00000 n 0000019749 00000 n 0000019992 00000 n 0000020207 00000 n 0000020271 00000 n 0000020517 00000 n 0000020581 00000 n 0000020645 00000 n 0000020904 00000 n 0000020968 00000 n 0000021032 00000 n 0000021096 00000 n 0000021340 00000 n 0000021562 00000 n 0000021626 00000 n 0000021855 00000 n 0000022120 00000 n 0000022184 00000 n 0000022426 00000 n 0000022490 00000 n 0000022729 00000 n 0000022793 00000 n 0000022857 00000 n 0000023113 00000 n 0000023177 00000 n 0000023407 00000 n 0000023471 00000 n 0000023706 00000 n 0000023770 00000 n 0000023834 00000 n 0000023898 00000 n 0000023962 00000 n 0000024176 00000 n 0000024240 00000 n 0000024304 00000 n 0000024565 00000 n 0000024804 00000 n 0000025059 00000 n 0000025123 00000 n 0000025187 00000 n 0000025391 00000 n 0000025455 00000 n 0000025519 00000 n 0000025583 00000 n 0000025799 00000 n 0000026025 00000 n 0000026089 00000 n 0000026346 00000 n 0000026598 00000 n 0000026662 00000 n 0000026954 00000 n 0000027018 00000 n 0000027258 00000 n 0000027322 00000 n 0000027608 00000 n 0000027819 00000 n 0000027883 00000 n 0000028072 00000 n 0000028136 00000 n 0000028375 00000 n 0000028439 00000 n 0000028503 00000 n 0000028567 00000 n 0000028631 00000 n 0000028695 00000 n 0000028759 00000 n 0000028823 00000 n 0000029028 00000 n 0000029092 00000 n 0000029156 00000 n 0000029220 00000 n 0000029477 00000 n 0000029541 00000 n 0000029606 00000 n 0000029671 00000 n 0000029737 00000 n 0000029802 00000 n 0000029867 00000 n 0000029932 00000 n 0000030193 00000 n 0000030258 00000 n 0000030323 00000 n 0000030388 00000 n 0000030453 00000 n 0000030518 00000 n 0000030583 00000 n 0000030648 00000 n 0000030713 00000 n 0000030778 00000 n 0000031049 00000 n 0000031302 00000 n 0000031368 00000 n 0000031433 00000 n 0000031679 00000 n 0000031883 00000 n 0000032131 00000 n 0000032196 00000 n 0000032428 00000 n 0000032734 00000 n 0000032975 00000 n 0000033199 00000 n 0000033264 00000 n 0000033523 00000 n 0000033765 00000 n 0000033979 00000 n 0000034045 00000 n 0000034265 00000 n 0000034537 00000 n 0000034602 00000 n 0000034871 00000 n 0000034936 00000 n 0000035189 00000 n 0000035495 00000 n 0000035561 00000 n 0000035626 00000 n 0000035691 00000 n 0000035946 00000 n 0000036151 00000 n 0000036424 00000 n 0000036658 00000 n 0000036911 00000 n 0000037114 00000 n 0000037355 00000 n 0000037577 00000 n 0000037807 00000 n 0000037872 00000 n 0000038103 00000 n 0000038314 00000 n 0000038558 00000 n 0000038790 00000 n 0000039017 00000 n 0000039265 00000 n 0000039478 00000 n 0000039543 00000 n 0000039763 00000 n 0000040028 00000 n 0000040228 00000 n 0000040462 00000 n 0000040705 00000 n 0000040915 00000 n 0000041142 00000 n 0000041370 00000 n 0000041621 00000 n 0000041882 00000 n 0000042131 00000 n 0000042401 00000 n 0000042466 00000 n 0000042708 00000 n 0000002272 00000 n 0000002303 00000 n 0000015308 00000 n 0000002365 00000 n 0000005086 00000 n 0000091848 00000 n 0000042926 00000 n 0000043168 00000 n 0000043234 00000 n 0000043503 00000 n 0000043782 00000 n 0000044040 00000 n 0000044298 00000 n 0000044539 00000 n 0000044605 00000 n 0000044782 00000 n 0000045062 00000 n 0000045320 00000 n 0000045530 00000 n 0000045802 00000 n 0000045868 00000 n 0000046120 00000 n 0000046333 00000 n 0000046599 00000 n 0000046835 00000 n 0000047093 00000 n 0000047361 00000 n 0000047604 00000 n 0000047865 00000 n 0000047930 00000 n 0000048177 00000 n 0000048242 00000 n 0000048486 00000 n 0000048760 00000 n 0000049010 00000 n 0000049245 00000 n 0000049480 00000 n 0000049717 00000 n 0000049978 00000 n 0000050190 00000 n 0000050491 00000 n 0000050556 00000 n 0000050805 00000 n 0000050871 00000 n 0000051113 00000 n 0000051349 00000 n 0000051591 00000 n 0000051811 00000 n 0000052044 00000 n 0000052341 00000 n 0000052594 00000 n 0000052822 00000 n 0000052887 00000 n 0000053132 00000 n 0000053406 00000 n 0000053693 00000 n 0000053924 00000 n 0000054206 00000 n 0000054447 00000 n 0000054686 00000 n 0000054911 00000 n 0000054977 00000 n 0000055196 00000 n 0000055437 00000 n 0000055691 00000 n 0000055920 00000 n 0000056132 00000 n 0000056376 00000 n 0000056609 00000 n 0000056843 00000 n 0000057095 00000 n 0000057282 00000 n 0000005108 00000 n 0000015463 00000 n 0000005160 00000 n 0000007590 00000 n 0000090997 00000 n 0000057538 00000 n 0000057793 00000 n 0000058053 00000 n 0000058119 00000 n 0000058410 00000 n 0000058682 00000 n 0000058970 00000 n 0000059227 00000 n 0000059507 00000 n 0000059572 00000 n 0000059793 00000 n 0000060042 00000 n 0000060295 00000 n 0000060577 00000 n 0000060801 00000 n 0000061058 00000 n 0000061282 00000 n 0000061553 00000 n 0000061798 00000 n 0000062043 00000 n 0000062301 00000 n 0000062558 00000 n 0000062815 00000 n 0000062881 00000 n 0000063137 00000 n 0000063388 00000 n 0000063620 00000 n 0000063818 00000 n 0000064042 00000 n 0000007612 00000 n 0000015618 00000 n 0000007663 00000 n 0000010429 00000 n 0000090303 00000 n 0000064274 00000 n 0000064497 00000 n 0000064705 00000 n 0000064966 00000 n 0000065241 00000 n 0000065306 00000 n 0000065535 00000 n 0000065779 00000 n 0000066011 00000 n 0000066242 00000 n 0000066502 00000 n 0000066774 00000 n 0000067067 00000 n 0000067352 00000 n 0000067627 00000 n 0000067870 00000 n 0000068151 00000 n 0000068420 00000 n 0000068687 00000 n 0000068918 00000 n 0000069188 00000 n 0000069407 00000 n 0000010451 00000 n 0000088661 00000 n 0000069632 00000 n 0000015773 00000 n 0000010512 00000 n 0000012801 00000 n 0000069857 00000 n 0000070108 00000 n 0000070331 00000 n 0000070546 00000 n 0000070728 00000 n 0000070794 00000 n 0000071022 00000 n 0000071257 00000 n 0000071323 00000 n 0000071567 00000 n 0000071812 00000 n 0000072065 00000 n 0000072306 00000 n 0000072582 00000 n 0000072817 00000 n 0000073040 00000 n 0000073249 00000 n 0000073315 00000 n 0000073557 00000 n 0000073787 00000 n 0000074038 00000 n 0000074296 00000 n 0000074527 00000 n 0000074768 00000 n 0000075030 00000 n 0000075096 00000 n 0000075336 00000 n 0000075587 00000 n 0000075818 00000 n 0000076056 00000 n 0000076299 00000 n 0000076554 00000 n 0000076796 00000 n 0000077054 00000 n 0000077311 00000 n 0000077533 00000 n 0000077715 00000 n 0000077906 00000 n 0000077972 00000 n 0000078214 00000 n 0000078455 00000 n 0000078669 00000 n 0000078892 00000 n 0000079131 00000 n 0000079351 00000 n 0000079602 00000 n 0000079859 00000 n 0000080101 00000 n 0000080342 00000 n 0000080595 00000 n 0000080837 00000 n 0000081095 00000 n 0000081364 00000 n 0000081613 00000 n 0000081864 00000 n 0000082115 00000 n 0000082358 00000 n 0000082614 00000 n 0000082898 00000 n 0000083161 00000 n 0000083400 00000 n 0000083658 00000 n 0000083909 00000 n 0000084164 00000 n 0000084437 00000 n 0000084668 00000 n 0000084914 00000 n 0000085091 00000 n 0000012823 00000 n 0000015928 00000 n 0000012894 00000 n 0000015035 00000 n 0000088250 00000 n 0000085356 00000 n 0000085616 00000 n 0000085868 00000 n 0000086123 00000 n 0000086363 00000 n 0000086605 00000 n 0000086896 00000 n 0000087136 00000 n 0000087397 00000 n 0000087696 00000 n 0000087980 00000 n 0000015057 00000 n trailer << /Size 400 /Root 1 0 R /Info 2 0 R >> startxref 96437 %%EOF fluidsynth-1.1.9/doc/Makefile.am000066400000000000000000000016241322272076000165200ustar00rootroot00000000000000# install the man pages and include in distribution man_MANS = fluidsynth.1 EXTRA_DIST = CMakeLists.txt \ Doxyfile.cmake \ $(man_MANS) \ Doxyfile \ example.c \ fluidsynth-v10-devdoc.xml \ fluidsynth-v11-devdoc.txt \ fluidsynth_arpeggio.c \ fluidsynth_fx.c \ fluidsynth_metronome.c \ fluidsynth_simple.c \ xtrafluid.txt \ FluidSynth-LADSPA.pdf docbook_docs = fluidsynth-v10-devdoc.xml DOCBOOK_STYLESHEET ?= http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl # Run "make update-docs" to update developer doc and doxygen reference update-docs: html/index.html doxygen # The new XML DocBook way: html/index.html: $(docbook_docs) -rm -rf html $(mkinstalldirs) html xsltproc -o html/ --catalogs $(DOCBOOK_STYLESHEET) $< doc-clean: -rm -rf html maintainer-clean-local: doc-clean doxygen: Doxyfile doxygen Doxyfile # Update docs for distribution dist-hook: update-docs fluidsynth-1.1.9/doc/README000066400000000000000000000007371322272076000153500ustar00rootroot00000000000000To build FluidSynth API reference documentation, make sure you have Doxygen installed. If you are using the cmake build system, change to the root build directory. If you are using the deprecated auto-tools based build system, execute the following command in this doc/ directory: $ make doxygen The latest generated API HTML docs can also be found, along with other information, on the Wiki documentation page: http://sourceforge.net/apps/trac/fluidsynth/wiki/Documentation fluidsynth-1.1.9/doc/example.c000066400000000000000000000030051322272076000162560ustar00rootroot00000000000000/* An example of how to use FluidSynth. To compile it on Linux: $ gcc -o example example.c `pkg-config fluidsynth --libs` To compile it on Windows: ... Author: Peter Hanappe. This code is in the public domain. Use it as you like. */ #include #if defined(WIN32) #include #define sleep(_t) Sleep(_t * 1000) #else #include #endif int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth; fluid_audio_driver_t* adriver; int sfont_id; int i, key; /* Create the settings. */ settings = new_fluid_settings(); /* Change the settings if necessary*/ /* Create the synthesizer. */ synth = new_fluid_synth(settings); /* Create the audio driver. The synthesizer starts playing as soon as the driver is created. */ adriver = new_fluid_audio_driver(settings, synth); /* Load a SoundFont and reset presets (so that new instruments * get used from the SoundFont) */ sfont_id = fluid_synth_sfload(synth, "example.sf2", 1); /* Initialize the random number generator */ srand(getpid()); for (i = 0; i < 12; i++) { /* Generate a random key */ key = 60 + (int) (12.0f * rand() / (float) RAND_MAX); /* Play a note */ fluid_synth_noteon(synth, 0, key, 80); /* Sleep for 1 second */ sleep(1); /* Stop the note */ fluid_synth_noteoff(synth, 0, key); } /* Clean up */ delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } fluidsynth-1.1.9/doc/fluidsynth-v10-devdoc.xml000066400000000000000000000611601322272076000212460ustar00rootroot00000000000000
FluidSynth 1.0 — Developer Documentation Peter Hanappe 1.0 2003-12-11 hanappe First attempt. 2003 Copyright Peter Hanappe 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 License. To view a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. The FluidSynth library is distributed under the GNU Library General Public License. A copy of the GNU Library General Public License is contained in the FluidSynth package; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. FluidSynth software synthesizer SoundFont Linux audio development documentation HOWTO FluidSynth is a software synthesizer based on the SoundFont 2 specifications. The synthesizer is available as a shared object that can easily be reused in any application that wants to use wavetable synthesis. This documents explains the basic usage of FluidSynth. Some of the more advanced features are not yet discussed but will be added in future versions. Disclaimer This documentation, in its current version, is probably outdated and most certainly incomplete. As always, the source code is the final reference. SoundFont(R) is a registered trademark of E-mu Systems, Inc. Introduction FluidSynth can easily be embedded in an application. It has a main header file, fluidsynth.h, and one dynamically linkable library. FluidSynth runs on Linux, MacOS 9, MacOS X, and the Win32 platforms. It has audio and midi drivers for all mentioned platforms but you can use it with your own drivers if your application already handles audio and MIDI input/output. This document explains the basic usage of FluidSynth and provides examples that you can reuse. Creating and changing the settings Before you can use the synthesizer, you have to create a settings object. The settings objects is used by all components of the FluidSynth library. It gives a unified API to set the parameters of the audio drivers, the midi drivers, the synthesizer, andsoforth. A number of default settings are defined by the current implementation. In future versions, the use of the settings will probably be generalized. All settings have a name that follows the "dotted-name" notation. For example, "synth.polyphony" refers to the number of voices (polyphony) preallocated by the synthesizer. The settings also have a type. There are currently three types: strings, numbers (double floats), and integers. You can change the values of a setting using the fluid_settings_setstr, fluid_settings_setnum, and fluid_settings_setint functions. For example: #include <fluidsynth.h> int main(int argc, char** argv) { fluid_settings_t* settings = new_fluid_settings(); fluid_settings_setint(settings, "synth.polyphony", 128); delete_fluid_settings(settings); return 0; } The API contains the functions to query the type, the current value, the default value, the range and the "hints" of a setting. The range is the minumum and maximum value of the setting. The hints gives additional information about a setting. For example, whether a string represents a filename. Or whether a number should be interpreted on on a logarithmic scale. Check the API documentation for a description of all functions. Creating the synthesizer To create the synthesizer, you pass it the settings object, as in the following example: #include <fluidsynth.h> int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth; fluid_settings_t* settings = new_fluid_settings(); synth = new_fluid_synth(settings); /* Do useful things here */ delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } The default settings should be fine for most uses. A detailed description of all the settings used by the synthesizer described below. Synthesizer settings synth.gain Type number Default 0.2 Min-Max 0.0-10.0 Description The gain is applied to the final or master output of the synthesizer. It is set to a low value by default to avoid the saturation of the output when random MIDI files are played. synth.sample-rate Type number Default 44100 Min-Max 22050-96000 Description The sample rate of the audio generated by the synthesizer. synth.polyphony Type integer Default 256 Min-Max 16-4096 Description The polyphony defines how many voices can be played in parallel. The number of voices is not necessarily equivalent to the number of notes played simultaniously. Indeed, when a note is struck on a specific MIDI channel, the preset on that channel may created several voices, for example, one for the left audio channel and one for the right audio channels. The number of voices activated depends on the number of instrument zones that fall in the correspond to the velocity and key of the played note. synth.midi-channels Type integer Default 16 Min-Max 16-256 Description This setting defines the number of MIDI channels of the synthesizer. The MIDI standard defines 16 channels, so most hardware keyboards are limited to 16. If you plan to use the synthesizer as a plugin in an application, it might be interesting to set the number of channels to a larger value. In this case you can program a greater number of presets. synth.reverb.active Type string Default "yes" Description When set to "yes" the reverb effects module is activated. Otherwise, no reverb will be added to the output signal. Note that when the reverb module is active, the amount of signal send to the reverb module depends on the "reverb send" generator defined in the SoundFont. synth.chorus.active Type string Default "yes" Description When set to "yes" the chorus effects module is activated. Otherwise, no chorus will be added to the output signal. Note that when the reverb module is active, the amount of signal send to the chorus module depends on the "chorus send" generator defined in the SoundFont. synth.ladspa.active Type string Default "no" Description When set to "yes" the LADSPA subsystem will be called. This subsystem allows to load and interconnect LADSPA plugins. The output of the synthesizer is processed by the LADSPA subsystem. Note that the synthesizer has to be compiled with LADSPA support. More information about the LADSPA subsystem later. synth.audio-groups Type integer Default 1 Min-Max 1-128 Description By default, the synthesizer outputs a single stereo signal. Using this option, the synthesizer can output multichannel audio. synth.effects-channels Type integer Default 2 Min-Max 2-2 Description synth.verbose Type string Default "no" Description When set to "yes" the synthesizer will print out information about the received MIDI events to the stdout. This can be helpful for debugging. This setting can not be changed after the synthesizer has started. synth.dump Type string Default "no" Description
Creating the audio driver 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 theaudio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description. alsa: Advanced Linux Sound Architecture oss: Open Sound System (Linux) jack: JACK Audio Connection Kit (Linux, Mac OS X) portaudio: Portaudio Library (MacOS 9 & X, Windows, Linux) sndmgr: Apple SoundManager (Mac OS Classic) coreaudio: Apple CoreAudio (MacOS X, experimental) dsound: Microsoft DirectSound (Windows) The default audio driver depends on the settings with which FluidSynth was compiled. You can get the default driver with fluid_settings_getstr_default(settings, "audio.driver"). 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. Additional options that define the audio quality and latency are "audio.sample-format", "audio.period-size", and "audio.periods". The details are described later. You create the audio driver with the new_fluid_audio_driver function. This function takes the settings and synthesizer object as arguments. For example: void init() { fluid_settings_t* settings; fluid_synth_t* synth; fluid_audio_driver_t* adriver; settings = new_fluid_settings(); /* Set the synthesizer settings, if necessary */ synth = new_fluid_synth(settings); fluid_settings_setstr(settings, "audio.driver", "jack"); adriver = new_fluid_audio_driver(settings, synth); } As soon as the audio driver is created, it will start playing. The audio driver creates a separate thread that runs in real-time mode (is the application has sufficient privileges) and call the synthesizer object to generate the audio. There are a number of general audio driver settings. The audio.driver settings defines 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. They will be documented later. General audio driver settings audio.driver Type string Default alsa (Linux), dsound (Windows), sndman (MacOS9), coreaudio (MacOS X) Options alsa, oss, jack, dsound, sndman, coreaudio, portaudio Description The audio system to be used. audio.periods Type int Default 16 (Linux, MacOS X), 8 (Windows) Min-Max 2-64 Description The number of the audio buffers used by the driver. This number of buffers, multiplied by the buffer size (see setting audio.period-size), determines the maximum latency of the audio driver. audio.period-size Type int Default 64 (Linux, MacOS X), 512 (Windows) Min-Max 64-8192 Description The size of the audio buffers (in frames). audio.sample-format Type string Default "16bits" Options "16bits", "float" Description The format of the audio samples. This is currently only an indication; the audio driver may ignore this setting if it can't handle the specified format.
Using the synthesizer without an audio driver It is possible to use the synthesizer object without creating an audio driver. This is desirable if the application using FluidSynth manages the audio output itself. The synthesizer has several API functions that can be used to obtain the audio output: fluid_synth_write_s16 fills two buffers (left and right channel) with samples coded as signed 16 bits (the endian-ness is machine dependent). fluid_synth_write_float fills a left and right audio buffer with 32 bits floating point samples. For multi channel audio output, the function fluid_synth_nwrite_float has to be used. The function fluid_synth_process is still experimental and its use is therefore not recommended but it will probably become the generic interface in future versions. Loading and managing SoundFonts Before any sound can be produced, the synthesizer needs a SoundFont. For a discussion on SoundFont please refer to some other, not yet existing, therefore virtual document. SoundFonts are loaded with the fluid_synth_sfload function. The function takes the path to a SoundFont file as argument and a boolean to indicate whether the presets of the MIDI channels should be updated after the SoundFont is loaded. More on the preset updates below. The synthesizer can load any number of SoundFonts. This is an advantage, of course, but there are some issues that you must be aware of. The presets in a SoundFont are identified by their bank and preset number. The MIDI specifications allows the change of a preset on a MIDI channel using the combination of "bank select" and the "program change" messages. An ambiguity arrizes when a preset with a specific bank and preset number is defined in multiple loaded SoundFonts. This is solved by searching the SoundFonts in the inverse order they were loaded, i.e. the lastly loaded SoundFont is searched first for the request preset (identified by bank and preset number) then the on but last loaded SoundFont, and so on until. The first preset found is then used. You can somehow consider the SoundFonts placed on a stack. The SoundFont on top of the stack is inspected first, followed by the SoundFont down on the stack. Newly loaded SoundFonts are always placed on top of the stack. This is how commercial, hardware synthesizers work. The inconvenience is that a preset in a SoundFont at the bottom end of the stack may be masked by a preset in a SoundFont at the top of the stack. Using the standard MIDI messages, bank select and program change, there is no way to select a masked preset. However, FluidSynth has an API function to unambiguously select a preset (fluid_synth_program_select). This function is not invokeable through MIDI messages, though. The fluid_synth_sfload function returns the unique identifier of the loaded SoundFont, or -1 in case of an error. This identifier is used in subsequent management functions: fluid_synth_sfunload removes the SoundFont, fluid_synth_sfreload reloads the SoundFont. When a SoundFont is reloaded, it retains it's ID and position on the SoundFont stack. Additional API functions are provided to get the number of loaded SoundFonts ot to get a pointer to the SoundFont. Another issue that needs some explanation is the reprogramming of the presets after a SoundFont load or unload. The default behavior of commercial synthesizers is to reset all the preset that are programmed on the MIDI channels when a SoundFont is loaded or unloaded. Consider the case where MIDI channel 1 uses preset (0, 0) (the couple indicates the bank and program number). This preset was found in the SoundFont with ID 3, for example. When a new SoundFont is loaded that also contains a preset with bank number 0 and program number 0, then the newly loaded preset will be used on channel 1 for future events. This behavior is as if a bank select and program change message is send to all channels after a load/unload using the channel's bank and program number. This may be sometimes confusing or unwanted. A user may not want to loose its preset setup when a new SoundFont is loaded. To avoid the reprogramming of the presets, the third parameter to the fluid_synth_sfload and fluid_synth_sfunload functions should be set to zero. Sending MIDI events Once the synthesizer is up and running and a SoundFont is loaded, most people will want to do something usefull with it. Make noise, for example. The synthesizer aims to be compatible with the MIDI standard, so it accepts almost all MIDI messages (details on the MIDI compatibility elsewhere). The MIDI channel messages can be send using the fluid_synth_noteon, fluid_synth_noteoff, fluid_synth_cc, fluid_synth_pitch_bend, fluid_synth_pitch_wheel_sens, and fluid_synth_program_change functions. For convenience, there's also a fluid_synth_bank_select function (the bank select message is normally sent using a control change message). The following example show a generic graphical button that plays a not when clicked: class SoundButton : public SomeButton { public: SoundButton() : SomeButton() { if (!_synth) { initSynth(); } } static void initSynth() { _settings = new_fluid_settings(); _synth = new_fluid_synth(_settings); _adriver = new_fluid_audio_driver(_settings, _synth); } /* ... */ virtual int handleMouseDown(int x, int y) { /* Play a note on key 60 with velocity 100 on MIDI channel 0 */ fluid_synth_noteon(_synth, 0, 60, 100); } virtual int handleMouseUp(int x, int y) { /* Release the note on key 60 */ fluid_synth_noteoff(_synth, 0, 60); } protected: static fluid_settings_t* _settings; static fluid_synth_t* _synth; static fluid_audio_driver_t* _adriver; }; Advanced features, not yet documented Accessing low-level voice parameters Reverb settings Chorus settings Interpolation settings (set_gen, get_gen, NRPN) Sequencer LADSPA effects unit MIDI router Multi-channel audio MIDI tunings MIDI file player SoundFont loader
fluidsynth-1.1.9/doc/fluidsynth-v11-devdoc.txt000066400000000000000000001671431322272076000212760ustar00rootroot00000000000000/*! \mainpage FluidSynth 1.1 Developer Documentation \author Peter Hanappe \author Conrad Berhörster \author Antoine Schmitt \author Pedro López-Cabanillas \author Josh Green \author David Henningsson \author Tom Moebert \author Copyright © 2003-2018 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert \version Revision 1.1.9 \date 2018-01-02 All the source code examples in this document are in the public domain; you can use them as you please. This document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ . The FluidSynth library is distributed under the GNU Lesser General Public License. A copy of the GNU Lesser General Public License is contained in the FluidSynth package; if not, visit http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. \section Abstract FluidSynth is a software synthesizer based on the SoundFont 2 specifications. The synthesizer is available as a shared object that can easily be reused in any application that wants to use wave-table synthesis. This document explains the basic usage of FluidSynth. Some of the more advanced features are not yet discussed but will be added in future versions. \section Contents Table of Contents - \ref Disclaimer - \ref Introduction - \ref NewIn1_1_9 - \ref NewIn1_1_8 - \ref NewIn1_1_7 - \ref NewIn1_1_6 - \ref NewIn1_1_5 - \ref NewIn1_1_4 - \ref NewIn1_1_3 - \ref NewIn1_1_2 - \ref NewIn1_1_1 - \ref NewIn1_1_0 - \ref CreatingSettings - \ref CreatingSynth - \ref CreatingAudioDriver - \ref UsingSynth - \ref LoadingSoundfonts - \ref SendingMIDI - \ref RealtimeMIDI - \ref MIDIPlayer - \ref MIDIPlayerMem - \ref MIDIRouter - \ref Sequencer - \ref Shell - \ref Advanced \section Disclaimer This documentation, in its current version, is incomplete. As always, the source code is the final reference. SoundFont(R) is a registered trademark of E-mu Systems, Inc. \section Introduction What is FluidSynth? - FluidSynth is a software synthesizer based on the SoundFont 2 specifications. The synthesizer is available as a shared object (a concept also named Dynamic Linking Library, or DLL) that can be easily reused in any application for wave-table synthesis. This document explains the basic usage of FluidSynth. - FluidSynth provides a Command Line Interface program ready to be used from the console terminal, offering most of the library functionalities to end users, among them the ability of render and play Standard MIDI Files, receive real-time MIDI events from external hardware ports and other applications, perform advanced routing of such events, enabling at the same time a local shell as well as a remote server commands interface. - FluidSynth is an API (Application Programming Interface) relieving programmers from a lot of details of reading SoundFont and MIDI events and files, and sending the digital audio output to a Sound Card. These tasks can be accomplished using a small set of functions. This document explains most of the API functions and gives short examples about them. - FluidSynth uses instrument samples contained in standard SF2 (SoundFont 2) files, having a file structure based on the RIFF format. The specification can be obtained here: http://connect.creativelabs.com/developer/SoundFont/Forms/AllItems.aspx but most users don't need to know any details of the format. - FluidSynth can easily be embedded in an application. It has a main header file, fluidsynth.h, and one dynamically linkable library. FluidSynth runs on Linux, Mac OS X, and the Windows platforms, and support for OS/2 and OpenSolaris is experimental. It has audio and midi drivers for all mentioned platforms but you can use it with your own drivers if your application already handles MIDI and audio input/output. This document explains the basic usage of FluidSynth and provides examples that you can reuse. - FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org \section NewIn1_1_9 Whats new in 1.1.9? Changes in FluidSynth 1.1.9 concerning developers: - add a function for registering audio drivers based on acutal needs: fluid_audio_driver_register() - dsound driver now uses the desktop window handle, fluid_get_hinstance() and fluid_set_hinstance() are deprecated - implement handling of #FLUID_SEQ_ALLSOUNDSOFF events in fluid_seq_fluidsynth_callback() - fix return value of fluid_file_set_encoding_quality() - deprecate fluid_synth_set_gen2() - For a full list of bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-119 \section NewIn1_1_8 Whats new in 1.1.8? Changes in FluidSynth 1.1.8 concerning developers: - fluid_synth_get_channel_preset() is not deprecated anymore - deprecate fluid_synth_get_channel_info() - deprecate fluid_synth_set_midi_router() - deprecate redundant tuning functions - deprecate fluid_gen_set_default_values() - deprecate struct _fluid_mod_t, use the respective getter and setter functions - For a full list of bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-118 \section NewIn1_1_7 Whats new in 1.1.7? Changes in FluidSynth 1.1.7 concerning developers: - "synth.parallel-render" has been deprecated - fluid_synth_set_channel_type() was not exported properly - "audio.jack.multi" had inverse logic - fluid_synth_write_*() had timing issues when requesting more than 64 audio frames - reverb and chorus are routed to distinct buffers in fluid_synth_nwrite_float() - vorbis-compressed sf3 sound fonts are supported - the following getters have been added: fluid_voice_is_on(), fluid_voice_is_sustained(), fluid_voice_is_sostenuto(), fluid_voice_get_channel(), fluid_voice_get_key(), fluid_voice_get_actual_key(), fluid_voice_get_velocity(), fluid_voice_get_actual_velocity(), fluid_player_get_current_tick(), fluid_player_get_total_ticks(), fluid_player_get_bpm(), fluid_player_get_midi_tempo() - the following enum values have been deprecated: \c FLUID_SEQ_LASTEVENT, \c GEN_LAST, \c LAST_LOG_LEVEL - For a full list of bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-117 \section NewIn1_1_6 Whats new in 1.1.6? Changes in FluidSynth 1.1.6 concerning developers: - The player will not continue to the next song until all EOT (end of track events) have been reached. - Enable long arguments on all platforms where getopt.h is available - Windows: Fluidsynth.pc (pkg-config spec) is now installed. - Mac OS X Lion: A build failure was fixed. - For a full list of bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-116 \section NewIn1_1_5 Whats new in 1.1.5? Changes in FluidSynth 1.1.5 concerning developers: - A change in the Jack driver might require a newer Jack version compared to 1.1.4. - For a full list of bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-115 \section NewIn1_1_4 Whats new in 1.1.4? Changes in FluidSynth 1.1.4 concerning developers: - You can now play MIDI files that reside in memory (instead of specifying a filename). See \ref MIDIPlayerMem for an example. - A hook can be inserted for MIDI file playback, at playback time. This is done through the new fluid_player_set_playback_callback API function. You can use this to both inspect and modify MIDI events as they are being played (or add a MIDI router), just as you can for MIDI input drivers. - Channel 10 used to be the one and only drum channel, this can now be changed using the fluid_synth_set_channel_type. - fluid_synth_all_sounds_off and fluid_synth_all_notes_off are new public API functions. You can use them to turn notes off (i e releasing all keys, voices advance to release phase) or sounds off (more like pressing the mute button), for one channel or all channels. - For Mac OS X users: The CoreAudio driver has been adapted to use AuHAL, and the default build style has changed to "FluidSynth.framework". - For a full list of other enhancements and bug fixes, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-114 \section NewIn1_1_3 Whats new in 1.1.3? Changes in FluidSynth 1.1.3 concerning developers: - There are no new API additions in 1.1.3, this is a pure bug-fix release. For a list of bugs fixed, see https://github.com/FluidSynth/fluidsynth/wiki/ChangeLog#fluidsynth-113 \section NewIn1_1_2 Whats new in 1.1.2? Changes in FluidSynth 1.1.2 concerning developers: - Build system has switched from autotools to CMake. For more information, see README.cmake. The autotools build system is still working, but it is deprecated. The "winbuild" and "macbuild" directories have been dropped in favor of CMake's ability to create project files on demand. - Thread safety has been reworked to overcome the limitations and bugs in version 1.1.0 and 1.1.1. There are two new settings controlling the thread safety, synth.threadsafe-api and synth.parallel-render. More information about these settings is in the \ref CreatingSettings section. Please look them through and set them appropriately according to your use case. - Voice overflow, i e what voice to kill when polyphony is exceeded, is now configurable. - Possibility to update polyphony and sample rate real-time. Note that updating polyphony is not hard real-time safe, and updating sample rate will kill all currently sounding voices. - MIDI Bank Select handling is now configurable. See the synth.midi-bank-select setting in the \ref CreatingSettings section for more information. - Can use RealTimeKit (on Linux) to get real-time priority, if the original attempt fails. Note that you'll need development headers for DBus to enable this functionality. - Shell commands for pitch bend and pitch bend range. - PulseAudio driver: two new settings allows you to specify media role, and control whether pulseaudio can adjust latency. \section NewIn1_1_1 Whats new in 1.1.1? Changes in FluidSynth 1.1.1 concerning developers: - fluid_synth_get_channel_preset() marked as deprecated. New function fluid_synth_get_channel_info() added which is thread safe and should replace most uses of the older function. - Added fluid_synth_unset_program() to unset the active preset on a channel. \section NewIn1_1_0 Whats new in 1.1.0? Overview of changes in FluidSynth 1.1.0 concerning developers: - Extensive work to make FluidSynth thread safe. Previous versions had many multi-thread issues which could lead to crashes or synthesis glitches. Some of the API additions, deprecations and function recommended conditions of use are related to this work. - File renderer object for rendering audio to files. - Sequencer objects can now use the system timer or the sample clock. When using the sample clock, events are triggered based on the current output audio sample position. This means that MIDI is synchronized with the audio and identical output will be achieved for the same MIDI event input. - libsndfile support for rendering audio to different formats and file types. - API for using the MIDI router subsystem. - MIDI Tuning Standard functions were added for specifying whether to activate tuning changes in realtime or not. - SYSEX support (MIDI Tuning Standard only at the moment). - Changed all yes/no boolean string settings to integer #FLUID_HINT_TOGGLED settings with backwards compatibility (assignment and query of boolean values as strings). - Many other improvements and bug fixes. API additions: - A file renderer can be created with new_fluid_file_renderer(), deleted with delete_fluid_file_renderer() and a block of audio processed with fluid_file_renderer_process_block(). - Additional functions were added for using the MIDI router subsystem. To clear all rules from a router use fluid_midi_router_clear_rules() and to set a router to default rules use fluid_midi_router_set_default_rules(). To create a router rule use new_fluid_midi_router_rule() and to delete a rule use delete_fluid_midi_router_rule() (seldom used). Set values of a router rule with fluid_midi_router_rule_set_chan(), fluid_midi_router_rule_set_param1() and fluid_midi_router_rule_set_param2(). fluid_midi_router_add_rule() can be used to add a rule to a router. - New MIDI event functions were added, including fluid_event_channel_pressure(), fluid_event_system_reset(), and fluid_event_unregistering(). - Additional sequencer functions include fluid_sequencer_add_midi_event_to_buffer(), fluid_sequencer_get_use_system_timer() and fluid_sequencer_process(). new_fluid_sequencer2() was added to allow for the timer type to be specified (system or sample clock). - The settings subsystem has some new functions for thread safety: fluid_settings_copystr() and fluid_settings_dupstr(). Also there are new convenience functions to count the number of string options for a setting: fluid_settings_option_count() and for concatenating setting options with a separator: fluid_settings_option_concat(). - MIDI Tuning Standard functions added include: fluid_synth_activate_key_tuning(), fluid_synth_activate_octave_tuning(), fluid_synth_activate_tuning() and fluid_synth_deactivate_tuning(). All of which provide a parameter for specifying if tuning changes should occur in realtime (affect existing voices) or not. - Additional synthesizer API: fluid_synth_get_sfont_by_name() to get a SoundFont by name, fluid_synth_program_select_by_sfont_name() to select an instrument by SoundFont name/bank/program, fluid_synth_set_gen2() for specifying additional parameters when assigning a generator value, fluid_synth_sysex() for sending SYSEX messages to the synth and fluid_synth_get_active_voice_count() to get the current number of active synthesis voices. - Miscellaneous additions: fluid_player_set_loop() to set playlist loop count and fluid_player_get_status() to get current player state. \section CreatingSettings Creating and changing the settings Before you can use the synthesizer, you have to create a settings object. The settings objects is used by many components of the FluidSynth library. It gives a unified API to set the parameters of the audio drivers, the midi drivers, the synthesizer, and so forth. A number of default settings are defined by the current implementation. All settings have a name that follows the "dotted-name" notation. For example, "synth.polyphony" refers to the number of voices (polyphony) preallocated by the synthesizer. The settings also have a type. There are currently three types: strings, numbers (double floats), and integers. You can change the values of a setting using the fluid_settings_setstr(), fluid_settings_setnum(), and fluid_settings_setint() functions. For example: \code #include int main(int argc, char** argv) { fluid_settings_t* settings = new_fluid_settings(); fluid_settings_setint(settings, "synth.polyphony", 128); /* ... */ delete_fluid_settings(settings); return 0; } \endcode The API contains the functions to query the type, the current value, the default value, the range and the "hints" of a setting. The range is the minimum and maximum value of the setting. The hints gives additional information about a setting. For example, whether a string represents a filename. Or whether a number should be interpreted on on a logarithmic scale. Check the settings.h API documentation for a description of all functions. \section CreatingSynth Creating the synthesizer To create the synthesizer, you pass it the settings object, as in the following example: \code #include int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth; settings = new_fluid_settings(); synth = new_fluid_synth(settings); /* Do useful things here */ delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } \endcode The following table provides details on all the settings used by the synthesizer.
Table 1. Synthesizer settings
synth.audio-channels Type integer
Default 1
Min-Max 1-128
Description By default, the synthesizer outputs a single stereo signal. Using this option, the synthesizer can output multichannel audio. Sets the number of stereo channel pairs. So 1 is actually 2 channels (a stereo pair).
synth.audio-groups Type integer
Default 1
Min-Max 1-128
Description Normally the same value as synth.audio-channels. LADSPA effects subsystem can use this value though, in which case it may differ.
synth.chorus.active Type boolean
Default 1 (TRUE)
Description When set to 1 (TRUE) the chorus effects module is activated. Otherwise, no chorus will be added to the output signal. Note that the amount of signal sent to the chorus module depends on the "chorus send" generator defined in the SoundFont.
synth.cpu-cores Type integer
Default 1
Min-Max 1-256
Description (Experimental) Sets the number of synthesis CPU cores. If set to a value greater than 1, then additional synthesis threads will be created to take advantage of a multi CPU or CPU core system. This has the affect of utilizing more of the total CPU for voices or decreasing render times when synthesizing audio to a file.
synth.device-id Type integer
Default 0
Min-Max 0-126
Description Device identifier used for SYSEX commands, such as MIDI Tuning Standard commands. Only those SYSEX commands destined for this ID or to all devices will be acted upon.
synth.dump Type boolean
Default 0 (FALSE)
Description Does nothing currently.
synth.effects-channels Type integer
Default 2
Min-Max 2-2
Description
synth.gain Type number
Default 0.2
Min-Max 0.0-10.0
Description The gain is applied to the final or master output of the synthesizer. It is set to a low value by default to avoid the saturation of the output when many notes are played.
synth.ladspa.active Type boolean
Default 0 (FALSE)
Description When set to "yes" the LADSPA subsystem will be enabled. This subsystem allows to load and interconnect LADSPA plug-ins. The output of the synthesizer is processed by the LADSPA subsystem. Note that the synthesizer has to be compiled with LADSPA support. More information about the LADSPA subsystem later.
synth.midi-channels Type integer
Default 16
Min-Max 16-256
Description This setting defines the number of MIDI channels of the synthesizer. The MIDI standard defines 16 channels, so MIDI hardware is limited to this number. Internally FluidSynth can use more channels which can be mapped to different MIDI sources.
synth.midi-bank-select Type string
Default gs
Options gm, gs, xg, mma
Description This setting defines how the synthesizer interprets Bank Select messages.
  • gm: ignores CC0 and CC32 messages.
  • gs: (default) CC0 becomes the bank number, CC32 is ignored.
  • xg: CC32 becomes the bank number, CC0 is ignored.
  • mma: bank is calculated as CC0*128+CC32.
synth.min-note-length Type integer
Default 10
Min-Max 0-65535
Description Sets the minimum note duration in milliseconds. This ensures that really short duration note events, such as percussion notes, have a better chance of sounding as intended. Set to 0 to disable this feature.
synth.parallel-render Type boolean
Default 1 (TRUE)
Description synth.parallel-render is the low-latency setting. If on, you're allowed to call fluid_synth_write_s16, fluid_synth_write_float, fluid_synth_nwrite_float or fluid_synth_process in parallel with the rest of the calls, and it won't be blocked by time intensive calls to the synth. Turn it off if throughput is more important than latency, e g in rendering-to-file scenarios where underruns is not an issue.

Deprecated:
As of 1.1.7 this option is deprecated. This option enforces thread safety for rvoice_mixer, which causes rvoice_events to be queued internally. The current implementation relies on the fact that this option is set to TRUE to correctly render any amount of requested audio. Also calling fluid_synth_write_* in parallel is not considered to be a use-case. It would cause undefined audio output, as it would be unpredictable for the user which rvoice_events specifically would be dispatched to which fluid_synth_write_* call.
synth.polyphony Type integer
Default 256
Min-Max 1-65535
Description The polyphony defines how many voices can be played in parallel. A note event produces one or more voices. Its good to set this to a value which the system can handle and will thus limit FluidSynth's CPU usage. When FluidSynth runs out of voices it will begin terminating lower priority voices for new note events.
synth.reverb.active Type boolean
Default 1 (TRUE)
Description When set to 1 (TRUE) the reverb effects module is activated. Otherwise, no reverb will be added to the output signal. Note that the amount of signal sent to the reverb module depends on the "reverb send" generator defined in the SoundFont.
synth.sample-rate Type number
Default 44100
Min-Max 22050-96000
Description The sample rate of the audio generated by the synthesizer.
synth.threadsafe-api Type boolean
Default 1 (TRUE)
Description synth.threadsafe-api controls whether the synth's public API is protected by a mutex or not. Default is on, turn it off for slightly better performance if you know you're only accessing the synth from one thread only, this could be the case in many embedded use cases for example. Note that libfluidsynth can use many threads by itself (shell is one, midi driver is one, midi player is one etc) so you should usually leave it on. Also see synth.parallel-render.
synth.verbose Type boolean
Default 0 (FALSE)
Description When set to 1 (TRUE) the synthesizer will print out information about the received MIDI events to the stdout. This can be helpful for debugging. This setting cannot be changed after the synthesizer has started.
\section CreatingAudioDriver Creating the Audio Driver 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. - jack: JACK Audio Connection Kit (Linux, Mac OS X, Windows) - alsa: Advanced Linux Sound Architecture (Linux) - oss: Open Sound System (Linux, Unix) - pulseaudio: PulseAudio (Linux, Mac OS X, Windows) - coreaudio: Apple CoreAudio (Mac OS X) - dsound: Microsoft DirectSound (Windows) - portaudio: PortAudio Library (Mac OS 9 & X, Windows, Linux) - sndman: Apple SoundManager (Mac OS Classic) - dart: DART sound driver (OS/2) - file: Driver to output audio to a file 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. Additional options that define the audio quality and latency are "audio.sample-format", "audio.period-size", and "audio.periods". The details are described later. You create the audio driver with the new_fluid_audio_driver() function. This function takes the settings and synthesizer object as arguments. For example: \code void init() { fluid_settings_t* settings; fluid_synth_t* synth; fluid_audio_driver_t* adriver; settings = new_fluid_settings(); /* Set the synthesizer settings, if necessary */ synth = new_fluid_synth(settings); fluid_settings_setstr(settings, "audio.driver", "jack"); adriver = new_fluid_audio_driver(settings, synth); } \endcode As soon as the audio driver is created, it will start playing. The audio driver creates a separate thread that uses the synthesizer object to generate the audio. 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 which are documented in another table.
Table 2. General audio driver settings
audio.driver Type string
Default jack (Linux), dsound (Windows), sndman (MacOS9), coreaudio (Mac OS X), dart (OS/2)
Options jack, alsa, oss, pulseaudio, coreaudio, dsound, portaudio, sndman, dart, file
Description The audio system to be used.
audio.periods Type int
Default 16 (Linux, Mac OS X), 8 (Windows)
Min-Max 2-64
Description The number of the audio buffers used by the driver. This number of buffers, multiplied by the buffer size (see setting audio.period-size), determines the maximum latency of the audio driver.
audio.period-size Type integer
Default 64 (Linux, Mac OS X), 512 (Windows)
Min-Max 64-8192
Description The size of the audio buffers (in frames).
audio.realtime-prio Type integer
Default 60
Min-Max 0-99
Description Sets the realtime scheduling priority of the audio synthesis thread (0 disables high priority scheduling). Linux is the only platform which currently makes use of different priority levels. Drivers which use this option: alsa, oss and pulseaudio
audio.sample-format Type string
Default "16bits"
Options "16bits", "float"
Description The format of the audio samples. This is currently only an indication; the audio driver may ignore this setting if it can't handle the specified format.
The following table describes audio driver specific settings.
Table 3. Audio driver specific settings
audio.alsa.device Type string
Default "default"
Options ALSA device string, such as: "hw:0", "plughw:1", etc.
Description Selects the ALSA audio device to use.
audio.coreaudio.device Type string
Default "default"
Description Selects the CoreAudio device to use.
audio.dart.device Type string
Default "default"
Description Selects the Dart (OS/2 driver) device to use.
audio.dsound.device Type string
Default "default"
Description Selects the DirectSound (Windows) device to use.
audio.file.endian Type string
Default 'auto' if libsndfile support is built in, 'cpu' otherwise.
Options auto, big, cpu, little ('cpu' is all that is supported if libsndfile support is not built in)
Description Defines the byte order when using the 'file' driver or file renderer to store audio to a file. 'auto' uses the default for the given file type, 'cpu' uses the CPU byte order, 'big' uses big endian byte order and 'little' uses little endian byte order.
audio.file.format Type string
Default s16
Options double, float, s16, s24, s32, s8, u8 ('s16' is all that is supported if libsndfile support not built in)
Description Defines the audio format when rendering audio to a file. 'double' is 64 bit floating point, 'float' is 32 bit floating point, 's16' is 16 bit signed PCM, 's24' is 24 bit signed PCM, 's32' is 32 bit signed PCM, 's8' is 8 bit signed PCM and 'u8' is 8 bit unsigned PCM.
audio.file.name Type string
Default 'fluidsynth.wav' if libsndfile support is built in, 'fluidsynth.raw' otherwise.
Description Specifies the file name to store the audio to, when rendering audio to a file.
audio.file.type Type string
Default 'auto' if libsndfile support is built in, 'raw' otherwise.
Options aiff, au, auto, avr, caf, flac, htk, iff, mat, oga, paf, pvf, raw, sd2, sds, sf, voc, w64, wav, xi (actual list of types may vary and depends on the libsndfile library used, 'raw' is the only type available if no libsndfile support is built in).
Description Sets the file type of the file which the audio will be stored to. 'auto' attempts to determine the file type from the audio.file.name file extension and falls back to 'wav' if the extension doesn't match any types.
audio.jack.autoconnect Type boolean
Default 0 (FALSE)
Description If 1 (TRUE), then FluidSynth output is automatically connected to jack system audio output.
audio.jack.id Type string
Default fluidsynth
Description ID used when creating Jack client connection.
audio.jack.multi Type boolean
Default 0 (FALSE)
Description If 1 (TRUE), then multi-channel Jack output will be enabled if synth.audio-channels is greater than 1.
audio.jack.server Type string
Default
Description Jack server to connect to. Defaults to an empty string, which uses default Jack server.
audio.oss.device Type string
Default /dev/dsp
Description Device to use for OSS audio output.
audio.portaudio.device Type string
Default PortAudio Default
Description Device to use for PortAudio driver output. Note that 'PortAudio Default' is a special value which outputs to the default PortAudio device.
audio.pulseaudio.device Type string
Default "default"
Description Device to use for PulseAudio driver output
audio.pulseaudio.server Type string
Default "default"
Description Server to use for PulseAudio driver output
\section UsingSynth Using the synthesizer without an audio driver It is possible to use the synthesizer object without creating an audio driver. This is desirable if the application using FluidSynth manages the audio output itself. The synthesizer has several API functions that can be used to obtain the audio output: fluid_synth_write_s16() fills two buffers (left and right channel) with samples coded as signed 16 bits (the endian-ness is machine dependent). fluid_synth_write_float() fills a left and right audio buffer with 32 bits floating point samples. For multi channel audio output, the function fluid_synth_nwrite_float() has to be used. The function fluid_synth_process() is still experimental and its use is therefore not recommended but it will probably become the generic interface in future versions. \section LoadingSoundfonts Loading and managing SoundFonts Before any sound can be produced, the synthesizer needs a SoundFont. SoundFonts are loaded with the fluid_synth_sfload() function. The function takes the path to a SoundFont file and a boolean to indicate whether the presets of the MIDI channels should be updated after the SoundFont is loaded. When the boolean value is TRUE, all MIDI channel bank and program numbers will be refreshed, which may cause new instruments to be selected from the newly loaded SoundFont. The synthesizer can load any number of SoundFonts. The loaded SoundFonts are treated as a stack, where each new loaded SoundFont is placed at the top of the stack. When selecting presets by bank and program numbers, SoundFonts are searched beginning at the top of the stack. In the case where there are presets in different SoundFonts with identical bank and program numbers, the preset from the most recently loaded SoundFont is used. The fluid_synth_program_select() can be used for unambiguously selecting a preset or bank offsets could be applied to each SoundFont with fluid_synth_set_bank_offset(), to try and ensure that each preset has unique bank and program numbers. The fluid_synth_sfload() function returns the unique identifier of the loaded SoundFont, or -1 in case of an error. This identifier is used in subsequent management functions: fluid_synth_sfunload() removes the SoundFont, fluid_synth_sfreload() reloads the SoundFont. When a SoundFont is reloaded, it retains it's ID and position on the SoundFont stack. Additional API functions are provided to get the number of loaded SoundFonts and to get a pointer to the SoundFont. \section SendingMIDI Sending MIDI Events Once the synthesizer is up and running and a SoundFont is loaded, most people will want to do something useful with it. Make noise, for example. MIDI messages can be sent using the fluid_synth_noteon(), fluid_synth_noteoff(), fluid_synth_cc(), fluid_synth_pitch_bend(), fluid_synth_pitch_wheel_sens(), and fluid_synth_program_change() functions. For convenience, there's also a fluid_synth_bank_select() function (the bank select message is normally sent using a control change message). The following example show a generic graphical button that plays a note when clicked: \code class SoundButton : public SomeButton { public: SoundButton() : SomeButton() { if (!_synth) { initSynth(); } } static void initSynth() { _settings = new_fluid_settings(); _synth = new_fluid_synth(_settings); _adriver = new_fluid_audio_driver(_settings, _synth); } /* ... */ virtual int handleMouseDown(int x, int y) { /* Play a note on key 60 with velocity 100 on MIDI channel 0 */ fluid_synth_noteon(_synth, 0, 60, 100); } virtual int handleMouseUp(int x, int y) { /* Release the note on key 60 */ fluid_synth_noteoff(_synth, 0, 60); } protected: static fluid_settings_t* _settings; static fluid_synth_t* _synth; static fluid_audio_driver_t* _adriver; }; \endcode \section RealtimeMIDI Creating a Real-time MIDI Driver FluidSynth can process real-time MIDI events received from hardware MIDI ports or other applications. To do so, the client must create a MIDI input driver. It is a very similar process to the creation of the audio driver: you initialize some properties in a settings instance and call the new_fluid_midi_driver() function providing a callback function that will be invoked when a MIDI event is received. The following MIDI drivers are currently supported: - jack: JACK Audio Connection Kit MIDI driver (Linux, Mac OS X) - oss: Open Sound System raw MIDI (Linux, Unix) - alsa_raw: ALSA raw MIDI interface (Linux) - alsa_seq: ALSA sequencer MIDI interface (Linux) - winmidi: Microsoft Windows MM System (Windows) - midishare: MIDI Share (Linux, Mac OS X) - coremidi: Apple CoreMIDI (Mac OS X) \code #include int handle_midi_event(void* data, fluid_midi_event_t* event) { printf("event type: %d\n", fluid_midi_event_get_type(event)); } int main(int argc, char** argv) { fluid_settings_t* settings; fluid_midi_driver_t* mdriver; settings = new_fluid_settings(); mdriver = new_fluid_midi_driver(settings, handle_midi_event, NULL); /* ... */ delete_fluid_midi_driver(mdriver); return 0; } \endcode There are a number of general MIDI driver settings. The midi.driver setting defines the MIDI subsystem that will be used. There are additional settings for the MIDI subsystems used, which are described in a following table.
Table 4. General MIDI driver settings
midi.driver Type string
Default alsa_seq (Linux), winmidi (Windows), jack (Mac OS X)
Options alsa_raw, alsa_seq, coremidi, jack, midishare, oss, winmidi
Description The MIDI system to be used.
midi.realtime-prio Type integer
Default 50
Min-Max 0-99
Description Sets the realtime scheduling priority of the MIDI thread (0 disables high priority scheduling). Linux is the only platform which currently makes use of different priority levels. Drivers which use this option: alsa_raw, alsa_seq, oss
The following table defines MIDI driver specific settings.
Table 5. MIDI driver specific settings
midi.alsa.device Type string
Default "default"
Description ALSA MIDI device to use for RAW ALSA MIDI driver.
midi.alsa_seq.device Type string
Default "default"
Description ALSA sequencer device to use for ALSA sequencer driver.
midi.alsa_seq.id Type string
Default pid
Description ID to use when registering ports with the ALSA sequencer driver. If set to "pid" then the ID will be "FLUID Synth (PID)", where PID is the FluidSynth process ID of the audio thread otherwise the provided string will be used in place of PID.
midi.jack.id Type string
Default fluidsynth-midi
Description Client ID to use with the Jack MIDI driver.
midi.jack.server Type string
Default
Description Jack server to connect to for Jack MIDI driver. If an empty string then the default server will be used.
midi.oss.device Type string
Default /dev/midi
Description Device to use for OSS MIDI driver.
midi.portname Type string
Default
Description Used by coremidi and alsa_seq drivers for the portnames registered with the MIDI subsystem.
\section MIDIPlayer Loading and Playing a MIDI file FluidSynth can be used to play MIDI files, using the MIDI File Player interface. It follows a high level implementation, though its implementation is currently incomplete. After initializing the synthesizer, create the player passing the synth instance to new_fluid_player(). Then, you can add some SMF file names to the player using fluid_player_add(), and finally call fluid_player_play() to start the playback. You can check if the player has finished by calling fluid_player_get_status(), or wait for the player to terminate using fluid_player_join(). \code #include int main(int argc, char** argv) { int i; fluid_settings_t* settings; fluid_synth_t* synth; fluid_player_t* player; fluid_audio_driver_t* adriver; settings = new_fluid_settings(); synth = new_fluid_synth(settings); player = new_fluid_player(synth); adriver = new_fluid_audio_driver(settings, synth); /* process command line arguments */ for (i = 1; i < argc; i++) { if (fluid_is_soundfont(argv[i])) { fluid_synth_sfload(synth, argv[1], 1); } if (fluid_is_midifile(argv[i])) { fluid_player_add(player, argv[i]); } } /* play the midi files, if any */ fluid_player_play(player); /* wait for playback termination */ fluid_player_join(player); /* cleanup */ delete_fluid_audio_driver(adriver); delete_fluid_player(player); delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } \endcode Settings which the MIDI player uses are documented below.
Table 6. General MIDI driver settings
player.reset-synth type boolean
default 1 (TRUE)
description If true, reset the synth before starting a new MIDI song, so the state of a previous song can't affect the new song. Turn it off for seamless looping of a song.
player.timing-source type string
default 'sample'
options 'sample', 'system'
description Determines the timing source of the player sequencer. 'sample' uses the sample clock (how much audio has been output) to sequence events, in which case audio is synchronized with MIDI events. 'system' uses the system clock, audio and MIDI are not synchronized exactly.
\section MIDIPlayerMem Playing a MIDI file from memory FluidSynth can be also play MIDI files directly from a buffer in memory. If you need to play a file from a stream (such as stdin, a network, or a high-level file interface), you can load the entire file into a buffer first, and then use this approach. Use the same technique as above, but rather than calling fluid_player_add(), load it into memory and call fluid_player_add_mem() instead. Once you have passed a buffer to fluid_player_add_mem(), it is copied, so you may use it again or free it immediately (it is your responsibility to free it if you allocated it). \code #include #include #include /* An example midi file */ const char MIDIFILE[] = { 0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01, 0x01, 0xe0, 0x4d, 0x54, 0x72, 0x6b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x90, 0x3c, 0x64, 0x87, 0x40, 0x80, 0x3c, 0x7f, 0x00, 0x90, 0x43, 0x64, 0x87, 0x40, 0x80, 0x43, 0x7f, 0x00, 0x90, 0x48, 0x64, 0x87, 0x40, 0x80, 0x48, 0x7f, 0x83, 0x60, 0xff, 0x2f, 0x00 }; int main(int argc, char** argv) { int i; void* buffer; size_t buffer_len; fluid_settings_t* settings; fluid_synth_t* synth; fluid_player_t* player; fluid_audio_driver_t* adriver; settings = new_fluid_settings(); synth = new_fluid_synth(settings); player = new_fluid_player(synth); adriver = new_fluid_audio_driver(settings, synth); /* process command line arguments */ for (i = 1; i < argc; i++) { if (fluid_is_soundfont(argv[i])) { fluid_synth_sfload(synth, argv[1], 1); } } /* queue up the in-memory midi file */ fluid_player_add_mem(player, MIDIFILE, sizeof(MIDIFILE)); /* play the midi file */ fluid_player_play(player); /* wait for playback termination */ fluid_player_join(player); /* cleanup */ delete_fluid_audio_driver(adriver); delete_fluid_player(player); delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } \endcode \section MIDIRouter Real-time MIDI router The MIDI router is one more processing layer directly behind the MIDI input. It processes incoming MIDI events and generates control events for the synth. It can be used to filter or modify events prior to sending them to the synthesizer. When created, the MIDI router is transparent and simply passes all MIDI events. Router "rules" must be added to actually make use of its capabilities. Some examples of MIDI router usage: - Filter messages (Example: Pass sustain pedal CCs only to selected channels) - Split the keyboard (Example: noteon with notenr < x: to ch 1, >x to ch 2) - Layer sounds (Example: for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...) - Velocity scaling (Example: for each noteon event, scale the velocity by 1.27) - Velocity switching (Example: v <= 100: "Angel Choir"; v > 100: "Hell's Bells") - Get rid of aftertouch The MIDI driver API has a clean separation between the midi thread and the synthesizer. That opens the door to add a midi router module. MIDI events coming from the MIDI player do not pass through the MIDI router. \code #include int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth; fluid_midi_router_t* router; fluid_midi_router_rule_t* rule; settings = new_fluid_settings(); synth = new_fluid_synth(settings); /* Create the MIDI router and pass events to the synthesizer */ router = new_fluid_midi_router (settings, fluid_synth_handle_midi_event, synth); /* Clear default rules */ fluid_midi_router_clear_rules (router); /* Add rule to map all notes < MIDI note #60 on any channel to channel 4 */ rule = new_fluid_midi_router_rule (); fluid_midi_router_rule_set_chan (rule, 0, 15, 0.0, 4); /* Map all to channel 4 */ fluid_midi_router_rule_set_param1 (rule, 0, 59, 1.0, 0); /* Match notes < 60 */ fluid_midi_router_add_rule (router, rule, FLUID_MIDI_ROUTER_RULE_NOTE); /* Add rule to map all notes >= MIDI note #60 on any channel to channel 5 */ rule = new_fluid_midi_router_rule (); fluid_midi_router_rule_set_chan (rule, 0, 15, 0.0, 5); /* Map all to channel 5 */ fluid_midi_router_rule_set_param1 (rule, 60, 127, 1.0, 0); /* Match notes >= 60 */ fluid_midi_router_add_rule (router, rule, FLUID_MIDI_ROUTER_RULE_NOTE); /* Add rule to reverse direction of pitch bender on channel 7 */ rule = new_fluid_midi_router_rule (); fluid_midi_router_rule_set_chan (rule, 7, 7, 1.0, 0); /* Match channel 7 only */ fluid_midi_router_rule_set_param1 (rule, 0, 16383, -1.0, 16383); /* Reverse pitch bender */ fluid_midi_router_add_rule (router, rule, FLUID_MIDI_ROUTER_RULE_PITCH_BEND); /* ... Create audio driver, process events, etc ... */ /* cleanup */ delete_fluid_midi_router(router); delete_fluid_synth(synth); delete_fluid_settings(settings); return 0; } \endcode \section Sequencer FluidSynth's sequencer can be used to play MIDI events in a more flexible way than using the MIDI file player, which expects the events to be stored as Standard MIDI Files. Using the sequencer, you can provide the events one by one, with an optional timestamp for scheduling. The client program should first initialize the sequencer instance using the function new_fluid_sequencer2(). There is a complementary function delete_fluid_sequencer() to delete it. After creating the sequencer instance, the destinations can be registered using fluid_sequencer_register_fluidsynth() for the synthesizer destination, and optionally using fluid_sequencer_register_client() for the client destination providing a suitable callback function. It can be unregistered using fluid_sequencer_unregister_client(). After the initialization, events can be sent with fluid_sequencer_send_now() and scheduled to the future with fluid_sequencer_send_at(). The registration functions return identifiers, that can be used as destinations of an event using fluid_event_set_dest(). The function fluid_sequencer_get_tick() returns the current playing position. A program may choose a new timescale in milliseconds using fluid_sequencer_set_time_scale(). The following example uses the fluidsynth sequencer to implement a sort of music box. FluidSynth internal clock is used to schedule repetitive sequences of notes. The next sequence is scheduled on advance before the end of the current one, using a timer event that triggers a callback function. The scheduling times are always absolute values, to avoid slippage. \code #include "fluidsynth.h" fluid_synth_t* synth; fluid_audio_driver_t* adriver; fluid_sequencer_t* sequencer; short synthSeqID, mySeqID; unsigned int now; unsigned int seqduration; // prototype void seq_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data); 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"); synth = new_fluid_synth(settings); adriver = new_fluid_audio_driver(settings, synth); sequencer = new_fluid_sequencer2(0); // register synth as first destination synthSeqID = fluid_sequencer_register_fluidsynth(sequencer, synth); // register myself as second destination mySeqID = fluid_sequencer_register_client(sequencer, "me", seq_callback, NULL); // the sequence duration, in ms seqduration = 1000; } void deletesynth() { delete_fluid_sequencer(sequencer); delete_fluid_audio_driver(adriver); delete_fluid_synth(synth); } void loadsoundfont() { int fluid_res; // put your own path here fluid_res = fluid_synth_sfload(synth, "Inside:VintageDreamsWaves-v2.sf2", 1); } void sendnoteon(int chan, short key, unsigned int date) { int fluid_res; fluid_event_t *evt = new_fluid_event(); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, synthSeqID); fluid_event_noteon(evt, chan, key, 127); fluid_res = fluid_sequencer_send_at(sequencer, evt, date, 1); delete_fluid_event(evt); } void schedule_next_callback() { int fluid_res; // I want to be called back before the end of the next sequence unsigned int callbackdate = now + seqduration/2; fluid_event_t *evt = new_fluid_event(); fluid_event_set_source(evt, -1); fluid_event_set_dest(evt, mySeqID); fluid_event_timer(evt, NULL); fluid_res = fluid_sequencer_send_at(sequencer, evt, callbackdate, 1); delete_fluid_event(evt); } void schedule_next_sequence() { // Called more or less before each sequence start // the next sequence start date now = now + seqduration; // the sequence to play // the beat : 2 beats per sequence sendnoteon(0, 60, now + seqduration/2); sendnoteon(0, 60, now + seqduration); // melody sendnoteon(1, 45, now + seqduration/10); sendnoteon(1, 50, now + 4*seqduration/10); sendnoteon(1, 55, now + 8*seqduration/10); // so that we are called back early enough to schedule the next sequence schedule_next_callback(); } /* sequencer callback */ void seq_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data) { schedule_next_sequence(); } int main(void) { createsynth(); loadsoundfont(); // initialize our absolute date now = fluid_sequencer_get_tick(sequencer); schedule_next_sequence(); sleep(100000); deletesynth(); return 0; } \endcode \section Shell Shell interface The shell interface allows you to send simple textual commands to the synthesizer, to parse a command file, or to read commands from the stdin or other input streams. To find the list of currently supported commands, please check the fluid_cmd.c file or type "help" in the fluidsynth command line shell.
Table 7. General MIDI driver settings
shell.prompt type string
default ""
description In dump mode we set the prompt to "". the ui cannot easily handle lines, which don't end with cr. changing the prompt cannot be done through a command, because the current shell does not handle empty arguments.
shell.port type number
default 9800
min-max 1-65535
description The shell can be used in a client/server mode. This setting controls what TCP/IP port the server uses.
\section Advanced Advanced features, not yet documented. API reference may contain more info. - Accessing low-level voice parameters - Reverb settings - Chorus settings - Interpolation settings (set_gen, get_gen, NRPN) - Voice overflow settings - LADSPA effects unit - Multi-channel audio - MIDI tunings - Fast file renderer for rendering audio to file in non-realtime */ /*! \example example.c Example producing short random music with FluidSynth */ /*! \example fluidsynth_simple.c A basic example of using fluidsynth to play a single note */ /*! \example fluidsynth_fx.c Example of using effects with fluidsynth */ /*! \example fluidsynth_metronome.c Example of a simple metronome using the MIDI sequencer API */ /*! \example fluidsynth_arpeggio.c Example of an arpeggio generated using the MIDI sequencer API */ /*! \example fluidsynth_register_adriver.c Example of how to register audio drivers using fluid_audio_driver_register() (advanced users only) */ fluidsynth-1.1.9/doc/fluidsynth.1000066400000000000000000000373561322272076000167520ustar00rootroot00000000000000.\" hey, Emacs: -*- nroff -*- .\" FluidSynth 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 program 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 General Public License for more details. .\" .\" You should have received a copy of the GNU Lesser General Public License .\" 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 "Jan 2, 2018" .\" Please update the above date whenever this man page is modified. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins (default) .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME FluidSynth \- a SoundFont synthesizer .SH SYNOPSIS .B fluidsynth .RI [ options ] [ SoundFonts ] [ midifiles ] .SH DESCRIPTION \fBFluidSynth\fP is a real-time MIDI synthesizer based on the SoundFont(R) 2 specifications. It can be used to render MIDI input or MIDI files to audio. The MIDI events are read from a MIDI device. The sound is rendered in real-time to the sound output device. .PP The easiest way to start the synthesizer is to give it a SoundFont on the command line: 'fluidsynth soundfont.sf2'. fluidsynth will load the SoundFont and read MIDI events from the default MIDI device using the default MIDI driver. Once FluidSynth is running, it reads commands from the stdin. There are commands to send MIDI events manually, to load or unload SoundFonts, and so forth. All the available commands are discussed below. .PP FluidSynth can also be used to play a list of MIDI files. Simply run FluidSynth with the SoundFont and the list of MIDI files to play. In this case you might not want to open the MIDI device to read external events. Use the \-n option to deactivate MIDI input. If you also want to deactivate the use of the shell, start FluidSynth with the \-i option: 'fluidsynth \-ni soundfont.sf2 midifile1.mid midifile2.mid'. .PP Run fluidsynth with the \-\-help option to check for changes in the list of options. .SH OPTIONS \fBfluidsynth\fP accepts the following options: .TP .B \-a, \-\-audio\-driver=[label] The audio driver to use. "\-a help" to list valid options .TP .B \-c, \-\-audio\-bufcount=[count] Number of audio buffers .TP .B \-C, \-\-chorus Turn the chorus on or off [0|1|yes|no, default = on] .TP .B \-d, \-\-dump Dump incoming and outgoing MIDI events to stdout .TP .B \-E, \-\-audio\-file\-endian Audio file endian for fast rendering or aufile driver ("\-E help" for list) .TP .B \-f, \-\-load\-config Load command configuration file (shell commands) .TP .B \-F, \-\-fast\-render=[file] Render MIDI file to raw audio data and store in [file] .TP .B \-g, \-\-gain Set the master gain [0 < gain < 10, default = 0.2] .TP .B \-G, \-\-audio\-groups Defines the number of LADSPA audio nodes .TP .B \-h, \-\-help Print out this help summary .TP .B \-i, \-\-no\-shell Don't read commands from the shell [default = yes] .TP .B \-j, \-\-connect\-jack\-outputs Attempt to connect the jack outputs to the physical ports .TP .B \-K, \-\-midi\-channels=[num] The number of midi channels [default = 16] .TP .B \-l, \-\-disable\-lash Don't connect to LASH server .TP .B \-L, \-\-audio\-channels=[num] The number of stereo audio channels [default = 1] .TP .B \-m, \-\-midi\-driver=[label] The name of the midi driver to use. "\-m help" to list valid options. .TP .B \-n, \-\-no\-midi\-in Don't create a midi driver to read MIDI input events [default = yes] .TP .B \-o Define a setting, \-o name=value ("\-o help" to dump current values) .TP .B \-O, \-\-audio\-file\-format Audio file format for fast rendering or aufile driver ("\-O help" for list) .TP .B \-p, \-\-portname=[label] Set MIDI port name (alsa_seq, coremidi drivers) .TP .B \-r, \-\-sample\-rate Set the sample rate .TP .B \-R, \-\-reverb Turn the reverb on or off [0|1|yes|no, default = on] .TP .B \-s, \-\-server Start FluidSynth as a server process .TP .B \-T, \-\-audio\-file\-type Audio file type for fast rendering or aufile driver ("\T help" for list) .TP .B \-v, \-\-verbose Print out verbose messages about midi events .TP .B \-V, \-\-version Show version of program .TP .B \-z, \-\-audio\-bufsize=[size] Size of each audio buffer .SH SETTINGS All settings are non-realtime (have no effect if set after startup), except for those indicated as realtime. .TP .B SYNTHESIZER .TP .B synth.audio\-channels INT [min=1, max=128, def=1] Number of audio channels (DOCME!). .TP .B synth.audio\-groups INT [min=1, max=128, def=1] Number of audio groups (DOCME!). .TP .B synth.chorus.active BOOL [def=True] Chorus effect enable toggle. .TP .B synth.cpu\-cores INT [min=1, max=256, def=1] Number of CPU cores to use for multi-core support. .TP .B synth.device\-id INT [min=0, max=126, def=0] REALTIME Device ID to use for accepting incoming SYSEX messages. .TP .B synth.dump BOOL [def=False] No effect currently. .TP .B synth.effects\-channels INT [min=2, max=2, def=2] No effect currently. .TP .B synth.gain FLOAT [min=0.000, max=10.000, def=0.200] REALTIME Master synthesizer gain. .TP .B synth.ladspa.active BOOL [def=False] LADSPA subsystem enable toggle. .TP .B synth.midi\-channels INT [min=16, max=256, def=16] Total MIDI channel count (must be multiple of 16). .TP .B synth.midi\-bank\-select STR [def='gs' vals:'gm', 'gs', 'xg', 'mma'] MIDI Bank Select message style. .TP .B synth.min\-note\-length INT [min=0, max=65535, def=10] Minimum duration for note events (work around for very short percussion notes). .TP .B synth.overflow.age FLOAT [min=\-10000, max=10000, def=1000] Weigthing (on overflow) for a voice's duration. .TP .B synth.overflow.percussion FLOAT [min=\-10000, max=10000, def=4000] Weighting (on overflow) for a voice being on the drum channel. .TP .B synth.overflow.released FLOAT [min=\-10000, max=10000, def=\-2000] Weighting (on overflow) for a voice that has been released, i e note off and no sustain pedal. .TP .B synth.overflow.sustained FLOAT [min=\-10000, max=10000, def=\-1000] Weighting (on overflow) for a voice that has been sustained, i e note off, but sustain pedal held down. .TP .B synth.overflow.volume FLOAT [min=\-10000, max=10000, def=500] Weighting (on overflow) for a voice's volume. .TP .B synth.parallel-render BOOL [def=True] Enables low-latency audio rendering response, even if synth is otherwise busy. Should always to be true for usage by fluidsynth executable. .TP .B synth.polyphony INT [min=1, max=65535, def=256] REALTIME Voice polyphony count (number of simultaneous voices allowed). .TP .B synth.reverb.active BOOL [def=True] Reverb effect enable toggle. .TP .B synth.sample\-rate FLOAT [min=22050.000, max=96000.000, def=44100.000] Synthesizer sample rate. .TP .B synth.threadsafe-api BOOL [def=True] Serializes access to the synth API. Must always to be true for usage by fluidsynth executable. .TP .B synth.verbose BOOL [def=False] Print received MIDI events to stdout. .TP .B GENERAL AUDIO .TP .B audio.driver STR Audio driver to use. Default and valid options depend on available drivers. .TP .B audio.input\-channels INT [min=0, max=2, def=0] Not used currently? (DOCME). .TP .B audio.output\-channels INT [min=2, max=32, def=2] DOCME .TP .B audio.period\-size INT [min=64, max=8192, def=64] Period size for audio buffers. Used by many audio drivers. .TP .B audio.periods INT [min=2, max=64, def=16] Count of audio buffers. Used by many audio drivers. .TP .B audio.realtime\-prio INT [min=0, max=99, def=60] Realtime priority to assign to audio thread or 0 to disable high priority scheduling. Only used by some audio drivers (currently 'alsa' and 'oss'). .TP .B audio.sample\-format STR [def='16bits' vals:'16bits','float'] Audio output format, to select format for those drivers which support 16 bit or floating point. .TP .B AUDIO DRIVER SPECIFIC .TP .B audio.alsa.device STR [def='default'] ALSA audio driver output device. .TP .B audio.coreaudio.device STR [def='default'] CoreAudio driver output device. Valid options depend on system. .TP .B audio.dart.device STR [def='default'] OS/2 Dart audio driver device. .TP .B audio.dsound.device STR [def='default'] Device to use for DirectSound driver. Valid options depend on system. .TP .B audio.file.endian STR [def='auto' vals:'auto','big','cpu','little'] File renderer or file driver byte order selection. 'auto' selects the default for the selected file type. 'cpu' uses the CPU byte order. Limited to 'cpu' if no libsndfile support. .TP .B audio.file.format STR [def='s16' vals:'double','float','s16','s24','s32','s8','u8'] File renderer or file driver audio format. Limited to 's16' if no libsndfile support. .TP .B audio.file.name STR [def='fluidsynth.wav'] Output file name for file renderer or file driver. .TP .B audio.file.type STR [def='auto' vals:'aiff','au','auto','flac','oga','raw','wav'] Output file type for file renderer or file driver. 'auto' attempts to determine type from file extension in audio.file.name. Limited to 'raw' if no libsndfile support. Actual options will vary depending on libsndfile library. .TP .B audio.jack.autoconnect BOOL [def=False] If enabled, then FluidSynth is automatically connected to Jack system audio output ports. .TP .B audio.jack.id STR [def='fluidsynth'] Client ID to use when connecting to Jack. .TP .B audio.jack.multi BOOL [def=False] TRUE to enable multi-channel output. .TP .B audio.jack.server STR [def=''] Jack server name. Blank for default. .TP .B audio.oss.device STR [def='/dev/dsp'] OSS driver output device. .TP .B audio.portaudio.device STR [def='PortAudio Default'] PortAudio driver output device. Available options depends on system. .TP .B audio.pulseaudio.adjust-latency BOOL [def=True] If TRUE initializes the maximum length of the audio buffer to the highest supported value and increases the latency dynamically if PulseAudio suggests so. Else uses a buffer with length of audio.period-size. .TP .B audio.pulseaudio.device STR [def='default'] PulseAudio driver output device. .TP .B audio.pulseaudio.media-role STR [def='music'] PulseAudio media role information. .TP .B audio.pulseaudio.server STR [def='default'] PulseAudio driver server. .TP .B GENERAL MIDI .TP .B midi.driver STR MIDI driver to use. Default and valid options depend on available drivers. .TP .B midi.realtime\-prio INT [min=0, max=99, def=50] Realtime priority to assign to MIDI thread or 0 to disable high priority scheduling. Only used by some MIDI drivers (currently 'alsa_seq', 'alsa_raw' and 'oss'). .TP .B MIDI DRIVER SPECIFIC .TP .B midi.alsa.device STR [def='default'] ALSA raw MIDI driver device. .TP .B midi.alsa_seq.device STR [def='default'] ALSA sequencer MIDI driver device. .TP .B midi.alsa_seq.id STR [def='pid'] ALSA sequencer client ID. 'pid' will use process ID as part of the client name. .TP .B midi.coremidi.id STR [def='pid'] Client ID to use for CoreMIDI driver. 'pid' will use process ID as port of the client name. .TP .B midi.jack.id STR [def='fluidsynth-midi'] Jack MIDI driver client ID. .TP .B midi.jack.server STR [def=''] Jack MIDI driver server. Blank to use default. .TP .B midi.oss.device STR [def='/dev/midi'] OSS MIDI driver device. .TP .B midi.portname STR [def=''] Port name used for CoreAudio and ALSA sequencer drivers. .TP .B midi.winmidi.device STR [def='default'] Device for Windows MIDI driver. .TP .B MISCELLANEOUS .TP .B player.reset\-synth BOOL [def=True] TRUE to reset synthesizer MIDI state between MIDI songs. .TP .B player.timing\-source STR [def='sample' vals:'sample','system'] Selects timing source for MIDI sequencer. 'system' uses the system timer. 'sample' uses the sample clock (amount of audio output, events synchronized with audio). .TP .B shell.port INT [min=1, max=65535, def=9800] Shell command server TCP/IP port number to use. .TP .B shell.prompt STR [def=''] Shell prompt string. .SH SHELL COMMANDS .TP .B GENERAL .TP .B help Prints out list of help topics (type "help ") .TP .B quit Quit the synthesizer .TP .B SOUNDFONTS .TP .B load filename Load a SoundFont .TP .B unload number Unload a SoundFont. The number is the index of the SoundFont on the stack. .TP .B fonts Lists the current SoundFonts on the stack .TP .B inst number Print out the available instruments for the SoundFont. .TP .B MIDI MESSAGES .TP .B noteon channel key velocity Send a note-on event .TP .B noteoff channel key Send a note-off event .TP .B cc channel ctrl value Send a control change event .TP .B prog chan num Send program-change message .TP .B select chan sfont bank prog Combination of bank-select and program-change .TP .B channels Print out the presets of all channels. .TP .B AUDIO SYNTHESIS .TP .B gain value Set the master gain (0 < gain < 5) .TP .B interp num Choose interpolation method for all channels .TP .B interpc chan num Choose interpolation method for one channel .TP .B REVERB .TP .B reverb [0|1|on|off] Turn the reverb on or off .TP .B rev_preset num Load preset num into the reverb unit .TP .B rev_setroomsize num Change reverb room size .TP .B rev_setdamp num Change reverb damping .TP .B rev_setwidth num Change reverb width .TP .B rev_setlevel num Change reverb level .TP .B CHORUS .TP .B chorus [0|1|on|off] Turn the chorus on or off .TP .B cho_set_nr n Use n delay lines (default 3) .TP .B cho_set_level num Set output level of each chorus line to num .TP .B cho_set_speed num Set mod speed of chorus to num (Hz) .TP .B cho_set_depth num Set chorus modulation depth to num (ms) .TP .B MIDI ROUTER .TP .B router_default Reloads the default MIDI routing rules (input channels are mapped 1:1 to the synth) .TP .B router_clear Deletes all MIDI routing rules. .TP .B router_begin [note|cc|prog|pbend|cpress|kpress] Starts a new routing rule for events of the given type .TP .B router_chan min max mul add Limits the rule for events on min <= chan <= max. If the channel falls into the window, it is multiplied by 'mul', then 'add' is added. .TP .B router_par1 min max mul add Limits parameter 1 (for example note number in a note events). Similar to router_chan. .TP .B router_par2 min max mul add Limits parameter 2 (for example velocity in a note event). Similar to router_chan .TP .B router_end Finishes the current rule and adds it to the router. .TP .B Router examples .TP router_clear .TP router_begin note .TP router_chan 0 7 0 15 .TP router_end .TP Will accept only note events from the lower 8 MIDI channels. Regardless of the channel, the synthesizer plays the note on ch 15 (synthchannel=midichannel*0+15) .TP router_begin cc .TP router_chan 0 7 0 15 .TP router_par1 1 1 0 64 .TP router_add Configures the modulation wheel to act as sustain pedal (transforms CC 1 to CC 64 on the lower 8 MIDI channels, routes to ch 15) .SH AUTHORS Peter Hanappe .br Markus Nentwig .br Antoine Schmitt .br Josh Green .br Stephane Letz Please check the AUTHORS and THANKS files for all credits .SH DISCLAIMER SoundFont(R) is a registered trademark of E-mu Systems, Inc. fluidsynth-1.1.9/doc/fluidsynth_arpeggio.c000066400000000000000000000107421322272076000206770ustar00rootroot00000000000000/* FluidSynth Arpeggio - Sequencer API example * * This code is in the public domain. * * To compile: * gcc -o fluidsynth_arpeggio -lfluidsynth fluidsynth_arpeggio.c * * To run: * fluidsynth_arpeggio soundfont [steps [duration]] * * [Pedro Lopez-Cabanillas ] */ #include #include #include fluid_synth_t *synth; fluid_audio_driver_t *audiodriver; fluid_sequencer_t *sequencer; short synth_destination, client_destination; unsigned int time_marker; /* duration of the pattern in ticks. */ unsigned int duration = 1440; /* notes of the arpeggio */ unsigned int notes[] = { 60, 64, 67, 72, 76, 79, 84, 79, 76, 72, 67, 64 }; /* number of notes in one pattern */ unsigned int pattern_size; /* prototype */ void sequencer_callback (unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data); /* schedule a note on message */ void schedule_noteon (int chan, short key, unsigned int ticks) { fluid_event_t *ev = new_fluid_event (); fluid_event_set_source (ev, -1); fluid_event_set_dest (ev, synth_destination); fluid_event_noteon (ev, chan, key, 127); fluid_sequencer_send_at (sequencer, ev, ticks, 1); delete_fluid_event (ev); } /* schedule a note off message */ void schedule_noteoff (int chan, short key, unsigned int ticks) { fluid_event_t *ev = new_fluid_event (); fluid_event_set_source (ev, -1); fluid_event_set_dest (ev, synth_destination); fluid_event_noteoff (ev, chan, key); fluid_sequencer_send_at (sequencer, ev, ticks, 1); delete_fluid_event (ev); } /* schedule a timer event (shall trigger the callback) */ void schedule_timer_event () { fluid_event_t *ev = new_fluid_event (); fluid_event_set_source (ev, -1); fluid_event_set_dest (ev, client_destination); fluid_event_timer (ev, NULL); fluid_sequencer_send_at (sequencer, ev, time_marker, 1); delete_fluid_event (ev); } /* schedule the arpeggio's notes */ void schedule_pattern () { int i, note_time, note_duration; note_time = time_marker; note_duration = duration / pattern_size; for (i = 0; i < pattern_size; ++i) { schedule_noteon (0, notes[i], note_time); note_time += note_duration; schedule_noteoff (0, notes[i], note_time); } time_marker += duration; } void sequencer_callback (unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { schedule_timer_event (); schedule_pattern (); } void usage (char* prog_name) { printf ("Usage: %s soundfont.sf2 [steps [duration]]\n", prog_name); printf ("\t(optional) steps: number of pattern notes, from 2 to %d\n", pattern_size); printf ("\t(optional) duration: of the pattern in ticks, default %d\n", duration); } int main (int argc, char* argv[]) { int n; fluid_settings_t *settings; settings = new_fluid_settings (); pattern_size = sizeof(notes) / sizeof(int); if (argc < 2) { usage (argv[0]); } else { /* create the synth, driver and sequencer instances */ synth = new_fluid_synth (settings); audiodriver = new_fluid_audio_driver (settings, synth); sequencer = new_fluid_sequencer (); /* register the synth with the sequencer */ synth_destination = fluid_sequencer_register_fluidsynth (sequencer, synth); /* register the client name and callback */ client_destination = fluid_sequencer_register_client (sequencer, "arpeggio", sequencer_callback, NULL); /* load a SoundFont */ n = fluid_synth_sfload (synth, argv[1], 1); if (n != -1) { if (argc > 2) { n = atoi (argv[2]); if ((n > 1) && (n <= pattern_size)) pattern_size = n; } if (argc > 3) { n = atoi (argv[3]); if (n > 0) duration = n; } /* get the current time in ticks */ time_marker = fluid_sequencer_get_tick (sequencer); /* schedule patterns */ schedule_pattern (); schedule_timer_event (); schedule_pattern (); /* wait for user input */ printf ("press to stop\n"); n = getchar (); } /* clean and exit */ delete_fluid_sequencer (sequencer); delete_fluid_audio_driver (audiodriver); delete_fluid_synth (synth); } delete_fluid_settings (settings); return 0; } fluidsynth-1.1.9/doc/fluidsynth_fx.c000066400000000000000000000066201322272076000175170ustar00rootroot00000000000000/* FluidSynth FX - An example of using effects with fluidsynth * * This code is in the public domain. * * To compile: * gcc -g -O -o fluidsynth_fx fluidsynth_fx.c -lfluidsynth * * To run * fluidsynth_fx soundfont gain * * [Peter Hanappe] */ #include #include #include /* The structure with the effects data. This example simply applies a * linear gain the to synthesizer output. */ struct fx_data_t { fluid_synth_t* synth; float gain; } fx_data_t; /* This function implements the callback function of the audio driver * (see new_fluid_audio_driver2 below). The data argument is a pointer * to your private data structure. 'len' is the number of samples in * the buffers. 'nin' and 'nout' are the number of input and output * audio buffers. 'in' and 'out' are an array of float buffers with * the samples. The audio driver fills the 'in' buffers the incoming * audio. The 'out' buffers should be filled by your function. The * 'out' buffers will be sent to the audio output of the sound card. * * IMPORTANT NOTE: The API was designed to be generic but none of the * audio drivers currently handles audio input. Either 'nin' will be * zero, or the buffers will be filled with zero samples. */ int fx_function(void* data, int len, int nin, float** in, int nout, float** out) { struct fx_data_t* fx_data = (struct fx_data_t*) data; int i, k; float* out_i; /* Call the synthesizer to fill the output buffers with its * audio output. */ if (fluid_synth_process(fx_data->synth, len, nin, in, nout, out) != 0) { /* Some error occured. Very unlikely to happen, though. */ return -1; } /* Apply your effects here. In this example, the gain is * applied to all the output buffers. */ for (i = 0; i < nout; i++) { out_i = out[i]; for (k = 0; k < len; k++) { out_i[k] *= fx_data->gain; } } return 0; } int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth = NULL; fluid_audio_driver_t* adriver = NULL; int err = 0; struct fx_data_t fx_data; if (argc != 3) { fprintf(stderr, "Usage: fluidsynth_simple [soundfont] [gain]\n"); return 1; } /* Create the settings object. This example uses the default * values for the settings. */ settings = new_fluid_settings(); if (settings == NULL) { fprintf(stderr, "Failed to create the settings\n"); err = 2; goto cleanup; } /* Create the synthesizer */ synth = new_fluid_synth(settings); if (synth == NULL) { fprintf(stderr, "Failed to create the synthesizer\n"); err = 3; goto cleanup; } /* Load the soundfont */ if (fluid_synth_sfload(synth, argv[1], 1) == -1) { fprintf(stderr, "Failed to load the SoundFont\n"); err = 4; goto cleanup; } /* Fill in the data of the effects unit */ fx_data.synth = synth; fx_data.gain = atof(argv[2]); /* Create the audio driver. As soon as the audio driver is * created, the synthesizer can be played. */ adriver = new_fluid_audio_driver2(settings, fx_function, (void*) &fx_data); if (adriver == NULL) { fprintf(stderr, "Failed to create the audio driver\n"); err = 5; goto cleanup; } /* Play a note */ fluid_synth_noteon(synth, 0, 60, 100); printf("Press \"Enter\" to stop: "); fgetc(stdin); printf("done\n"); cleanup: if (adriver) { delete_fluid_audio_driver(adriver); } if (synth) { delete_fluid_synth(synth); } if (settings) { delete_fluid_settings(settings); } return err; } fluidsynth-1.1.9/doc/fluidsynth_metronome.c000066400000000000000000000077531322272076000211170ustar00rootroot00000000000000/* FluidSynth Metronome - Sequencer API example * * This code is in the public domain. * * To compile: * gcc -o fluidsynth_metronome -lfluidsynth fluidsynth_metronome.c * * To run: * fluidsynth_metronome soundfont [beats [tempo]] * * [Pedro Lopez-Cabanillas ] */ #include #include #include fluid_synth_t *synth; fluid_audio_driver_t *audiodriver; fluid_sequencer_t *sequencer; short synth_destination, client_destination; unsigned int time_marker; /* default tempo, beats per minute */ #define TEMPO 120 unsigned int note_duration = 60000 / TEMPO; /* metronome click/bell */ unsigned int weak_note = 33; unsigned int strong_note = 34; /* number of notes in one pattern */ unsigned int pattern_size = 4; /* prototype */ void sequencer_callback (unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data); /* schedule a note on message */ void schedule_noteon (int chan, short key, unsigned int ticks) { fluid_event_t *ev = new_fluid_event (); fluid_event_set_source (ev, -1); fluid_event_set_dest (ev, synth_destination); fluid_event_noteon (ev, chan, key, 127); fluid_sequencer_send_at (sequencer, ev, ticks, 1); delete_fluid_event (ev); } /* schedule a timer event (shall trigger the callback) */ void schedule_timer_event () { fluid_event_t *ev = new_fluid_event (); fluid_event_set_source (ev, -1); fluid_event_set_dest (ev, client_destination); fluid_event_timer (ev, NULL); fluid_sequencer_send_at (sequencer, ev, time_marker, 1); delete_fluid_event (ev); } /* schedule the metronome pattern */ void schedule_pattern () { int i, note_time; note_time = time_marker; for (i = 0; i < pattern_size; ++i) { schedule_noteon (9, i ? weak_note : strong_note, note_time); note_time += note_duration; } time_marker = note_time; } void sequencer_callback (unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { schedule_timer_event (); schedule_pattern (); } void usage (char* prog_name) { printf ("Usage: %s soundfont.sf2 [beats [tempo]]\n", prog_name); printf ("\t(optional) beats: number of pattern beats, default %d\n", pattern_size); printf ("\t(optional) tempo: BPM (Beats Per Minute), default %d\n", TEMPO); } int main (int argc, char *argv[]) { int n; fluid_settings_t *settings; settings = new_fluid_settings (); if (argc < 2) { usage (argv[0]); } else { /* create the synth, driver and sequencer instances */ synth = new_fluid_synth (settings); audiodriver = new_fluid_audio_driver (settings, synth); sequencer = new_fluid_sequencer (); /* register the synth with the sequencer */ synth_destination = fluid_sequencer_register_fluidsynth (sequencer, synth); /* register the client name and callback */ client_destination = fluid_sequencer_register_client (sequencer, "fluidsynth_metronome", sequencer_callback, NULL); /* load a SoundFont */ n = fluid_synth_sfload (synth, argv[1], 1); if (n != -1) { if (argc > 2) { n = atoi (argv[2]); if (n > 0) pattern_size = n; } if (argc > 3) { n = atoi (argv[3]); if (n > 0) note_duration = 60000 / n; } /* get the current time in ticks */ time_marker = fluid_sequencer_get_tick (sequencer); /* schedule patterns */ schedule_pattern (); schedule_timer_event (); schedule_pattern (); /* wait for user input */ printf ("press to stop\n"); n = getchar (); } /* clean and exit */ delete_fluid_sequencer (sequencer); delete_fluid_audio_driver (audiodriver); delete_fluid_synth (synth); } delete_fluid_settings (settings); return 0; } fluidsynth-1.1.9/doc/fluidsynth_register_adriver.c000066400000000000000000000044311322272076000224400ustar00rootroot00000000000000/** * This is a simple C99 program that demonstrates the usage of fluid_audio_driver_register() * * There are 3 calls to fluid_audio_driver_register(), i.e. 3 iterations: * First the alsa driver is registered and created, followed by the jack and portaudio driver. * * The usual usecase would be to call fluid_audio_driver_register() only once providing the audio drivers needed during fluidsynth usage. * If necessary however fluid_audio_driver_register() can be called multiple times as demonstrated here. * Therefore the user must make sure to delete all fluid-instances of any kind before making the call to fluid_audio_driver_register(). * Else the behaviour is undefined and the application is likely to crash. */ #include #include int main() { const char* DRV[] = { "alsa", "jack", "portaudio" }; const char* adrivers[2]; for(int i=0; i= 2 if(res != FLUID_OK) #else if(res == 0) #endif { puts("audio.driver set err"); return -1; } fluid_synth_t* synth = new_fluid_synth(settings); fluid_audio_driver_t* ad = new_fluid_audio_driver(settings, synth); /* * ~~~ Do your daily business here ~~~ */ delete_fluid_audio_driver(ad); delete_fluid_synth(synth); delete_fluid_settings(settings); /* everything cleaned up, fluid_audio_driver_register() can be called again if needed */ } return 0; } fluidsynth-1.1.9/doc/fluidsynth_simple.c000066400000000000000000000032631322272076000203730ustar00rootroot00000000000000/* FluidSynth Simple - An example of using fluidsynth * * This code is in the public domain. * * To compile: * gcc -g -O -o fluidsynth_simple fluidsynth_simple.c -lfluidsynth * * To run * fluidsynth_simple soundfont * * [Peter Hanappe] */ #include #include int main(int argc, char** argv) { fluid_settings_t* settings; fluid_synth_t* synth = NULL; fluid_audio_driver_t* adriver = NULL; int err = 0; if (argc != 2) { fprintf(stderr, "Usage: fluidsynth_simple [soundfont]\n"); return 1; } /* Create the settings object. This example uses the default * values for the settings. */ settings = new_fluid_settings(); if (settings == NULL) { fprintf(stderr, "Failed to create the settings\n"); err = 2; goto cleanup; } /* Create the synthesizer */ synth = new_fluid_synth(settings); if (synth == NULL) { fprintf(stderr, "Failed to create the synthesizer\n"); err = 3; goto cleanup; } /* Load the soundfont */ if (fluid_synth_sfload(synth, argv[1], 1) == -1) { fprintf(stderr, "Failed to load the SoundFont\n"); err = 4; goto cleanup; } /* Create the audio driver. As soon as the audio driver is * created, the synthesizer can be played. */ adriver = new_fluid_audio_driver(settings, synth); if (adriver == NULL) { fprintf(stderr, "Failed to create the audio driver\n"); err = 5; goto cleanup; } /* Play a note */ fluid_synth_noteon(synth, 0, 60, 100); printf("Press \"Enter\" to stop: "); fgetc(stdin); printf("done\n"); cleanup: if (adriver) { delete_fluid_audio_driver(adriver); } if (synth) { delete_fluid_synth(synth); } if (settings) { delete_fluid_settings(settings); } return err; } fluidsynth-1.1.9/doc/xtrafluid.txt000066400000000000000000000713401322272076000172310ustar00rootroot00000000000000Xtra iiwu Version 2.7 October 2002 --- Introduction The iiwu Xtra integrates the iiwusynth synthesizer into Director. The iiwusynth software synthesizer has been designed by Peter Hanappe, and is available under the LGPL licence. It emulates in software the SoundFont 2.01 Specifications (http://www.soundfont.com) designed by Creative Labs (SoundBlaster maker). It is basically a small, fast and robust wavetable synthesizer, with a MIDI-like interface and integrated sequencer. For more information on the iiwu synthesizer : http://www.iiwu.org/iiwusynth The iiwu Xtra has been developped as part of the "infiniteCD Author" project managed by Antoine Schmitt and Hyptique, with support from the PRIAM funds from the French government. For more information on the infiniteCD Author project : http://www.infiniteCD.org/ http://www.hyptique.com/ http://www.gratin.org/as/ --- Licencing The Version 2.7 of the iiwu Xtra is beta. Do not distribute without permission. Eventually, the Xtra will be part of the iiwu project, and thus will conform to the LGPL licence. --- Release notes Version 2.0.1 : first beta version - October 2002 Version 2.0.1 : first beta version - October 2002 Version 2.5 : second beta - November 2002 Version 2.6 : third beta - November 2002 Version 2.7 : fourth beta - November 2002 --- Technical Requirements OS: - Macintosh >= MacOS8.6.1 < MacOSX - Windows >= 95 Director 8.5.1 Sound Card --- Installation Drop the folder "iiwuXtraFolder" into the Xtras folder of Director. ----------------- --- Documentation ----------------- - Calls All function calls are done using lingo's call syntax: object.func(args...) or func(object, args...) Where object is the instance of the Xtra created with the 'new' method. All functions return either a value, which can be 0 (zero), meaning that the function executed without errors, either a negative number, meaning that an error occurred. If an error occured, the function 'getError' returns a human-readable string describing the error. - Instances Many instances of the Xtra may coexist at the same time. Instances are created with the 'new' function, and deleted by assigning the lingo variable to VOID, as usual for Xtra instances. - SoundFont file, stack, presets A SoundFont bank is typically stored in a file, called a SoundFont file, of extension .sf2. In the following documentation, we will refer to the SoundFont file or SoundFont bank by the term 'SoundFont'. Please note that the term 'bank' will always refer to a MIDI bank, not a SoundFont bank. A SoundFont has a name and contains 'presets'. A preset represents a way to play a sound, as a combination of sample data and parameters on how to play it. The preset is the fundamental sound object of the SoundFont format. A preset has a name, and is defined uniquely by a MIDI bank number and a preset number in the bank. The MIDI bank number ranges from 0 to 16383 and the preset number from 0 to 127. Thus a SoundFont file may contain up to 128x16384=2097152 presets. SoundFont files may be loaded in the synthesizer, using the 'loadSoundFont' function, thus making its presets available for playing. A SoundFont may be unloaded using the 'unloadSoundFont' function. If more than one SoundFont file is loaded, the SoundFonts are stacked in the synthesizer : when a preset is requested by the 'programChange' function, it is looked up in all succcessive SoundFonts, from first to last, until the preset with the right preset number and bank number is found. The SoundFont stack may be examined but not changed : the SoundFonts are stacked in inverse loading order: the last loaded SoundFont is the first searched. A special SoundFont is maintained by the synthesizer, corresponding to user-defined presets, built from user-provided samples. These presets are defined using the 'loadSample' and 'loadSoundMember' functions. This user SoundFont is inserted in the SoundFont stack at the first call of one of these functions. All subsequent calls to one of these functions will insert the created preset in the same user SoundFont. - Channels According to the MIDI format and protocol, the synthesizer has a fixed number of channels. This is the maximum number of presets that may be playing at the same time. At all times, one given channel plays at most one preset (but can play many notes from this preset). A preset is associated to a channel using the 'programChange' function. The number of channels of the synthesizer is defined at init time using the 'new' function. A given preset may be associated to many channels. ----------------- -- Initialization ----------------- new(Xtra"iiwusynth") new(Xtra"iiwusynth", plist initParams) ----------------- ex : iiwuObj = new(Xtra "iiwusynth", [#channels: 32]) Creates a new instance of the iiwu Xtra. The optional initParams argument is a propert-list of key-value paris defining initialization parameters. Only one key is currently defined: - #channels : int > 0 : the number of channels allocated by the synthesizer. If omitted, the default number of channels is set to 64. Version 2.0.1 : This is not implemented yet : the number of channels is always 64. To destroy the synthesizer, simply set its variable to VOID. Returns an error code (unable to create, hardware error, bad arguments). getChannelsCount(object me) ----------------- ex : nbchan = getChannelsCount(iiwuObj) Returns the number of channels of the synthesizer. By default, there are 64 channels. Returns an error code (no synth). getMasterGain(object me) ----------------- ex : gain = getMasterGain(iiwuObj) Returns the master gain of the synthesizer. By default the gain is 1.0 (full volume). The gain is between 0.0 and 2.0. Returns an error code (no synth). setMasterGain(object me, float gain) ----------------- ex : setMasterGain(iiwuObj, 0.5) Sets the master gain of the synthesizer. By default the gain is 1.0. The gain should between 0.0 and 2.0. Gains superior to 1.0 (full volume, no attenuation) should be handled carefully, as distortion may happen. Returns an error code (no synth). ----------------- -- Reverb/Chorus ----------------- setReverb(object me) setReverb(object me, int onOrOff) ----------------- ex : setReverb(iiwuObj) ex : setReverb(iiwuObj, FALSE) Sets the reverb module of the synthesizer on or off. Returns an error code (no synth, bad arguments). getReverb(object me) ----------------- ex : revOn = getReverb(iiwuObj) Return 1 of the reverb module is on (default), 0 if it is off. Returns an error code (no synth, bad arguments). setReverbProp(object me, symbol prop, float value) ----------------- ex : setReverbProp(iiwuObj, #roomsize, 0.9) ex : setReverbProp(iiwuObj, #level, 0.2) Sets the value of a property of the reverb module. Accepted properties are: - #level : the level of the reverb (0.0 = no reverb, 1.0 = full reverb). - #roomsize : the size of the room. 0.0 means a very small room, 1.0 means outerspace. - #width : the spatial width of the reverb. 0.0 means a very narrow reverb, 1.0 a wide one. - #damping : how much power is lost at each reverberation. 1.0 : mostly lost, 0.0 : full reverberation. All values are between 0.0 and 1.0. Returns an error code (no synth, bad arguments). getReverbProp(object me, symbol prop) ----------------- ex : roomsiz = getReverbProp(iiwuObj, #roomsize) ex : damping = getReverbProp(iiwuObj, #damping) Returns the float value of the given property of the reverb module. Accepted properties are listed above. Returns an error code (no synth, bad arguments). setChorus(object me) setChorus(object me, int onOrOff) ----------------- ex : setChorus(iiwuObj) ex : setChorus(iiwuObj, FALSE) Sets the chorus module of the synthesizer on or off. Returns an error code (no synth, bad arguments). getChorus(object me) ----------------- ex : chorusOn = getChorus(iiwuObj) Return 1 of the chorus module is on (default), 0 if it is off. Returns an error code (no synth, bad arguments). setChorusProp(object me, symbol prop, float value) ----------------- ex : setChorusProp(iiwuObj, #number, 3) ex : setChorusProp(iiwuObj, #level, 0.2) Sets the value of a property of the chorus module. Accepted properties are: - #level : the level of the chorus. Min is 0.0, maximum accepted is 10.0. - #number : the number of secondary voices. Maximum is 99. - #modulation : the amplitude of the frequency modulation, in Hz. Min is 0.29, max is 5.0. - #delay : the maximum delay between secondary voices, in ms. Min is 0, maximum is 100.0. Returns an error code (no synth, bad arguments). getChorusProp(object me, symbol prop) ----------------- ex : number = getChorusProp(iiwuObj, #number) ex : modulation = getChorusProp(iiwuObj, #modulation) Returns the float value of the given property of the chorus module. Accepted properties are listed above. Returns an error code (no synth, bad arguments). ----------------- -- Sound Data ----------------- loadSoundFont(object me, string filePath) ----------------- ex : soundFontID = loadSoundFont(iiwuObj, the moviePath & "MySoundFonts.sf2") Loads a SoundFonts file in the synthesizer and places it at the top of the SoundFont stack : it will be the first searched when looking for a preset. The path is absolute, and should be expressed with the local file system conventions. The loaded soundFont will be of type #file. Returns an ID, an interger uniquely idenfiying the SoundFont in the stack, or an error code (no synth, no file, bad file format, not enough memory). createSoundFont(object me) createSoundFont(object me, string name) ----------------- ex : soundFontID = createSoundFont(iiwuObj) ex : soundFontID = createSoundFont(iiwuObj, "mysoundfont in memory") Creates an empty SoundFont in memory (type #ram) and places it at the top of the SoundFont stack : it will be the first searched when looking for a preset. The returned soundFontID can be used in the #soundFont property of the loadSampleFile or loadSampleMember functions. The created soundFont can be removed from memory, using the unloadSoundFont function. The 'name' argument is optional and defaults to EMPTY (the empty string). Returns an ID, an interger uniquely idenfiying the SoundFont in the stack, or an error code (no synth, no file, bad file format, not enough memory). unloadSoundFont(object me) unloadSoundFont(object me, int soundFontID) ----------------- ex : unloadSoundFont(iiwuObj, 1) ex : unloadSoundFont(iiwuObj) Unloads the SoundFont of the given ID of the SoundFont stack. The soundFontID argument is optional, and defaults to the first searched SoundFont (the last loaded). All types of soundFonts can be unloaded from memory. Returns an error code (no synth, bad ID). reloadSoundFont(object me) reloadSoundFont(object me, int soundFontID) ----------------- ex : reloadSoundFont(iiwuObj, 1) ex : reloadSoundFont(iiwuObj) Version 2.0.1 : Not Yet Implemented. Reloads the SoundFont of the given ID of the SoundFont stack. The soundFontID argument is optional, and defaults to the first searched SoundFont (the last loaded). This is useful when a soundFont is known to have changed outside the synthesizer. Only SoundFonts of type #file can be reloaded. Returns an error code (no synth, bad ID). getSoundFontsStack(object me) ----------------- ex : aList = getSoundFontsStack(iiwuObj) Returns a lingo list containing all the IDs of the SoundFonts in the stack, sorted from first searched to last searched (last loded to first loaded). May return an error code (no synth). getSoundFontInfo(object me) getSoundFontInfo(object me, int soundFontID) ----------------- ex : nam = getSoundFontInfo(iiwuObj) Returns information about the SoundFont of the given soundFontID of the SoundFont stack. The soundFontID argument is optional, and defaults to the first searched SoundFont (the last loaded). The information is returned in the form of a lingo property-list, containing key-value pairs describing the SoundFont: its name, and info about its presets (name, preset number and bank number): [ #name:"SoundFontName", #type:#file or #ram, #presets: [ [#name:"preset1Name", #bank:preset1bankNb, #number:preset1Number], [#name:"preset2Name", #bank:preset2bankNb, #number:preset2Number], ... ] ] (More information may be added in future versions of the Xtra) Can return an error code (no synth, bad soundFontID). loadSampleFile(object me, string filePath, plist presetInfo) loadSampleFile(object me, string filePath, int number) ----------------- Version 2.0.1 : Not Yet Implemented. ex : loadSampleFile(iiwuObj, the moviePath&"MidPiano.aiff", [#name:"piano", #bank:0, #number:1, #rootKey:60, #keyrange:[50, 70]]) ex : loadSampleFile(iiwuObj, the moviePath&"Piano.aiff", [#number:2]) ex : loadSampleFile(iiwuObj, the moviePath&"Piano.aiff", 2) Loads a sample file from filePath and creates a preset from it. This is a simple way of creating a preset from a specific sample. The 'filePath' is absolute, and should be expressed with the local file system conventions. Accepted sample formats are : wav, and aiff. The preset is added to a ram (memory) SoundFont (type #ram). This SoundFont is either specified using the #soundFont property (see below), either the highest soundfont in the stack is used if it is a ram soundfont, either it is created on the fly in memory, in which case it is placed at the top of the SoundFont stack of the synthesizer. The preset is created according to the 'presetInfo' lingo property-list. This plist is a combination of key-value pairs describing the preset. The following keys are taken into account: - #number : integer, mandatory. Between 0 and 127 : the preset number in the bank If this preset number (and bank if set) was already in use, the sample is added to the already preset samples of this preset. This is a handy way to define a keyRange for a preset. - #bank : integer, optional (default = 0). Between 0 and 16383: the number of the MIDI bank that will be assigned to the preset. - #soundFont : integer, optional. Specifies the soundFontID of the SoundFont in which the preset will be created. If specified, it should be a soundFont of type #ram. If not specified, the first soundfont on the stack will be used if it is of type #ram. If not, a new SoundFont is created on the fly. - #name : string : the name of the preset - optional - #rootKey : int between 0 and 127 : the key at which the sample will be played 'as is' - optional : if omitted, 60 (middle-C) is used. - #keyRangeStart : integer, optional (default = 0). Defines the first of two keys between which the sample should be played (the comparison is inclusive). If a noteon asks for a key outside this range, the sample is not played. If omitted, the whole range [0, 127] is used. - #keyRangeEnd : integer, optional (default = 127). See keyRangeStart. - #loop : boolean, optional. Specifies that the sample should be played as a loop. Default is FALSE. - #attack : float, optional (default = 0.0). Duration, in milliseconds of the attack phase of the sound, i.e. the time for the sample to reach peak volume after a noteon is issued. - #decay : float, optional (default = 0.0). Duration, in milliseconds of the decay phase of the sound, i.e. the time for the sample to reach sustain volume after the attack. - #sustainlevel : float, optional (default = 0.0). Level, in dB relative to the peak level, of the sustain level. A sustainlevel of 0 means the same level than the peak level. A sustainlevel of 10 mean peak-10dB, which is very low. A sustainlevel of 100dB conventionnaly means full attenuation. - #release : float, optional (default = 0.0). Duration, in milliseconds of the release phase of the sound, i.e. the time for the sample to reach silence after a noteoff. Note that in one preset, two different samples may be used for two different key ranges. example : loadSampleFile(iiwuObj, the moviePath&"PianoLo.aiff", [#name:"piano", #bank:0, #number:1, #rootKey:30, #keyrange:[0, 60]]) loadSampleFile(iiwuObj, the moviePath&"PianoHi.aiff", [#name:"piano", #bank:0, #number:1, #rootKey:90, #keyrange:[61, 127]]) This example defines two samples for two ranges of the same preset. A preset may not be unloaded ; only the whole user SoundFont may be unloaded from the SoundFont stack, using the 'unloadSoundFont' function. Returns the soundFontID of the SoundFont to which the sample has been added, or an error code (no synth, no file, bad file format, not enough memory, bad bank number, bad preset number, bad key). loadSampleMember(object me, obj member, plist presetInfo) ----------------- ex : loadSampleMember(iiwuObj, member(3), [#name:"piano", #bank:0, #number:1]) Version 2.0.1 : Not Yet Implemented. Loads a sample from the given Director sound cast member and creates a preset from it. The preset is created according to the 'presetInfo' lingo property-list. (see above for details on the presetInfo format). Returns the soundFontID of the SoundFont to which the sample has been added, or an error code (no synth, no member, bad member format, not enough memory, bad bank number, bad preset number, bad key, not a user soundFont). ----------------- -- Event sequencer ----------------- Introduction ----------------- - Sequencer The API functions for playing music is derived from the MIDI protocol. This protocol defines the use of a number of presets for every synthesizer object. Application communicate with a preset in a synthesizer over a channel. The communication is event based (MIDI events). The API of the music functions (discussed below) can be thought of as sending an event over a channel to a preset. The music API functions includes the optional use of a sequencer. A sequencer is an object that assures the delivery of an event at a future time. To use the sequencer object, the events have to specify a time property. Two property key values can be used: - #date: specifies the time on an absolute time axis (the creation time of the synthesizer is used as time zero), - #delay: specifies the time relative to the sequencer's current time. The time is measured in 'ticks'. A 'tick' is an arbitrary unit that can be set by the application. See the sequencer API below. - Event destinations The API includes the use of several synthesizer objects. The property key '#dest' indicates the destination of the event. The destination value is the name of the synthesizer object. If no destination is specified, the event will be sent to the first known destination which is the default synthesizer. - Event sources Events can also specify an event source. This is useful when the sender of an event wants to cancel some time the event later. To use this feature, the event simply contains the key '#source' with a string as value. - Callbacks Lingo objects can ask the sequencer to schedule a callback function at a precise time. The callback is the name of a Lingo handler. The movie will have to call the poll function of the xtra regularly to receive the callbacks. - Sequencer API The following functions address the sequencer directly. setTimeUnit(object me, float ticksPerSecond) ----------------- ex. setTimeUnit(iiwuObj, 10.0) -- 1 tick equals 100 millisecond Sets the tick unit of the sequencer. ticksPerSecond is a float > 0. Returns a error indication (bad initialization). getTimeUnit(object me) ----------------- ex. getTimeUnit(iiwuObj) Returns the tick unit of the sequencer or an error indication (bad initialization). By default, the tick value is 1000.0 (1 tick = 1 millisecond). getTime(object me) ----------------- ex. getTime(iiwuObj) Returns the time in tick from the start of the synthesizer, in tick units of the sequencer or an error indication (bad initialization). getDestinations(object me) ----------------- ex. getDestinations(iiwuObj) Returns a list with all the names of possible event destinations. removeEvents(object me, plist filter) ----------------- ex. removeEvents(iiwuObj, [#dest: "iiwusynth", #source: "DrumMachine"]) ex. removeEvents(iiwuObj, [#source: "DrumMachine", #type: #note]) Removes events that are queued for sending in the sequencer. The function takes property list as argument. The property list defines the events that should be filtered. Currently, any of the three following keys can be used: - #source: remove the events for the specified source (if not specified, removes all source) - #dest: remove the events sent by the specified destination (if not specified, removes all destination) - #type: remove the events according to this event type (if not specified, removes all events) Possible event types are: - #note - #noteon - #noteoff - #allsoundsoff - #allnotesoff - #programchange - #controlchange: includes all controlChange events (pitchbend, modulation, sustain, pan, volume, reverbsend, chorussend) - #pitchbend - #modulation - #sustain - #pan - #volume - #reverbsend - #chorussend - #callback Returns an error code (no sequencer, invalid argument). scheduleCallback(object me, plist callbackInfo) ----------------- ex. scheduleCallback(iiwuObj, [#delay: 1200, #handler:"updateDrumMachine", #args: [1,2,3]]) ex. scheduleCallback(iiwuObj, [#delay: 1200, #source:"drumMachine", #handler:"updateDrumMachine"]) Schedules a callback event. When the event is reached, the lingo handler defined in the 'callbackInfo' propertylist is called back. Note that the callback happens during idle time. The 'callbackInfo' propertylist has the following possible properties: - #handler, string, mandatory. It is the lingo handler that will be called. - #args, a lingo list, optional. This list contains the arguments of the handler. The scheduleCallback function retains a pointer to this list but does not copy it, according to the lingo tradition. The lingo eventually executed is equivalent to "handler(args[1], args[2], ...)". If the first argument is a child object, the corresponding handler will be called on that object. - #delay/#date : integer, optional. Specifies the time of the callback. Defaults to #delay:0 - #source : optional. Specifies the source. Useful for removing callbacks. - #dest : optional. Specifies the destination. To remove a scheduled callback, use removeEvents: ex : removeEvents(iiwuObj, [#source: "drumMachine", #type: #callback]) ex : removeEvents(iiwuObj, [#type: #callback]) Returns an error code (no sequencer, invalid argument). ----------------- -- Playing music ----------------- Most event functions accept an optional property list that describes the sequencing of the event. This is indicated by the 'seq' argument in the functions below. The following properties are recognized: - #date: the absolute time of the event - #delay: the time of the event relative to current time - #source: the event source - #dest: the event destination #date and #delay should not be specified together. programChange(object me, int channel, plist presetInfo, plist seq) programChange(object me, int channel, int presetNumber, plist seq) ----------------- ex : programChange(iiwuObj, 1, [#bank:1, #number:3]) ex : programChange(iiwuObj, 1, 3) Assigns the given preset of the SoundFont stack to the given channel of the synthethizer. The preset may be defined using a property-list defining its MIDI bank number and its preset number, using specific keys: - #bank : int between 0 and 16383: the number of the MIDI bank of the preset - optional : if #bank is not present, 0 is the default. - #number : int between 0 and 127 : the preset number in the bank - mandatory. The preset may also be defined by a unique int, wich is treated as the preset number, assuming that the MIDI bank is 0 (zero). In all cases, the preset is looked up in the SoundFont stack, starting from the first SoundFont up the stack, until a corresponding preset is found. The found preset is assigned to the channel. The maximum number of channels are defined using the 'new' method. If a preset was previoulsy assigned to that channel, it is forgotten, but all noteon finish normally. Returns an error code (no synth, no SoundFonts, bad channel, bad preset definition). getProgram(object me, int channel) ----------------- ex : getProgram(iiwuObj, 0) Returns information about the preset assigned to the channel. The information is of the form : [#name:"presetName", #bank:presetBankNb, #number:presetNumber] The maximum number of channels are defined using the 'new' method. May return an error code (no synth, no preset assigned, bad channel number) note(object me, int channel, int key, float vel, int dur, plist seq) ----------------- ex : note(iiwuObj, 1, 38, 1.0, 1000) Plays a note, using the specified 'channel' and 'key', with the specified velocity (strength). The duration 'dur' of the note is specified in ticks. 0.0 <= vel <= 1.0 (automatically limited if smaller or greater) 0 <= key <= 127 The maximum number of channels are defined using the 'new' method. Returns an error code (no synth, no SoundFonts, bad channel, no preset assigned, bad key, bad vel). noteon(object me, int channel, int key, float vel, plist seq) ----------------- ex : noteon(iiwuObj, 1, 38, 1.0) Starts a note, using the specified 'channel' and 'key', with the specified velocity (strength). 0.0 <= vel <= 1.0 (automatically limited if smaller or greater) 0 <= key <= 127 The maximum number of channels are defined using the 'new' method. Returns an error code (no synth, no SoundFonts, bad channel, no preset assigned, bad key, bad vel). noteoff(object me, int channel, int key, plist seq) ----------------- ex : noteoff(iiwuObj, 1) Stops all playing notes on the given 'channel' and the given 'key'. The maximum number of channels are defined using the 'new' method. Returns an error code (no synth, no SoundFonts, bad channel, no preset assigned). controlChange(object me, int channel, plist controlParams, plist seq) ----------------- ex: controlChange(obj, 1, [#pitchbend: 1.0, #sustain:1]) controlChange(obj, 1, [#pan: -0.2, #sustain:1, #volume:0.5, #reverbsend:1.0]) This function allow to change some control parameters of the given channel. The effect is immediate and allows for continuous modification of a sound. The 'controlParams' is a lingo property-list of key-value pairs, with the key describing the control parameter to affect, and the value specifying the amount of the change. For some keys, the value is not taken into account. The list of currently implemented keys is the following : - #pitchbend: float between -1.0 an 1.0: sets the pitchbend level 0.0 means no pitchbend. The pitchrange may be configured in the preset (for loaded SoundFonts), but the default range corresponds to 4 steps (one step = one semi-tone), so a value of -1.0 means two steps down, and a value of 1.0 means two steps up. - #pan: float between -1.0 an 1.0: sets the pan level. -1.0 is all sound on left channel, 1.0 is all sound on right channel 0.0 is sound in center. - #volume: float between 0.0 an 1.0: sets the volume level. 0.0 is silence, 1.0 is full volume. - #reverbsend: float between 0.0 an 1.0. Sets the volume of the auxiliary output send to the reverb module. 0.0 is no signal, 1.0 is full volume. - #chorussend: float between 0.0 an 1.0. Sets the volume of the auxiliary output send to the chorus module. 0.0 is no signal, 1.0 is full volume. - #sustain: int = 0 or 1: sets or removes the sustain from the channel. - #modulation: float between 0.0 an 1.0. Amplitude modulation. Vibrato. Returns an error code (no synth, no SoundFonts, bad channel, no preset assigned, unknown controlKey, bad controlValue). getControl(object me, int channel, symbol ctrl) ----------------- ex. getControl(iiwuObj, 1, #volume) Returns the value of a controller. getControls(object me, int channel) ----------------- ex. getControls(iiwuObj, 1) Returns a property list with the values of all controllers in the form: [#pan: 0.4, #sustain:1, #volume:0.8, #reverbsend:1.0, ...] allsoundsoff(object me, int channel, plist seq) ----------------- Instantly stops all sound on the channel. The optional argument is the sequencer information. Returns an error code (no sequencer). allnotesoff(object me, int channel, plist seq) ----------------- Sends a noteoff to all currently playing notes on the channel. The optional argument is the sequencer information. Returns an error code (no sequencer). --------------------------- -- Debug and maintenance --------------------------- debug(object me, string logFile) ----------------- ex : debug(iiwuObj, the moviePath&"logfile") Sets the debug mode of the Xtra. If a logFile is provided, debug is set. If VOID is provided, debug is turned off. If debug is on, a log of all actions and errors is written in the logFile. Returns an error code (cannot open/create log file). getError(object me) ----------------- ex : lastError = getError(iiwuObj) returns a human readable string describing the last error that occured in the Xtra. getCPUUsage(object me) ----------------- ex : aPercent = getCPUusage(iiwuObj) returns a float representing the estimation of the percentage of CPU used by the synthesizer, or an error code (no synth). ------------------------------- ------------------------------- ------------------------------- fluidsynth-1.1.9/fluidsynth.anjuta000066400000000000000000000017301322272076000173120ustar00rootroot00000000000000 fluidsynth-1.1.9/fluidsynth.pc.in000066400000000000000000000003241322272076000170350ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: FluidSynth Description: Software SoundFont synth Version: @VERSION@ Libs: -L${libdir} -lfluidsynth Cflags: -I${includedir} fluidsynth-1.1.9/fluidsynth.spec.in000066400000000000000000000045011322272076000173660ustar00rootroot00000000000000 %define name @PACKAGE@ %define version @VERSION@ %define release 1 %define prefix /usr Summary: A real-time software synthesizer based on SoundFont 2 specifications. Name: %{name} Version: %{version} Release: %{release} Prefix: %{prefix} Copyright: LGPL Group: Sound Source: http://savannah.nongnu.org/download/fluid/stable.pkg/%{version}/fluidsynth-%{version}.tar.gz URL: http://www.fluidsynth.org/ BuildRoot: /var/tmp/%{name}-%{version} %description FluidSynth is a real-time software synthesizer based on the SoundFont 2 specifications. FluidSynth can read MIDI events from MIDI input devices and render them to audio devices using SoundFont files to define the instrument sounds. It can also play MIDI files and supports real time effect control via SoundFont modulators and MIDI controls. FluidSynth can be interfaced to other programs in different ways, including linking as a shared library. %package devel Summary: Libraries and includes to build FluidSynth into other applications Group: Development/Libraries %description devel FluidSynth is a real-time software synthesizer based on the SoundFont 2 specifications. FluidSynth can read MIDI events from MIDI input devices and render them to audio devices using SoundFont files to define the instrument sounds. It can also play MIDI files and supports real time effect control via SoundFont modulators and MIDI controls. FluidSynth can be interfaced to other programs in different ways, including linking as a shared library. This package contains libraries and includes for building applications with FluidSynth support. %prep %setup %build ./configure --prefix=%{prefix} make %install if [ -d $RPM_BUILD_ROOT ]; then rm -rf $RPM_BUILD_ROOT; fi mkdir -p $RPM_BUILD_ROOT make prefix=$RPM_BUILD_ROOT%{prefix} install %clean if [ -d $RPM_BUILD_ROOT ]; then rm -rf $RPM_BUILD_ROOT; fi %files %defattr(-,root,root) %doc AUTHORS COPYING ChangeLog NEWS README TODO %{prefix}/bin/fluidsynth %{prefix}/lib/libfluidsynth.so* %{prefix}/man/man1/* %files devel %defattr(-,root,root) %doc doc/example.c doc/example.sf2 doc/api doc/html/* %{prefix}/lib/libfluidsynth.a %{prefix}/lib/libfluidsynth.la %{prefix}/lib/pkgconfig/fluidsynth.pc %{prefix}/include/fluidsynth.h %{prefix}/include/fluidsynth %changelog * Mon Aug 25 2003 Josh Green - Created initial fluidsynth.spec.in fluidsynth-1.1.9/include/000077500000000000000000000000001322272076000153375ustar00rootroot00000000000000fluidsynth-1.1.9/include/CMakeLists.txt000066400000000000000000000020211322272076000200720ustar00rootroot00000000000000# FluidSynth - A Software Synthesize # # Copyright (C) 2003-2010 Peter Hanappe and others. # # 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 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307, USA # CMake based build system. Pedro Lopez-Cabanillas add_subdirectory ( fluidsynth ) IF (NOT MACOSX_FRAMEWORK ) install ( FILES fluidsynth.h DESTINATION ${INCLUDE_INSTALL_DIR} ) ENDIF (NOT MACOSX_FRAMEWORK ) fluidsynth-1.1.9/include/Makefile.am000066400000000000000000000001231322272076000173670ustar00rootroot00000000000000 SUBDIRS = fluidsynth include_HEADERS = fluidsynth.h EXTRA_DIST = CMakeLists.txt fluidsynth-1.1.9/include/fluidsynth.h000066400000000000000000000063701322272076000177070ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_H #define _FLUIDSYNTH_H #include #ifdef __cplusplus extern "C" { #endif #if defined(WIN32) #if defined(FLUIDSYNTH_DLL_EXPORTS) #define FLUIDSYNTH_API __declspec(dllexport) #elif defined(FLUIDSYNTH_NOT_A_DLL) #define FLUIDSYNTH_API #else #define FLUIDSYNTH_API __declspec(dllimport) #endif #elif defined(MACOS9) #define FLUIDSYNTH_API __declspec(export) #elif defined(__GNUC__) #define FLUIDSYNTH_API __attribute__ ((visibility ("default"))) #else #define FLUIDSYNTH_API #endif #if defined(__GNUC__) || defined(__clang__) # define FLUID_DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) && _MSC_VER > 1200 # define FLUID_DEPRECATED __declspec(deprecated) #else # define FLUID_DEPRECATED #endif /** * @file fluidsynth.h * @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files. * * This is the header of the fluidsynth library and contains the * synthesizer's public API. * * Depending on how you want to use or extend the synthesizer you * will need different API functions. You probably do not need all * of them. Here is what you might want to do: * * - Embedded synthesizer: create a new synthesizer and send MIDI * events to it. The sound goes directly to the audio output of * your system. * * - Plugin synthesizer: create a synthesizer and send MIDI events * but pull the audio back into your application. * * - SoundFont plugin: create a new type of "SoundFont" and allow * the synthesizer to load your type of SoundFonts. * * - MIDI input: Create a MIDI handler to read the MIDI input on your * machine and send the MIDI events directly to the synthesizer. * * - MIDI files: Open MIDI files and send the MIDI events to the * synthesizer. * * - Command lines: You can send textual commands to the synthesizer. * * SoundFont(R) is a registered trademark of E-mu Systems, Inc. */ #include "fluidsynth/types.h" #include "fluidsynth/settings.h" #include "fluidsynth/synth.h" #include "fluidsynth/shell.h" #include "fluidsynth/sfont.h" #include "fluidsynth/ramsfont.h" #include "fluidsynth/audio.h" #include "fluidsynth/event.h" #include "fluidsynth/midi.h" #include "fluidsynth/seq.h" #include "fluidsynth/seqbind.h" #include "fluidsynth/log.h" #include "fluidsynth/misc.h" #include "fluidsynth/mod.h" #include "fluidsynth/gen.h" #include "fluidsynth/voice.h" #include "fluidsynth/version.h" #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_H */ fluidsynth-1.1.9/include/fluidsynth/000077500000000000000000000000001322272076000175305ustar00rootroot00000000000000fluidsynth-1.1.9/include/fluidsynth/CMakeLists.txt000066400000000000000000000026471322272076000223010ustar00rootroot00000000000000# FluidSynth - A Software Synthesize # # Copyright (C) 2003-2010 Peter Hanappe and others. # # 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 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307, USA # CMake based build system. Pedro Lopez-Cabanillas configure_file ( ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h ) if ( NOT MACOSX_FRAMEWORK ) set ( include_HEADERS audio.h event.h gen.h log.h midi.h misc.h mod.h ramsfont.h seq.h seqbind.h settings.h sfont.h shell.h synth.h types.h voice.h ) install ( FILES ${include_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/version.h DESTINATION ${INCLUDE_INSTALL_DIR}/fluidsynth ) endif ( NOT MACOSX_FRAMEWORK ) fluidsynth-1.1.9/include/fluidsynth/Makefile.am000066400000000000000000000005601322272076000215650ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in pkginclude_HEADERS = types.h \ settings.h \ synth.h \ event.h \ seq.h \ seqbind.h \ shell.h \ sfont.h \ ramsfont.h \ audio.h \ midi.h \ log.h \ misc.h \ version.h \ voice.h \ mod.h \ gen.h EXTRA_DIST = version.h.in CMakeLists.txt DISTCLEANFILES = version.h fluidsynth-1.1.9/include/fluidsynth/audio.h000066400000000000000000000056351322272076000210130ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_AUDIO_H #define _FLUIDSYNTH_AUDIO_H #ifdef __cplusplus extern "C" { #endif /** * @file audio.h * @brief Functions for audio driver output. * @defgroup AudioFunctions Functions for audio output * * Defines functions for creating audio driver output. Use * new_fluid_audio_driver() to create a new audio driver for a given synth * and configuration settings. The function new_fluid_audio_driver2() can be * used if custom audio processing is desired before the audio is sent to the * audio driver (although it is not as efficient). * * @sa @ref CreatingAudioDriver */ /** * Callback function type used with new_fluid_audio_driver2() to allow for * custom user audio processing before the audio is sent to the driver. This * function is responsible for rendering the audio to the buffers. * @param data The user data parameter as passed to new_fluid_audio_driver2(). * @param len Length of the audio in frames. * @param nin Count of buffers in 'in' * @param in Not used currently * @param nout Count of arrays in 'out' (i.e., channel count) * @param out Output buffers, one for each channel * @return Should return 0 on success, non-zero if an error occured. */ typedef int (*fluid_audio_func_t)(void* data, int len, int nin, float** in, int nout, float** out); FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t* driver); FLUIDSYNTH_API fluid_file_renderer_t *new_fluid_file_renderer(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t* dev); FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t* dev); FLUIDSYNTH_API int fluid_file_set_encoding_quality(fluid_file_renderer_t* dev, double q); FLUIDSYNTH_API int fluid_audio_driver_register(const char** adrivers); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_AUDIO_H */ fluidsynth-1.1.9/include/fluidsynth/event.h000066400000000000000000000146171322272076000210330ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_EVENT_H #define _FLUIDSYNTH_EVENT_H #ifdef __cplusplus extern "C" { #endif /** * @file event.h * @brief Sequencer event functions and defines. * * Functions and constants for creating/processing sequencer events. */ /** * Sequencer event type enumeration. */ enum fluid_seq_event_type { FLUID_SEQ_NOTE = 0, /**< Note event with duration */ FLUID_SEQ_NOTEON, /**< Note on event */ FLUID_SEQ_NOTEOFF, /**< Note off event */ FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */ FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */ FLUID_SEQ_BANKSELECT, /**< Bank select message */ FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ FLUID_SEQ_PROGRAMSELECT, /**< Program select message (DOCME) */ FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was mispelled previously */ FLUID_SEQ_MODULATION, /**< Modulation controller event */ FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ FLUID_SEQ_PAN, /**< Stereo pan set event */ FLUID_SEQ_VOLUME, /**< Volume set event */ FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ FLUID_SEQ_TIMER, /**< Timer event (useful for giving a callback at a certain time) */ FLUID_SEQ_ANYCONTROLCHANGE, /**< Any control change message (only internally used for remove_events) */ FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ FLUID_SEQ_LASTEVENT /**< Defines the count of event enums @deprecated As of 1.1.7 this enum value is deprecated and will be removed in a future release, because it prevents adding new enum values without breaking ABI compatibility. */ }; #define FLUID_SEQ_PITCHWHHELSENS FLUID_SEQ_PITCHWHEELSENS /**< Old deprecated misspelling of #FLUID_SEQ_PITCHWHEELSENS */ /* Event alloc/free */ FLUIDSYNTH_API fluid_event_t* new_fluid_event(void); FLUIDSYNTH_API void delete_fluid_event(fluid_event_t* evt); /* Initializing events */ FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t* evt, fluid_seq_id_t src); FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t* evt, fluid_seq_id_t dest); /* Timer events */ FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data); /* Note events */ FLUIDSYNTH_API void fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned int duration); FLUIDSYNTH_API void fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel); FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_t* evt, int channel, short key); FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_t* evt, int channel); FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_t* evt, int channel); /* Instrument selection */ FLUIDSYNTH_API void fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num); FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t* evt, int channel, short preset_num); FLUIDSYNTH_API void fluid_event_program_select(fluid_event_t* evt, int channel, unsigned int sfont_id, short bank_num, short preset_num); /* Real-time generic instrument controllers */ FLUIDSYNTH_API void fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val); /* Real-time instrument controllers shortcuts */ FLUIDSYNTH_API void fluid_event_pitch_bend(fluid_event_t* evt, int channel, int val); FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_pan(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_volume(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t* evt); /* Only for removing events */ FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t* evt, int channel); /* Only when unregistering clients */ FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t* evt); /* Accessing event data */ FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt); FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t* evt); FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_dest(fluid_event_t* evt); FLUIDSYNTH_API int fluid_event_get_channel(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_key(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_control(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_value(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_program(fluid_event_t* evt); FLUIDSYNTH_API void* fluid_event_get_data(fluid_event_t* evt); FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_t* evt); FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_t* evt); FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_t* evt); FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_t* evt); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_EVENT_H */ fluidsynth-1.1.9/include/fluidsynth/gen.h000066400000000000000000000126431322272076000204600ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_GEN_H #define _FLUIDSYNTH_GEN_H #ifdef __cplusplus extern "C" { #endif /** * @file gen.h * @brief Functions and defines for SoundFont generator effects. */ /** * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3) */ enum fluid_gen_type { GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ GEN_FILTERFC, /**< Filter cutoff */ GEN_FILTERQ, /**< Filter Q */ GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ GEN_UNUSED1, /**< Unused */ GEN_CHORUSSEND, /**< Chorus send amount */ GEN_REVERBSEND, /**< Reverb send amount */ GEN_PAN, /**< Stereo panning */ GEN_UNUSED2, /**< Unused */ GEN_UNUSED3, /**< Unused */ GEN_UNUSED4, /**< Unused */ GEN_MODLFODELAY, /**< Modulation LFO delay */ GEN_MODLFOFREQ, /**< Modulation LFO frequency */ GEN_VIBLFODELAY, /**< Vibrato LFO delay */ GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ GEN_MODENVDELAY, /**< Modulation envelope delay */ GEN_MODENVATTACK, /**< Modulation envelope attack */ GEN_MODENVHOLD, /**< Modulation envelope hold */ GEN_MODENVDECAY, /**< Modulation envelope decay */ GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ GEN_MODENVRELEASE, /**< Modulation envelope release */ GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ GEN_VOLENVDELAY, /**< Volume envelope delay */ GEN_VOLENVATTACK, /**< Volume envelope attack */ GEN_VOLENVHOLD, /**< Volume envelope hold */ GEN_VOLENVDECAY, /**< Volume envelope decay */ GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ GEN_VOLENVRELEASE, /**< Volume envelope release */ GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ GEN_RESERVED1, /**< Reserved */ GEN_KEYRANGE, /**< MIDI note range */ GEN_VELRANGE, /**< MIDI velocity range */ GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ GEN_KEYNUM, /**< Fixed MIDI note number */ GEN_VELOCITY, /**< Fixed MIDI velocity value */ GEN_ATTENUATION, /**< Initial volume attenuation */ GEN_RESERVED2, /**< Reserved */ GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ GEN_COARSETUNE, /**< Coarse tuning */ GEN_FINETUNE, /**< Fine tuning */ GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ GEN_SAMPLEMODE, /**< Sample mode flags */ GEN_RESERVED3, /**< Reserved */ GEN_SCALETUNE, /**< Scale tuning */ GEN_EXCLUSIVECLASS, /**< Exclusive class number */ GEN_OVERRIDEROOTKEY, /**< Sample root note override */ /* the initial pitch is not a "standard" generator. It is not * mentioned in the list of generator in the SF2 specifications. It * is used, however, as the destination for the default pitch wheel * modulator. */ GEN_PITCH, /**< Pitch (NOTE: Not a real SoundFont generator) */ GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) @deprecated As of 1.1.7 this enum value is deprecated and will be removed in a future release, because it prevents adding new enum values without breaking ABI compatibility. */ }; /** * SoundFont generator structure. */ typedef struct _fluid_gen_t { unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ double val; /**< The nominal value */ double mod; /**< Change by modulators */ double nrpn; /**< Change by NRPN messages */ } fluid_gen_t; /** * Enum value for 'flags' field of #fluid_gen_t (not really flags). */ enum fluid_gen_flags { GEN_UNUSED, /**< Generator value is not set */ GEN_SET, /**< Generator value is set */ GEN_ABS_NRPN /**< Generator is an absolute value */ }; FLUIDSYNTH_API FLUID_DEPRECATED int fluid_gen_set_default_values(fluid_gen_t* gen); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_GEN_H */ fluidsynth-1.1.9/include/fluidsynth/log.h000066400000000000000000000055041322272076000204660ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_LOG_H #define _FLUIDSYNTH_LOG_H #ifdef __cplusplus extern "C" { #endif /** * @file log.h * @brief Logging interface * * The default logging function of the fluidsynth prints its messages * to the stderr. The synthesizer uses five level of messages: #FLUID_PANIC, * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG. * * A client application can install a new log function to handle the * messages differently. In the following example, the application * sets a callback function to display #FLUID_PANIC messages in a dialog, * and ignores all other messages by setting the log function to * NULL: * * @code * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); * fluid_set_log_function(FLUID_ERR, NULL, NULL); * fluid_set_log_function(FLUID_WARN, NULL, NULL); * fluid_set_log_function(FLUID_DBG, NULL, NULL); * @endcode */ /** * FluidSynth log levels. */ enum fluid_log_level { FLUID_PANIC, /**< The synth can't function correctly any more */ FLUID_ERR, /**< Serious error occurred */ FLUID_WARN, /**< Warning */ FLUID_INFO, /**< Verbose informational messages */ FLUID_DBG, /**< Debugging messages */ LAST_LOG_LEVEL /**< @deprecated As of 1.1.7 this enum value is deprecated and will be removed in a future release, because it prevents adding new enum values without breaking ABI compatibility. */ }; /** * Log function handler callback type used by fluid_set_log_function(). * @param level Log level (#fluid_log_level) * @param message Log message text * @param data User data pointer supplied to fluid_set_log_function(). */ typedef void (*fluid_log_function_t)(int level, char* message, void* data); FLUIDSYNTH_API fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data); FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data); FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_LOG_H */ fluidsynth-1.1.9/include/fluidsynth/midi.h000066400000000000000000000155231322272076000206310ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_MIDI_H #define _FLUIDSYNTH_MIDI_H #ifdef __cplusplus extern "C" { #endif /** * @file midi.h * @brief Functions for MIDI events, drivers and MIDI file playback. */ FLUIDSYNTH_API fluid_midi_event_t* new_fluid_midi_event(void); FLUIDSYNTH_API int delete_fluid_midi_event(fluid_midi_event_t* event); FLUIDSYNTH_API int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type); FLUIDSYNTH_API int fluid_midi_event_get_type(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan); FLUIDSYNTH_API int fluid_midi_event_get_channel(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_get_key(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_key(fluid_midi_event_t* evt, int key); FLUIDSYNTH_API int fluid_midi_event_get_velocity(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int vel); FLUIDSYNTH_API int fluid_midi_event_get_control(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_control(fluid_midi_event_t* evt, int ctrl); FLUIDSYNTH_API int fluid_midi_event_get_value(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_value(fluid_midi_event_t* evt, int val); FLUIDSYNTH_API int fluid_midi_event_get_program(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val); FLUIDSYNTH_API int fluid_midi_event_get_pitch(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val); FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t* evt, void *data, int size, int dynamic); /** * MIDI router rule type. * @since 1.1.0 */ typedef enum { FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */ FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */ FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */ FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ FLUID_MIDI_ROUTER_RULE_COUNT /**< Total count of rule types */ } fluid_midi_router_rule_type; /** * Generic callback function for MIDI events. * @param data User defined data pointer * @param event The MIDI event * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise * * Will be used between * - MIDI driver and MIDI router * - MIDI router and synth * to communicate events. * In the not-so-far future... */ typedef int (*handle_midi_event_func_t)(void* data, fluid_midi_event_t* event); FLUIDSYNTH_API fluid_midi_router_t* new_fluid_midi_router(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); FLUIDSYNTH_API int delete_fluid_midi_router(fluid_midi_router_t* handler); FLUIDSYNTH_API int fluid_midi_router_set_default_rules (fluid_midi_router_t *router); FLUIDSYNTH_API int fluid_midi_router_clear_rules (fluid_midi_router_t *router); FLUIDSYNTH_API int fluid_midi_router_add_rule (fluid_midi_router_t *router, fluid_midi_router_rule_t *rule, int type); FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule (void); FLUIDSYNTH_API void delete_fluid_midi_router_rule (fluid_midi_router_rule_t *rule); FLUIDSYNTH_API void fluid_midi_router_rule_set_chan (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add); FLUIDSYNTH_API void fluid_midi_router_rule_set_param1 (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add); FLUIDSYNTH_API void fluid_midi_router_rule_set_param2 (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add); FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event); FLUIDSYNTH_API int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event); FLUIDSYNTH_API int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event); FLUIDSYNTH_API fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver); /** * MIDI player status enum. * @since 1.1.0 */ enum fluid_player_status { FLUID_PLAYER_READY, /**< Player is ready */ FLUID_PLAYER_PLAYING, /**< Player is currently playing */ FLUID_PLAYER_DONE /**< Player is finished playing */ }; FLUIDSYNTH_API fluid_player_t* new_fluid_player(fluid_synth_t* synth); FLUIDSYNTH_API int delete_fluid_player(fluid_player_t* player); FLUIDSYNTH_API int fluid_player_add(fluid_player_t* player, const char *midifile); FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len); FLUIDSYNTH_API int fluid_player_play(fluid_player_t* player); FLUIDSYNTH_API int fluid_player_stop(fluid_player_t* player); FLUIDSYNTH_API int fluid_player_join(fluid_player_t* player); FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t* player, int loop); FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo); FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t* player, int bpm); FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t* player, handle_midi_event_func_t handler, void* handler_data); FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t* player); FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t * player); FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t * player); FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t * player); FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t * player); /// #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MIDI_H */ fluidsynth-1.1.9/include/fluidsynth/misc.h000066400000000000000000000037271322272076000206450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_MISC_H #define _FLUIDSYNTH_MISC_H #ifdef __cplusplus extern "C" { #endif /** * @file misc.h * @brief Miscellaneous utility functions and defines */ /** * Value that indicates success, used by most libfluidsynth functions. * @since 1.1.0 * * NOTE: This was not publicly defined prior to libfluidsynth 1.1.0. When * writing code which should also be compatible with older versions, something * like the following can be used: * * @code * #include * * #ifndef FLUID_OK * #define FLUID_OK (0) * #define FLUID_FAILED (-1) * #endif * @endcode */ #define FLUID_OK (0) /** * Value that indicates failure, used by most libfluidsynth functions. * @since 1.1.0 * * NOTE: See #FLUID_OK for more details. */ #define FLUID_FAILED (-1) FLUIDSYNTH_API int fluid_is_soundfont (const char *filename); FLUIDSYNTH_API int fluid_is_midifile (const char *filename); #ifdef WIN32 FLUIDSYNTH_API FLUID_DEPRECATED void* fluid_get_hinstance(void); FLUIDSYNTH_API FLUID_DEPRECATED void fluid_set_hinstance(void* hinstance); #endif #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MISC_H */ fluidsynth-1.1.9/include/fluidsynth/mod.h000066400000000000000000000106431322272076000204640ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_MOD_H #define _FLUIDSYNTH_MOD_H #ifdef __cplusplus extern "C" { #endif /** * @file mod.h * @brief SoundFont modulator functions and constants. */ #define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ /** * Modulator structure. See SoundFont 2.04 PDF section 8.2. * * @deprecated To be removed from the public API. */ struct _fluid_mod_t { unsigned char dest; /**< Destination generator to control */ unsigned char src1; /**< Source controller 1 */ unsigned char flags1; /**< Source controller 1 flags */ unsigned char src2; /**< Source controller 2 */ unsigned char flags2; /**< Source controller 2 flags */ double amount; /**< Multiplier amount */ /* The 'next' field allows to link modulators into a list. It is * not used in fluid_voice.c, there each voice allocates memory for a * fixed number of modulators. Since there may be a huge number of * different zones, this is more efficient. */ fluid_mod_t * next; }; /** * Flags defining the polarity, mapping function and type of a modulator source. * Compare with SoundFont 2.04 PDF section 8.2. * * Note: Bit values do not correspond to the SoundFont spec! Also note that * #FLUID_MOD_GC and #FLUID_MOD_CC are in the flags field instead of the source field. */ enum fluid_mod_flags { FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */ FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */ FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */ FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */ FLUID_MOD_LINEAR = 0, /**< Linear mapping function */ FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */ FLUID_MOD_CONVEX = 8, /**< Convex mapping function */ FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */ FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */ FLUID_MOD_CC = 16 /**< MIDI CC controller (source will be a MIDI CC number) */ }; /** * General controller (if #FLUID_MOD_GC in flags). This * corresponds to SoundFont 2.04 PDF section 8.2.1 */ enum fluid_mod_src { FLUID_MOD_NONE = 0, /**< No source controller */ FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */ FLUID_MOD_KEY = 3, /**< MIDI note-on note number */ FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */ FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */ FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */ FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ }; FLUIDSYNTH_API fluid_mod_t* fluid_mod_new(void); FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod); FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags); FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags); FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst); FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount); FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod); FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod); FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod); FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod); FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod); FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod); FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MOD_H */ fluidsynth-1.1.9/include/fluidsynth/ramsfont.h000066400000000000000000000052361322272076000215400ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* RAM SoundFonts: October 2002 - Antoine Schmitt */ #ifndef _FLUIDSYNTH_RAMSFONT_H #define _FLUIDSYNTH_RAMSFONT_H #ifdef __cplusplus extern "C" { #endif /** * @file ramsfont.h * @brief API for creating and managing SoundFont instruments in RAM. * * RAM SoundFonts live in ram. The samples are loaded from files * or from RAM. A minimal API manages a soundFont structure, * with presets, each preset having only one preset-zone, which * instrument has potentially many instrument-zones. No global * zones, and nor generator nor modulator other than the default * ones are permitted. This may be extensible in the future. */ FLUIDSYNTH_API fluid_sfont_t* fluid_ramsfont_create_sfont(void); FLUIDSYNTH_API int fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, const char *name); FLUIDSYNTH_API int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int lokey, int hikey); FLUIDSYNTH_API int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample); FLUIDSYNTH_API int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int gen_type, float value); FLUIDSYNTH_API int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int on, float loopstart, float loopend); FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void); FLUIDSYNTH_API int delete_fluid_ramsample(fluid_sample_t* sample); FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, const char *name); FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, unsigned int nbframes, short copy_data, int rootkey); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_RAMSFONT_H */ fluidsynth-1.1.9/include/fluidsynth/seq.h000066400000000000000000000063051322272076000204750ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SEQ_H #define _FLUIDSYNTH_SEQ_H #ifdef __cplusplus extern "C" { #endif /** * @file seq.h * @brief MIDI event sequencer. */ /** * Event callback prototype for destination clients. * @param time Current sequencer tick value (see fluid_sequencer_get_tick()). * @param event The event being received * @param seq The sequencer instance * @param data User defined data registered with the client */ typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data); FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer(void); FLUIDSYNTH_API fluid_sequencer_t* new_fluid_sequencer2(int use_system_timer); FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t* seq); FLUIDSYNTH_API int fluid_sequencer_get_use_system_timer(fluid_sequencer_t* seq); FLUIDSYNTH_API fluid_seq_id_t fluid_sequencer_register_client(fluid_sequencer_t* seq, const char *name, fluid_event_callback_t callback, void* data); FLUIDSYNTH_API void fluid_sequencer_unregister_client(fluid_sequencer_t* seq, fluid_seq_id_t id); FLUIDSYNTH_API int fluid_sequencer_count_clients(fluid_sequencer_t* seq); FLUIDSYNTH_API fluid_seq_id_t fluid_sequencer_get_client_id(fluid_sequencer_t* seq, int index); FLUIDSYNTH_API char* fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int id); FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id); FLUIDSYNTH_API void fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec); FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt); FLUIDSYNTH_API int fluid_sequencer_send_at(fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int time, int absolute); FLUIDSYNTH_API void fluid_sequencer_remove_events(fluid_sequencer_t* seq, fluid_seq_id_t source, fluid_seq_id_t dest, int type); FLUIDSYNTH_API unsigned int fluid_sequencer_get_tick(fluid_sequencer_t* seq); FLUIDSYNTH_API void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale); FLUIDSYNTH_API double fluid_sequencer_get_time_scale(fluid_sequencer_t* seq); // Compile in internal traceing functions #define FLUID_SEQ_WITH_TRACE 0 #if FLUID_SEQ_WITH_TRACE FLUIDSYNTH_API char * fluid_seq_gettrace(fluid_sequencer_t* seq); FLUIDSYNTH_API void fluid_seq_cleartrace(fluid_sequencer_t* seq); #endif #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SEQ_H */ fluidsynth-1.1.9/include/fluidsynth/seqbind.h000066400000000000000000000024761322272076000213370ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SEQBIND_H #define _FLUIDSYNTH_SEQBIND_H #include "seq.h" #ifdef __cplusplus extern "C" { #endif /** * @file seqbind.h * @brief Functions for binding sequencer objects to other subsystems. */ FLUIDSYNTH_API fluid_seq_id_t fluid_sequencer_register_fluidsynth(fluid_sequencer_t* seq, fluid_synth_t* synth); FLUIDSYNTH_API int fluid_sequencer_add_midi_event_to_buffer(void* data, fluid_midi_event_t* event); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SEQBIND_H */ fluidsynth-1.1.9/include/fluidsynth/settings.h000066400000000000000000000166161322272076000215530ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SETTINGS_H #define _FLUIDSYNTH_SETTINGS_H #ifdef __cplusplus extern "C" { #endif /** * @file settings.h * @brief Synthesizer settings * @defgroup SettingsFunctions Functions for settings management * * To create a synthesizer object you will have to specify its * settings. These settings are stored in a fluid_settings_t object. * @code * void * my_synthesizer () * { * fluid_settings_t *settings; * fluid_synth_t *synth; * fluid_audio_driver_t *adriver; * * settings = new_fluid_settings (); * fluid_settings_setstr(settings, "audio.driver", "alsa"); * // ... change settings ... * synth = new_fluid_synth (settings); * adriver = new_fluid_audio_driver (settings, synth); * // ... * } * @endcode * @sa @ref CreatingSettings */ /** * Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field * of the FLUID_PortRangeHint should be considered meaningful. The * value in this field should be considered the (inclusive) lower * bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also * specified then the value of LowerBound should be multiplied by the * sample rate. */ #define FLUID_HINT_BOUNDED_BELOW 0x1 /** Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field of the FLUID_PortRangeHint should be considered meaningful. The value in this field should be considered the (inclusive) upper bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also specified then the value of UpperBound should be multiplied by the sample rate. */ #define FLUID_HINT_BOUNDED_ABOVE 0x2 /** * Hint FLUID_HINT_TOGGLED indicates that the data item should be * considered a Boolean toggle. Data less than or equal to zero should * be considered `off' or `false,' and data above zero should be * considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in * conjunction with any other hint. */ #define FLUID_HINT_TOGGLED 0x4 /** * Hint FLUID_HINT_SAMPLE_RATE indicates that any bounds specified * should be interpreted as multiples of the sample rate. For * instance, a frequency range from 0Hz to the Nyquist frequency (half * the sample rate) could be requested by this hint in conjunction * with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds * at all must support this hint to retain meaning. */ #define FLUID_HINT_SAMPLE_RATE 0x8 /** * Hint FLUID_HINT_LOGARITHMIC indicates that it is likely that the * user will find it more intuitive to view values using a logarithmic * scale. This is particularly useful for frequencies and gains. */ #define FLUID_HINT_LOGARITHMIC 0x10 /** * Hint FLUID_HINT_INTEGER indicates that a user interface would * probably wish to provide a stepped control taking only integer * values. * @deprecated * * As there is an integer setting type, this hint is not used. */ #define FLUID_HINT_INTEGER 0x20 #define FLUID_HINT_FILENAME 0x01 /**< String setting is a file name */ #define FLUID_HINT_OPTIONLIST 0x02 /**< Setting is a list of string options */ /** * Settings type * * Each setting has a defined type: numeric (double), integer, string or a * set of values. The type of each setting can be retrieved using the * function fluid_settings_get_type() */ enum fluid_types_enum { FLUID_NO_TYPE = -1, /**< Undefined type */ FLUID_NUM_TYPE, /**< Numeric (double) */ FLUID_INT_TYPE, /**< Integer */ FLUID_STR_TYPE, /**< String */ FLUID_SET_TYPE /**< Set of values */ }; FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void); FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings); FLUIDSYNTH_API int fluid_settings_get_type(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API int fluid_settings_get_hints(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API int fluid_settings_is_realtime(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API int fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str); FLUIDSYNTH_API int fluid_settings_copystr(fluid_settings_t* settings, const char *name, char *str, int len); FLUIDSYNTH_API int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str); FLUIDSYNTH_API char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API int fluid_settings_str_equal(fluid_settings_t* settings, const char *name, const char *value); FLUIDSYNTH_API int fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val); FLUIDSYNTH_API int fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val); FLUIDSYNTH_API double fluid_settings_getnum_default(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API void fluid_settings_getnum_range(fluid_settings_t* settings, const char *name, double* min, double* max); FLUIDSYNTH_API int fluid_settings_setint(fluid_settings_t* settings, const char *name, int val); FLUIDSYNTH_API int fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val); FLUIDSYNTH_API int fluid_settings_getint_default(fluid_settings_t* settings, const char *name); FLUIDSYNTH_API void fluid_settings_getint_range(fluid_settings_t* settings, const char *name, int* min, int* max); /** * Callback function type used with fluid_settings_foreach_option() * @param data User defined data pointer * @param name Setting name * @param option A string option for this setting (iterates through the list) */ typedef void (*fluid_settings_foreach_option_t)(void *data, char *name, char *option); FLUIDSYNTH_API void fluid_settings_foreach_option(fluid_settings_t* settings, const char* name, void* data, fluid_settings_foreach_option_t func); FLUIDSYNTH_API int fluid_settings_option_count (fluid_settings_t* settings, const char* name); FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings, const char* name, const char* separator); /** * Callback function type used with fluid_settings_foreach() * @param data User defined data pointer * @param name Setting name * @param type Setting type (#fluid_types_enum) */ typedef void (*fluid_settings_foreach_t)(void *data, char *name, int type); FLUIDSYNTH_API void fluid_settings_foreach(fluid_settings_t* settings, void* data, fluid_settings_foreach_t func); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SETTINGS_H */ fluidsynth-1.1.9/include/fluidsynth/sfont.h000066400000000000000000000265621322272076000210450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SFONT_H #define _FLUIDSYNTH_SFONT_H #ifdef __cplusplus extern "C" { #endif /** * @file sfont.h * @brief SoundFont plugins * * It is possible to add new SoundFont loaders to the * synthesizer. The API uses a couple of "interfaces" (structures * with callback functions): #fluid_sfloader_t, #fluid_sfont_t, and * #fluid_preset_t. This API allows for virtual SoundFont files to be loaded * and synthesized, which may not actually be SoundFont files, as long as they * can be represented by the SoundFont synthesis model. * * To add a new SoundFont loader to the synthesizer, call * fluid_synth_add_sfloader() and pass a pointer to an * fluid_sfloader_t structure. The important callback function in * this structure is "load", which should try to load a file and * returns a #fluid_sfont_t structure, or NULL if it fails. * * The #fluid_sfont_t structure contains a callback to obtain the * name of the SoundFont. It contains two functions to iterate * though the contained presets, and one function to obtain a * preset corresponding to a bank and preset number. This * function should return a #fluid_preset_t structure. * * The #fluid_preset_t structure contains some functions to obtain * information from the preset (name, bank, number). The most * important callback is the noteon function. The noteon function * should call fluid_synth_alloc_voice() for every sample that has * to be played. fluid_synth_alloc_voice() expects a pointer to a * #fluid_sample_t structure and returns a pointer to the opaque * #fluid_voice_t structure. To set or increment the values of a * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are * finished initializing the voice call fluid_voice_start() to * start playing the synthesis voice. */ /** * Some notification enums for presets and samples. */ enum { FLUID_PRESET_SELECTED, /**< Preset selected notify */ FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ FLUID_SAMPLE_DONE /**< Sample no longer needed notify */ }; /** * SoundFont loader structure. */ struct _fluid_sfloader_t { void* data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ /** * The free method should free the memory allocated for the loader in * addition to any private data. * @param loader SoundFont loader * @return Should return 0 if no error occured, non-zero otherwise */ int (*free)(fluid_sfloader_t* loader); /** * Method to load an instrument file (does not actually need to be a real file name, * could be another type of string identifier that the \a loader understands). * @param loader SoundFont loader * @param filename File name or other string identifier * @return The loaded instrument file (SoundFont) or NULL if an error occured. */ fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename); }; /** * Virtual SoundFont instance structure. */ struct _fluid_sfont_t { void* data; /**< User defined data */ unsigned int id; /**< SoundFont ID */ /** * Method to free a virtual SoundFont bank. * @param sfont Virtual SoundFont to free. * @return Should return 0 when it was able to free all resources or non-zero * if some of the samples could not be freed because they are still in use, * in which case the free will be tried again later, until success. */ int (*free)(fluid_sfont_t* sfont); /** * Method to return the name of a virtual SoundFont. * @param sfont Virtual SoundFont * @return The name of the virtual SoundFont. */ char* (*get_name)(fluid_sfont_t* sfont); /** * Get a virtual SoundFont preset by bank and program numbers. * @param sfont Virtual SoundFont * @param bank MIDI bank number (0-16384) * @param prenum MIDI preset number (0-127) * @return Should return an allocated virtual preset or NULL if it could not * be found. */ fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); /** * Start virtual SoundFont preset iteration method. * @param sfont Virtual SoundFont * * Starts/re-starts virtual preset iteration in a SoundFont. */ void (*iteration_start)(fluid_sfont_t* sfont); /** * Virtual SoundFont preset iteration function. * @param sfont Virtual SoundFont * @param preset Caller supplied preset to fill in with current preset information * @return 0 when no more presets are available, 1 otherwise * * Should store preset information to the caller supplied \a preset structure * and advance the internal iteration state to the next preset for subsequent * calls. */ int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset); }; #define fluid_sfont_get_id(_sf) ((_sf)->id) /** * Virtual SoundFont preset. */ struct _fluid_preset_t { void* data; /**< User supplied data */ fluid_sfont_t* sfont; /**< Parent virtual SoundFont */ /** * Method to free a virtual SoundFont preset. * @param preset Virtual SoundFont preset * @return Should return 0 */ int (*free)(fluid_preset_t* preset); /** * Method to get a virtual SoundFont preset name. * @param preset Virtual SoundFont preset * @return Should return the name of the preset. The returned string must be * valid for the duration of the virtual preset (or the duration of the * SoundFont, in the case of preset iteration). */ char* (*get_name)(fluid_preset_t* preset); /** * Method to get a virtual SoundFont preset MIDI bank number. * @param preset Virtual SoundFont preset * @param return The bank number of the preset */ int (*get_banknum)(fluid_preset_t* preset); /** * Method to get a virtual SoundFont preset MIDI program number. * @param preset Virtual SoundFont preset * @param return The program number of the preset */ int (*get_num)(fluid_preset_t* preset); /** * Method to handle a noteon event (synthesize the instrument). * @param preset Virtual SoundFont preset * @param synth Synthesizer instance * @param chan MIDI channel number of the note on event * @param key MIDI note number (0-127) * @param vel MIDI velocity (0-127) * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise * * This method may be called from within synthesis context and therefore * should be as efficient as possible and not perform any operations considered * bad for realtime audio output (memory allocations and other OS calls). * * Call fluid_synth_alloc_voice() for every sample that has * to be played. fluid_synth_alloc_voice() expects a pointer to a * #fluid_sample_t structure and returns a pointer to the opaque * #fluid_voice_t structure. To set or increment the values of a * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are * finished initializing the voice call fluid_voice_start() to * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices * created will be started at the same time. */ int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); /** * Virtual SoundFont preset notify method. * @param preset Virtual SoundFont preset * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED * @param chan MIDI channel number * @return Should return #FLUID_OK * * Implement this optional method if the preset needs to be notified about * preset select and unselect events. * * This method may be called from within synthesis context and therefore * should be as efficient as possible and not perform any operations considered * bad for realtime audio output (memory allocations and other OS calls). */ int (*notify)(fluid_preset_t* preset, int reason, int chan); }; /** * Virtual SoundFont sample. */ struct _fluid_sample_t { char name[21]; /**< Sample name */ unsigned int start; /**< Start index */ unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ unsigned int loopstart; /**< Loop start index */ unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ unsigned int samplerate; /**< Sample rate */ int origpitch; /**< Original pitch (MIDI note number, 0-127) */ int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ int sampletype; /**< Values: #FLUID_SAMPLETYPE_MONO, FLUID_SAMPLETYPE_RIGHT, FLUID_SAMPLETYPE_LEFT, FLUID_SAMPLETYPE_ROM */ int valid; /**< Should be TRUE if sample data is valid, FALSE otherwise (in which case it will not be synthesized) */ short* data; /**< Pointer to the sample's data */ int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ unsigned int refcount; /**< Count of voices using this sample (use #fluid_sample_refcount to access this field) */ /** * Implement this function to receive notification when sample is no longer used. * @param sample Virtual SoundFont sample * @param reason #FLUID_SAMPLE_DONE only currently * @return Should return #FLUID_OK */ int (*notify)(fluid_sample_t* sample, int reason); void* userdata; /**< User defined data */ }; #define fluid_sample_refcount(_sample) ((_sample)->refcount) /**< Get the reference count of a sample. Should only be called from within synthesis context (noteon method for example) */ #define FLUID_SAMPLETYPE_MONO 1 /**< Flag for #fluid_sample_t \a sampletype field for mono samples */ #define FLUID_SAMPLETYPE_RIGHT 2 /**< Flag for #fluid_sample_t \a sampletype field for right samples of a stereo pair */ #define FLUID_SAMPLETYPE_LEFT 4 /**< Flag for #fluid_sample_t \a sampletype field for left samples of a stereo pair */ #define FLUID_SAMPLETYPE_LINKED 8 /**< Flag for #fluid_sample_t \a sampletype field, not used currently */ #define FLUID_SAMPLETYPE_OGG_VORBIS 0x10 /**< Flag for #fluid_sample_t \a sampletype field for Ogg Vorbis compressed samples @since 1.1.7 */ #define FLUID_SAMPLETYPE_ROM 0x8000 /**< Flag for #fluid_sample_t \a sampletype field, ROM sample, causes sample to be ignored */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SFONT_H */ fluidsynth-1.1.9/include/fluidsynth/shell.h000066400000000000000000000077261322272076000210240ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SHELL_H #define _FLUIDSYNTH_SHELL_H #ifdef __cplusplus extern "C" { #endif /** * @file shell.h * @brief Command shell interface * * The shell interface allows you to send simple textual commands to * the synthesizer, to parse a command file, or to read commands * from the stdin or other input streams. */ FLUIDSYNTH_API fluid_istream_t fluid_get_stdin(void); FLUIDSYNTH_API fluid_ostream_t fluid_get_stdout(void); FLUIDSYNTH_API char* fluid_get_userconf(char* buf, int len); FLUIDSYNTH_API char* fluid_get_sysconf(char* buf, int len); /** * Command handler function prototype. * @param data User defined data * @param ac Argument count * @param av Array of string arguments * @param out Output stream to send response to * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise */ typedef int (*fluid_cmd_func_t)(void* data, int ac, char** av, fluid_ostream_t out); /** * Shell command information structure. */ typedef struct { char* name; /**< The name of the command, as typed in the shell */ char* topic; /**< The help topic group of this command */ fluid_cmd_func_t handler; /**< Pointer to the handler for this command */ void* data; /**< User data passed to the handler */ char* help; /**< A help string */ } fluid_cmd_t; /* The command handler */ FLUIDSYNTH_API fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth); FLUIDSYNTH_API void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler); FLUIDSYNTH_API void fluid_cmd_handler_set_synth(fluid_cmd_handler_t* handler, fluid_synth_t* synth); FLUIDSYNTH_API int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd); FLUIDSYNTH_API int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd); /* Command function */ FLUIDSYNTH_API int fluid_command(fluid_cmd_handler_t* handler, const char *cmd, fluid_ostream_t out); FLUIDSYNTH_API int fluid_source(fluid_cmd_handler_t* handler, const char *filename); FLUIDSYNTH_API void fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler); /* Shell */ FLUIDSYNTH_API fluid_shell_t* new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_istream_t in, fluid_ostream_t out, int thread); FLUIDSYNTH_API void delete_fluid_shell(fluid_shell_t* shell); /* TCP/IP server */ /** * Callback function which is executed for new server connections. * @param data User defined data supplied in call to new_fluid_server() * @param addr The IP address of the client (can be NULL) * @return Should return a new command handler for the connection (new_fluid_cmd_handler()). */ typedef fluid_cmd_handler_t* (*fluid_server_newclient_func_t)(void* data, char* addr); FLUIDSYNTH_API fluid_server_t* new_fluid_server(fluid_settings_t* settings, fluid_server_newclient_func_t func, void* data); FLUIDSYNTH_API void delete_fluid_server(fluid_server_t* server); FLUIDSYNTH_API int fluid_server_join(fluid_server_t* server); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SHELL_H */ fluidsynth-1.1.9/include/fluidsynth/synth.h000066400000000000000000000353051322272076000210540ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_SYNTH_H #define _FLUIDSYNTH_SYNTH_H #ifdef __cplusplus extern "C" { #endif /** * @file synth.h * @brief Embeddable SoundFont synthesizer * * You create a new synthesizer with new_fluid_synth() and you destroy * if with delete_fluid_synth(). Use the settings structure to specify * the synthesizer characteristics. * * You have to load a SoundFont in order to hear any sound. For that * you use the fluid_synth_sfload() function. * * You can use the audio driver functions described below to open * the audio device and create a background audio thread. * * The API for sending MIDI events is probably what you expect: * fluid_synth_noteon(), fluid_synth_noteoff(), ... */ #define FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE 32 /**< Length of channel info name field (including zero terminator) */ /** * Channel information structure for fluid_synth_get_channel_info(). * @since 1.1.1 */ struct _fluid_synth_channel_info_t { int assigned : 1; /**< TRUE if a preset is assigned, FALSE otherwise */ /* Reserved flag bits (at the least 31) */ int sfont_id; /**< ID of parent SoundFont */ int bank; /**< MIDI bank number (0-16383) */ int program; /**< MIDI program number (0-127) */ char name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE]; /**< Channel preset name */ char reserved[32]; /**< Reserved data for future expansion */ }; FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth); FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); /* MIDI channel messages */ FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int *handled, int dryrun); FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val); FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); FLUIDSYNTH_API int fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, unsigned int bank_num, unsigned int preset_num); FLUIDSYNTH_API int fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, const char *sfont_name, unsigned int bank_num, unsigned int preset_num); FLUIDSYNTH_API int fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num); FLUIDSYNTH_API int fluid_synth_unset_program (fluid_synth_t *synth, int chan); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_get_channel_info (fluid_synth_t *synth, int chan, fluid_synth_channel_info_t *info); FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); /** * The midi channel type used by fluid_synth_set_channel_type() */ enum fluid_midi_channel_type { CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ }; FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type); /* Low level access */ FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, int audio_chan, int midi_chan, int key, int vel); FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id); /* SoundFont management */ FLUIDSYNTH_API int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets); FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id); FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets); FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name (fluid_synth_t* synth, const char *name); FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); /* Reverb */ FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, double width, double level); FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on); FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth); #define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */ #define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */ #define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */ #define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */ /* Chorus */ /** * Chorus modulation waveform type. */ enum fluid_chorus_mod { FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ }; FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, double speed, double depth_ms, int type); FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on); FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */ #define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */ #define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */ #define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */ #define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */ #define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */ /* Audio and MIDI channels */ FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth); /* Synthesis parameters */ FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate); FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain); FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony); FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method); /** * Synthesis interpolation method. */ enum fluid_interp { FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ FLUID_INTERP_7THORDER = 7 /**< Seventh-order interpolation */ }; #define FLUID_INTERP_DEFAULT FLUID_INTERP_4THORDER /**< Default interpolation method from #fluid_interp. */ #define FLUID_INTERP_HIGHEST FLUID_INTERP_7THORDER /**< Highest interpolation method from #fluid_interp. */ /* Generator interface */ FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan, int param, float value, int absolute, int normalized); FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); /* Tuning */ FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch); FLUIDSYNTH_API int fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch, int apply); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch); FLUIDSYNTH_API int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch, int apply); FLUIDSYNTH_API int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, int len, const int *keys, const double* pitch, int apply); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog); FLUIDSYNTH_API int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, int apply); FLUIDSYNTH_API FLUID_DEPRECATED int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); FLUIDSYNTH_API int fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply); FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, char* name, int len, double* pitch); /* Misc */ FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t* synth); FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); /* * Synthesizer plugin * * To create a synthesizer plugin, create the synthesizer as * explained above. Once the synthesizer is created you can call * any of the functions below to get the audio. */ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr); FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr); FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, float** left, float** right, float** fx_left, float** fx_right); FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, int nout, float** out); /** * Type definition of the synthesizer's audio callback function. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param out1 Array to store left channel of audio to * @param loff Offset index in 'out1' for first sample * @param lincr Increment between samples stored to 'out1' * @param out2 Array to store right channel of audio to * @param roff Offset index in 'out2' for first sample * @param rincr Increment between samples stored to 'out2' */ typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, void* out1, int loff, int lincr, void* out2, int roff, int rincr); /* Synthesizer's interface to handle SoundFont loaders */ FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int channum, int key, int vel); FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int ID); FLUIDSYNTH_API int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event); FLUIDSYNTH_API FLUID_DEPRECATED void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_SYNTH_H */ fluidsynth-1.1.9/include/fluidsynth/types.h000066400000000000000000000066661322272076000210630ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_TYPES_H #define _FLUIDSYNTH_TYPES_H #ifdef __cplusplus extern "C" { #endif /** * @file types.h * @brief Type declarations */ typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */ typedef struct _fluid_synth_t fluid_synth_t; /**< Synthesizer instance */ typedef struct _fluid_synth_channel_info_t fluid_synth_channel_info_t; /**< SoundFont channel info */ typedef struct _fluid_voice_t fluid_voice_t; /**< Synthesis voice instance */ typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */ typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */ typedef struct _fluid_preset_t fluid_preset_t; /**< SoundFont preset */ typedef struct _fluid_sample_t fluid_sample_t; /**< SoundFont sample */ typedef struct _fluid_mod_t fluid_mod_t; /**< SoundFont modulator */ typedef struct _fluid_audio_driver_t fluid_audio_driver_t; /**< Audio driver instance */ typedef struct _fluid_file_renderer_t fluid_file_renderer_t; /**< Audio file renderer instance */ typedef struct _fluid_player_t fluid_player_t; /**< MIDI player instance */ typedef struct _fluid_midi_event_t fluid_midi_event_t; /**< MIDI event */ typedef struct _fluid_midi_driver_t fluid_midi_driver_t; /**< MIDI driver instance */ typedef struct _fluid_midi_router_t fluid_midi_router_t; /**< MIDI router instance */ typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; /**< MIDI router rule */ typedef struct _fluid_hashtable_t fluid_cmd_handler_t; /**< Command handler */ typedef struct _fluid_shell_t fluid_shell_t; /**< Command shell */ typedef struct _fluid_server_t fluid_server_t; /**< TCP/IP shell server instance */ typedef struct _fluid_event_t fluid_event_t; /**< Sequencer event */ typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer instance */ typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */ typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */ typedef int fluid_istream_t; /**< Input stream descriptor */ typedef int fluid_ostream_t; /**< Output stream descriptor */ typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */ #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_TYPES_H */ fluidsynth-1.1.9/include/fluidsynth/version.h.in000066400000000000000000000032121322272076000217710ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_VERSION_H #define _FLUIDSYNTH_VERSION_H #ifdef __cplusplus extern "C" { #endif /** * @file version.h * @brief Library version functions and defines */ #define FLUIDSYNTH_VERSION @FLUIDSYNTH_VERSION@ /**< String constant of libfluidsynth version. */ #define FLUIDSYNTH_VERSION_MAJOR @FLUIDSYNTH_VERSION_MAJOR@ /**< libfluidsynth major version integer constant. */ #define FLUIDSYNTH_VERSION_MINOR @FLUIDSYNTH_VERSION_MINOR@ /**< libfluidsynth minor version integer constant. */ #define FLUIDSYNTH_VERSION_MICRO @FLUIDSYNTH_VERSION_MICRO@ /**< libfluidsynth micro version integer constant. */ FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); FLUIDSYNTH_API char* fluid_version_str(void); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_VERSION_H */ fluidsynth-1.1.9/include/fluidsynth/voice.h000066400000000000000000000055411322272076000210130ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_VOICE_H #define _FLUIDSYNTH_VOICE_H #ifdef __cplusplus extern "C" { #endif /** * @file voice.h * @brief Synthesis voice manipulation functions. * * The interface to the synthesizer's voices. * Examples on using them can be found in fluid_defsfont.c. * Most of these functions should only be called from within synthesis context, * such as the SoundFont loader's noteon method. */ FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen); /** * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators. */ enum fluid_voice_add_mod { FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */ FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */ FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */ }; FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode); FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val); FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen); FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); FLUIDSYNTH_API unsigned int fluid_voice_get_id(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_is_playing(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_is_on(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_get_channel(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_get_actual_velocity(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s); #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_VOICE_H */ fluidsynth-1.1.9/install-sh000077500000000000000000000127011322272076000157210ustar00rootroot00000000000000#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else : fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=$mkdirprog fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f "$src" ] || [ -d "$src" ] then : else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else : fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else : fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else : fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else : fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 fluidsynth-1.1.9/missing000077500000000000000000000240361322272076000153200ustar00rootroot00000000000000#! /bin/sh # Common stub for a few missing GNU programs while installing. # Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi run=: # In the cases where this matters, `missing' is being run in the # srcdir already. if test -f configure.ac; then configure_ac=configure.ac else configure_ac=configure.in fi case "$1" in --run) # Try to run requested program, and just exit if it succeeds. run= shift "$@" && exit 0 ;; esac # If it does not exist, or fails to run (possibly an outdated version), # try to emulate it. case "$1" in -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit --run try to run the given command, and emulate it if it fails Supported PROGRAM values: aclocal touch file \`aclocal.m4' autoconf touch file \`configure' autoheader touch file \`config.h.in' automake touch all \`Makefile.in' files bison create \`y.tab.[ch]', if possible, from existing .[ch] flex create \`lex.yy.c', if possible, from existing .c help2man touch the output file lex create \`lex.yy.c', if possible, from existing .c makeinfo touch the output file tar try tar, gnutar, gtar, then tar without non-portable flags yacc create \`y.tab.[ch]', if possible, from existing .[ch]" ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing 0.4 - GNU automake" ;; -*) echo 1>&2 "$0: Unknown \`$1' option" echo 1>&2 "Try \`$0 --help' for more information" exit 1 ;; aclocal*) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." touch aclocal.m4 ;; autoconf) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure ;; autoheader) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acconfig.h' or \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" touch_files= for f in $files; do case "$f" in *:*) touch_files="$touch_files "`echo "$f" | sed -e 's/^[^:]*://' -e 's/:.*//'`;; *) touch_files="$touch_files $f.in";; esac done touch $touch_files ;; automake*) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | sed 's/\.am$/.in/' | while read f; do touch "$f"; done ;; autom4te) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is needed, and you do not seem to have it handy on your system. You might have modified some files without having the proper tools for further handling them. You can get \`$1Help2man' as part of \`Autoconf' from any GNU archive site." file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` if test -f "$file"; then touch $file else test -z "$file" || exec >$file echo "#! /bin/sh" echo "# Created by GNU Automake missing as a replacement of" echo "# $ $@" echo "exit 0" chmod +x $file exit 1 fi ;; bison|yacc) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.y' file. You may need the \`Bison' package in order for those modifications to take effect. You can get \`Bison' from any GNU archive site." rm -f y.tab.c y.tab.h if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.y) SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.c fi SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.h fi ;; esac fi if [ ! -f y.tab.h ]; then echo >y.tab.h fi if [ ! -f y.tab.c ]; then echo 'main() { return 0; }' >y.tab.c fi ;; lex|flex) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.l' file. You may need the \`Flex' package in order for those modifications to take effect. You can get \`Flex' from any GNU archive site." rm -f lex.yy.c if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.l) SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" lex.yy.c fi ;; esac fi if [ ! -f lex.yy.c ]; then echo 'main() { return 0; }' >lex.yy.c fi ;; help2man) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a dependency of a manual page. You may need the \`Help2man' package in order for those modifications to take effect. You can get \`Help2man' from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` fi if [ -f "$file" ]; then touch $file else test -z "$file" || exec >$file echo ".ab help2man is required to generate this page" exit 1 fi ;; makeinfo) if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then # We have makeinfo, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.texi' or \`.texinfo' file, or any other file indirectly affecting the aspect of the manual. The spurious call might also be the consequence of using a buggy \`make' (AIX, DU, IRIX). You might want to install the \`Texinfo' package or the \`GNU make' package. Grab either from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` fi touch $file ;; tar) shift if test -n "$run"; then echo 1>&2 "ERROR: \`tar' requires --run" exit 1 fi # We have already tried tar in the generic part. # Look for gnutar/gtar before invocation to avoid ugly error # messages. if (gnutar --version > /dev/null 2>&1); then gnutar "$@" && exit 0 fi if (gtar --version > /dev/null 2>&1); then gtar "$@" && exit 0 fi firstarg="$1" if shift; then case "$firstarg" in *o*) firstarg=`echo "$firstarg" | sed s/o//` tar "$firstarg" "$@" && exit 0 ;; esac case "$firstarg" in *h*) firstarg=`echo "$firstarg" | sed s/h//` tar "$firstarg" "$@" && exit 0 ;; esac fi echo 1>&2 "\ WARNING: I can't seem to be able to run \`tar' with the given arguments. You may want to install GNU tar or Free paxutils, or check the command line arguments." exit 1 ;; *) echo 1>&2 "\ WARNING: \`$1' is needed, and you do not seem to have it handy on your system. You might have modified some files without having the proper tools for further handling them. Check the \`README' file, it often tells you about the needed prerequirements for installing this package. You may also peek at any GNU archive site, in case some other package would contain this missing \`$1' program." exit 1 ;; esac exit 0 fluidsynth-1.1.9/mkinstalldirs000077500000000000000000000034111322272076000165210ustar00rootroot00000000000000#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain errstatus=0 dirmode="" usage="\ Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." # process command line arguments while test $# -gt 0 ; do case "${1}" in -h | --help | --h* ) # -h for help echo "${usage}" 1>&2; exit 0 ;; -m ) # -m PERM arg shift test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; } dirmode="${1}" shift ;; -- ) shift; break ;; # stop option processing -* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option * ) break ;; # first non-opt arg esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac case $dirmode in '') if mkdir -p -- . 2>/dev/null; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" fi ;; *) if mkdir -m "$dirmode" -p -- . 2>/dev/null; then echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" fi ;; esac for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr else if test ! -z "$dirmode"; then echo "chmod $dirmode $pathcomp" lasterr="" chmod "$dirmode" "$pathcomp" || lasterr=$? if test ! -z "$lasterr"; then errstatus=$lasterr fi fi fi fi pathcomp="$pathcomp/" done done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 3 # End: # mkinstalldirs ends here fluidsynth-1.1.9/src/000077500000000000000000000000001322272076000145035ustar00rootroot00000000000000fluidsynth-1.1.9/src/CMakeLists.txt000066400000000000000000000235221322272076000172470ustar00rootroot00000000000000# FluidSynth - A Software Synthesizer # # Copyright (C) 2003-2010 Peter Hanappe and others. # # 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 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307, USA # CMake based build system. Pedro Lopez-Cabanillas include_directories ( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/drivers ${CMAKE_SOURCE_DIR}/src/synth ${CMAKE_SOURCE_DIR}/src/rvoice ${CMAKE_SOURCE_DIR}/src/midi ${CMAKE_SOURCE_DIR}/src/utils ${CMAKE_SOURCE_DIR}/src/sfloader ${CMAKE_SOURCE_DIR}/src/bindings ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${PTHREADS_INCLUDE_DIR} ) include_directories ( SYSTEM ${GLIB_INCLUDEDIR} ${GLIB_INCLUDE_DIRS} ) # ************ library ************ if ( READLINE_SUPPORT ) include_directories ( ${READLINE_INCLUDE_DIR} ) endif ( READLINE_SUPPORT ) if ( PULSE_SUPPORT ) set ( fluid_pulse_SOURCES drivers/fluid_pulse.c ) include_directories ( ${PULSE_INCLUDEDIR} ${PULSE_INCLUDE_DIRS} ) endif ( PULSE_SUPPORT ) if ( ALSA_SUPPORT ) set ( fluid_alsa_SOURCES drivers/fluid_alsa.c ) include_directories ( ${ALSA_INCLUDEDIR} ${ALSA_INCLUDE_DIRS} ) endif ( ALSA_SUPPORT ) if ( COREAUDIO_SUPPORT ) set ( fluid_coreaudio_SOURCES drivers/fluid_coreaudio.c ) endif ( COREAUDIO_SUPPORT ) if ( COREMIDI_SUPPORT ) set ( fluid_coremidi_SOURCES drivers/fluid_coremidi.c ) endif ( COREMIDI_SUPPORT ) if ( DBUS_SUPPORT ) set ( fluid_dbus_SOURCES bindings/fluid_rtkit.c bindings/fluid_rtkit.h ) include_directories ( ${DBUS_INCLUDEDIR} ${DBUS_INCLUDE_DIRS} ) endif ( DBUS_SUPPORT ) if ( JACK_SUPPORT ) set ( fluid_jack_SOURCES drivers/fluid_jack.c ) include_directories ( ${JACK_INCLUDEDIR} ${JACK_INCLUDE_DIRS} ) endif ( JACK_SUPPORT ) if ( PORTAUDIO_SUPPORT ) set ( fluid_portaudio_SOURCES drivers/fluid_portaudio.c ) include_directories ( ${PORTAUDIO_INCLUDEDIR} ${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 ( OSS_SUPPORT ) set ( fluid_oss_SOURCES drivers/fluid_oss.c ) endif ( OSS_SUPPORT ) if ( LASH_SUPPORT OR LADCCA_SUPPORT ) set ( fluid_lash_SOURCES bindings/fluid_lash.c bindings/fluid_lash.h ) include_directories ( ${LASH_INCLUDEDIR} ${LADCCA_INCLUDEDIR} ${LASH_INCLUDE_DIRS} ${LADCCA_INCLUDE_DIRS} ) endif ( LASH_SUPPORT OR LADCCA_SUPPORT ) if ( DART_SUPPORT ) set ( fluid_dart_SOURCES drivers/fluid_dart.c ) include_directories ( ${DART_INCLUDE_DIRS} ) endif ( DART_SUPPORT ) if ( LIBSNDFILE_SUPPORT ) include_directories ( ${LIBSNDFILE_INCLUDEDIR} ${LIBSNDFILE_INCLUDE_DIRS} ) endif ( LIBSNDFILE_SUPPORT ) if ( LADSPA_SUPPORT ) set ( fluid_ladspa_SOURCES bindings/fluid_ladspa.c bindings/fluid_ladspa.h ) endif ( LADSPA_SUPPORT ) if ( MIDISHARE_SUPPORT ) set ( fluid_midishare_SOURCES drivers/fluid_midishare.c ) include_directories ( ${MidiShare_INCLUDE_DIRS} ) endif ( MIDISHARE_SUPPORT ) set ( config_SOURCES ${CMAKE_BINARY_DIR}/config.h ) if ( MSVC ) set ( config_SOURCES ${config_SOURCES} ${CMAKE_BINARY_DIR}/config_win32.h ) endif ( MSVC ) set ( libfluidsynth_SOURCES utils/fluid_conv.c utils/fluid_conv.h utils/fluid_hash.c utils/fluid_hash.h utils/fluid_list.c utils/fluid_list.h utils/fluid_ringbuffer.c utils/fluid_ringbuffer.h utils/fluid_settings.c utils/fluid_settings.h utils/fluidsynth_priv.h utils/fluid_sys.c utils/fluid_sys.h sfloader/fluid_defsfont.c sfloader/fluid_defsfont.h sfloader/fluid_ramsfont.c sfloader/fluid_ramsfont.h sfloader/fluid_sfont.h rvoice/fluid_adsr_env.c rvoice/fluid_adsr_env.h rvoice/fluid_chorus.c rvoice/fluid_chorus.h rvoice/fluid_iir_filter.c rvoice/fluid_iir_filter.h rvoice/fluid_lfo.c rvoice/fluid_lfo.h rvoice/fluid_rvoice.h rvoice/fluid_rvoice.c rvoice/fluid_rvoice_dsp.c rvoice/fluid_rvoice_event.h rvoice/fluid_rvoice_event.c rvoice/fluid_rvoice_mixer.h rvoice/fluid_rvoice_mixer.c rvoice/fluid_phase.h rvoice/fluid_rev.c rvoice/fluid_rev.h synth/fluid_chan.c synth/fluid_chan.h synth/fluid_event.c synth/fluid_event_priv.h synth/fluid_gen.c synth/fluid_gen.h synth/fluid_mod.c synth/fluid_mod.h synth/fluid_synth.c synth/fluid_synth.h synth/fluid_tuning.c synth/fluid_tuning.h synth/fluid_voice.c synth/fluid_voice.h midi/fluid_midi.c midi/fluid_midi.h midi/fluid_midi_router.c midi/fluid_midi_router.h midi/fluid_seqbind.c midi/fluid_seq.c drivers/fluid_adriver.c 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 ) set ( public_HEADERS ${CMAKE_SOURCE_DIR}/include/fluidsynth/audio.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/event.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/gen.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/log.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/midi.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/misc.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/mod.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/ramsfont.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/seq.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/seqbind.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/settings.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/sfont.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/shell.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/synth.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/types.h ${CMAKE_SOURCE_DIR}/include/fluidsynth/voice.h ${CMAKE_BINARY_DIR}/include/fluidsynth/version.h ) set ( public_main_HEADER ${CMAKE_SOURCE_DIR}/include/fluidsynth.h ) link_directories ( ${GLIB_LIBDIR} ${GLIB_LIBRARY_DIRS} ${LASH_LIBDIR} ${LASH_LIBRARY_DIRS} ${LADCCA_LIBDIR} ${LADCCA_LIBRARY_DIRS} ${JACK_LIBDIR} ${JACK_LIBRARY_DIRS} ${ALSA_LIBDIR} ${ALSA_LIBRARY_DIRS} ${PULSE_LIBDIR} ${PULSE_LIBRARY_DIRS} ${PORTAUDIO_LIBDIR} ${PORTAUDIO_LIBRARY_DIRS} ${LIBSNDFILE_LIBDIR} ${LIBSNDFILE_LIBRARY_DIRS} ${DBUS_LIBDIR} ${DBUS_LIBRARY_DIRS} ) # note: by default this target creates a shared object (or dll). To build a # static library instead, set the option BUILD_SHARED_LIBS to FALSE. add_library ( libfluidsynth ${config_SOURCES} ${fluid_alsa_SOURCES} ${fluid_coreaudio_SOURCES} ${fluid_coremidi_SOURCES} ${fluid_dart_SOURCES} ${fluid_dbus_SOURCES} ${fluid_jack_SOURCES} ${fluid_lash_SOURCES} ${fluid_ladspa_SOURCES} ${fluid_midishare_SOURCES} ${fluid_oss_SOURCES} ${fluid_portaudio_SOURCES} ${fluid_pulse_SOURCES} ${fluid_windows_SOURCES} ${libfluidsynth_SOURCES} ${public_HEADERS} ${public_main_HEADER} ) if ( MACOSX_FRAMEWORK ) set_property ( SOURCE ${public_HEADERS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/fluidsynth ) set_target_properties ( libfluidsynth PROPERTIES OUTPUT_NAME "FluidSynth" FRAMEWORK TRUE PUBLIC_HEADER "${public_main_HEADER}" FRAMEWORK_VERSION "${LIB_VERSION_CURRENT}" INSTALL_NAME_DIR "" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif ( OS2 ) set_target_properties ( libfluidsynth PROPERTIES OUTPUT_NAME "fluidsynth" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif ( WIN32 ) set_target_properties ( libfluidsynth PROPERTIES ARCHIVE_OUTPUT_NAME "fluidsynth" PREFIX "lib" OUTPUT_NAME "fluidsynth-${LIB_VERSION_CURRENT}" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) else ( MACOSX_FRAMEWORK ) set_target_properties ( libfluidsynth PROPERTIES PREFIX "lib" OUTPUT_NAME "fluidsynth" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) endif ( MACOSX_FRAMEWORK ) if ( LIBFLUID_CPPFLAGS ) set_target_properties ( libfluidsynth PROPERTIES COMPILE_FLAGS ${LIBFLUID_CPPFLAGS} ) endif ( LIBFLUID_CPPFLAGS ) target_link_libraries ( libfluidsynth ${GLIB_LIBRARIES} ${LASH_LIBRARIES} ${LADCCA_LIBRARIES} ${JACK_LIBRARIES} ${ALSA_LIBRARIES} ${PULSE_LIBRARIES} ${PORTAUDIO_LIBRARIES} ${LIBSNDFILE_LIBRARIES} ${DBUS_LIBRARIES} ${READLINE_LIBS} ${DART_LIBS} ${COREAUDIO_LIBS} ${COREMIDI_LIBS} ${WINDOWS_LIBS} ${MidiShare_LIBS} ${LIBFLUID_LIBS} ) # ************ CLI program ************ set ( fluidsynth_SOURCES fluidsynth.c ) add_executable ( fluidsynth ${fluidsynth_SOURCES} ) if ( FLUID_CPPFLAGS ) set_target_properties ( fluidsynth PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) endif ( FLUID_CPPFLAGS ) target_link_libraries ( fluidsynth libfluidsynth ${FLUID_LIBS} ) if ( MACOSX_FRAMEWORK ) install ( TARGETS fluidsynth libfluidsynth RUNTIME DESTINATION ${BIN_INSTALL_DIR} FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} ) else ( MACOSX_FRAMEWORK ) install ( TARGETS fluidsynth libfluidsynth RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY DESTINATION ${LIB_INSTALL_DIR}${LIB_SUFFIX} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}${LIB_SUFFIX} ) endif ( MACOSX_FRAMEWORK ) fluidsynth-1.1.9/src/Makefile.am000066400000000000000000000114761322272076000165500ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in AUTOMAKE_OPTIONS = subdir-objects # Tests for optional drivers if PULSE_SUPPORT fluid_pulse = drivers/fluid_pulse.c endif if ALSA_SUPPORT fluid_alsa = drivers/fluid_alsa.c endif if COREAUDIO_SUPPORT fluid_coreaudio = drivers/fluid_coreaudio.c endif if COREMIDI_SUPPORT fluid_coremidi = drivers/fluid_coremidi.c endif if DBUS_SUPPORT fluid_rtkit = bindings/fluid_rtkit.c bindings/fluid_rtkit.h endif if JACK_SUPPORT fluid_jack = drivers/fluid_jack.c endif if PORTAUDIO_SUPPORT fluid_portaudio = drivers/fluid_portaudio.c endif if MINGW32_SUPPORT fluid_windows = fluid_dll.c drivers/fluid_dsound.c drivers/fluid_winmidi.c endif if OSS_SUPPORT fluid_oss = drivers/fluid_oss.c endif # if LASH_SUPPORT || LADCCA_SUPPORT (Makefile supports OR?) if LASH_SUPPORT fluid_lash = bindings/fluid_lash.c bindings/fluid_lash.h else if LADCCA_SUPPORT fluid_lash = bindings/fluid_lash.c bindings/fluid_lash.h endif endif if DART_SUPPORT fluid_dart = drivers/fluid_dart.c endif # Extra files and optional drivers EXTRA_DIST = fluid_dll.c \ bindings/fluid_ladspa.c \ bindings/fluid_ladspa.h \ drivers/fluid_alsa.c \ drivers/fluid_coreaudio.c \ drivers/fluid_coremidi.c \ drivers/fluid_dart.c \ drivers/fluid_dsound.c \ drivers/fluid_jack.c \ drivers/fluid_midishare.c \ drivers/fluid_oss.c \ drivers/fluid_portaudio.c \ drivers/fluid_pulse.c \ drivers/fluid_sndmgr.c \ drivers/fluid_winmidi.c \ config_macos.h \ config_macosx.h \ config_macosx_pb.h \ CMakeLists.txt \ config_win32.cmake \ config_win32.h.in \ config.cmake DISTCLEANFILES = config_win32.h lib_LTLIBRARIES = libfluidsynth.la bin_PROGRAMS = fluidsynth libfluidsynth_la_SOURCES = \ $(fluid_alsa) \ $(fluid_coreaudio) \ $(fluid_coremidi) \ $(fluid_jack) \ $(fluid_ladspa) \ $(fluid_lash) \ $(fluid_oss) \ $(fluid_portaudio) \ $(fluid_pulse) \ $(fluid_windows) \ $(fluid_dart) \ $(fluid_rtkit) \ utils/fluid_conv.c \ utils/fluid_conv.h \ utils/fluid_hash.c \ utils/fluid_hash.h \ utils/fluid_list.c \ utils/fluid_list.h \ utils/fluid_ringbuffer.c \ utils/fluid_ringbuffer.h \ utils/fluid_settings.c \ utils/fluid_settings.h \ utils/fluidsynth_priv.h \ utils/fluid_sys.c \ utils/fluid_sys.h \ sfloader/fluid_defsfont.c \ sfloader/fluid_defsfont.h \ sfloader/fluid_ramsfont.c \ sfloader/fluid_ramsfont.h \ sfloader/fluid_sfont.h \ rvoice/fluid_adsr_env.c \ rvoice/fluid_adsr_env.h \ rvoice/fluid_chorus.c \ rvoice/fluid_chorus.h \ rvoice/fluid_iir_filter.c \ rvoice/fluid_iir_filter.h \ rvoice/fluid_lfo.c \ rvoice/fluid_lfo.h \ rvoice/fluid_rvoice.h \ rvoice/fluid_rvoice.c \ rvoice/fluid_rvoice_dsp.c \ rvoice/fluid_rvoice_event.h \ rvoice/fluid_rvoice_event.c \ rvoice/fluid_rvoice_mixer.h \ rvoice/fluid_rvoice_mixer.c \ rvoice/fluid_phase.h \ rvoice/fluid_rev.c \ rvoice/fluid_rev.h \ synth/fluid_chan.c \ synth/fluid_chan.h \ synth/fluid_event.c \ synth/fluid_event_priv.h \ synth/fluid_gen.c \ synth/fluid_gen.h \ synth/fluid_mod.c \ synth/fluid_mod.h \ synth/fluid_synth.c \ synth/fluid_synth.h \ synth/fluid_tuning.c \ synth/fluid_tuning.h \ synth/fluid_voice.c \ synth/fluid_voice.h \ midi/fluid_midi.c \ midi/fluid_midi.h \ midi/fluid_midi_router.c \ midi/fluid_midi_router.h \ midi/fluid_seqbind.c \ midi/fluid_seq.c \ drivers/fluid_adriver.c \ 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_ladspa.c \ bindings/fluid_ladspa.h \ bindings/fluid_filerenderer.c INCLUDES = -I$(top_srcdir)/include \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/drivers \ -I$(top_srcdir)/src/synth \ -I$(top_srcdir)/src/rvoice \ -I$(top_srcdir)/src/midi \ -I$(top_srcdir)/src/utils \ -I$(top_srcdir)/src/sfloader \ -I$(top_srcdir)/src/bindings \ $(LASH_CFLAGS) $(LADCCA_CFLAGS) \ $(READLINE_CFLAGS) $(JACK_CFLAGS) $(ALSA_CFLAGS) $(PULSE_CFLAGS) \ $(PORTAUDIO_CFLAGS) $(DART_CFLAGS) $(GLIB_CFLAGS) $(LIBSNDFILE_CFLAGS) \ $(DBUS_CFLAGS) libfluidsynth_la_LIBADD = $(LIBFLUID_LIBS) $(LASH_LIBS) $(LADCCA_LIBS) \ $(READLINE_LIBS) $(COREAUDIO_LIBS) $(COREMIDI_LIBS) $(JACK_LIBS) \ $(ALSA_LIBS) $(PULSE_LIBS) $(PORTAUDIO_LIBS) $(DART_LIBS) \ $(GLIB_LIBS) $(LIBSNDFILE_LIBS) $(DBUS_LIBS) libfluidsynth_la_LDFLAGS = \ -version-info @LT_VERSION_INFO@ \ -export-dynamic $(LIBFLUID_LDFLAGS) libfluidsynth_la_CPPFLAGS = $(LIBFLUID_CPPFLAGS) fluidsynth_SOURCES = fluidsynth.c fluidsynth_LDADD = libfluidsynth.la fluidsynth_CPPFLAGS = $(FLUIDSYNTH_CPPFLAGS) if GNU_LD_SUPPORT fluidsynth_LDFLAGS = -Wl,--as-needed endif fluidsynth-1.1.9/src/bindings/000077500000000000000000000000001322272076000163005ustar00rootroot00000000000000fluidsynth-1.1.9/src/bindings/fluid_cmd.c000066400000000000000000001571271322272076000204070ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 #include "fluidsynth_priv.h" #include "fluid_cmd.h" #include "fluid_synth.h" #include "fluid_settings.h" #include "fluid_hash.h" #include "fluid_sys.h" #include "fluid_midi_router.h" #include "fluid_sfont.h" #include "fluid_chan.h" #if WITH_READLINE #include #include #endif #define MAX_TOKENS 100 /* LADSPA plugins need lots of parameters */ #define MAX_COMMAND_LEN 1024 /* max command length accepted by fluid_command() */ #define FLUID_WORKLINELENGTH 1024 /* LADSPA plugins use long command lines */ struct _fluid_shell_t { fluid_settings_t* settings; fluid_cmd_handler_t* handler; fluid_thread_t* thread; fluid_istream_t in; fluid_ostream_t out; }; static int fluid_shell_run(fluid_shell_t* shell); static void fluid_shell_init(fluid_shell_t* shell, fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_istream_t in, fluid_ostream_t out); static int fluid_handle_voice_count (fluid_synth_t *synth, int ac, char **av, fluid_ostream_t out); void fluid_shell_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "shell.prompt", "", 0, NULL, NULL); fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0, NULL, NULL); } /** the table of all handled commands */ fluid_cmd_t fluid_commands[] = { { "help", "general", (fluid_cmd_func_t) fluid_handle_help, NULL, "help Show help topics ('help TOPIC' for more info)" }, { "quit", "general", (fluid_cmd_func_t) fluid_handle_quit, NULL, "quit Quit the synthesizer" }, { "noteon", "event", (fluid_cmd_func_t) fluid_handle_noteon, NULL, "noteon chan key vel Send noteon" }, { "noteoff", "event", (fluid_cmd_func_t) fluid_handle_noteoff, NULL, "noteoff chan key Send noteoff" }, { "pitch_bend", "event", (fluid_cmd_func_t) fluid_handle_pitch_bend, NULL, "pitch_bend chan offset Bend pitch" }, { "pitch_bend_range", "event", (fluid_cmd_func_t) fluid_handle_pitch_bend_range, NULL, "pitch_bend chan range Set bend pitch range" }, { "cc", "event", (fluid_cmd_func_t) fluid_handle_cc, NULL, "cc chan ctrl value Send control-change message" }, { "prog", "event", (fluid_cmd_func_t) fluid_handle_prog, NULL, "prog chan num Send program-change message" }, { "select", "event", (fluid_cmd_func_t) fluid_handle_select, NULL, "select chan sfont bank prog Combination of bank-select and program-change" }, { "load", "general", (fluid_cmd_func_t) fluid_handle_load, NULL, "load file [reset] [bankofs] Load SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, { "unload", "general", (fluid_cmd_func_t) fluid_handle_unload, NULL, "unload id [reset] Unload SoundFont by ID (reset=0|1, default 1)"}, { "reload", "general", (fluid_cmd_func_t) fluid_handle_reload, NULL, "reload id Reload the SoundFont with the specified ID" }, { "fonts", "general", (fluid_cmd_func_t) fluid_handle_fonts, NULL, "fonts Display the list of loaded SoundFonts" }, { "inst", "general", (fluid_cmd_func_t) fluid_handle_inst, NULL, "inst font Print out the available instruments for the font" }, { "channels", "general", (fluid_cmd_func_t) fluid_handle_channels, NULL, "channels [-verbose] Print out preset of all channels" }, { "interp", "general", (fluid_cmd_func_t) fluid_handle_interp, NULL, "interp num Choose interpolation method for all channels" }, { "interpc", "general", (fluid_cmd_func_t) fluid_handle_interpc, NULL, "interpc chan num Choose interpolation method for one channel" }, { "rev_preset", "reverb", (fluid_cmd_func_t) fluid_handle_reverbpreset, NULL, "rev_preset num Load preset num into the reverb unit" }, { "rev_setroomsize", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetroomsize, NULL, "rev_setroomsize num Change reverb room size" }, { "rev_setdamp", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetdamp, NULL, "rev_setdamp num Change reverb damping" }, { "rev_setwidth", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetwidth, NULL, "rev_setwidth num Change reverb width" }, { "rev_setlevel", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetlevel, NULL, "rev_setlevel num Change reverb level" }, { "reverb", "reverb", (fluid_cmd_func_t) fluid_handle_reverb, NULL, "reverb [0|1|on|off] Turn the reverb on or off" }, { "cho_set_nr", "chorus", (fluid_cmd_func_t) fluid_handle_chorusnr, NULL, "cho_set_nr n Use n delay lines (default 3)" }, { "cho_set_level", "chorus", (fluid_cmd_func_t) fluid_handle_choruslevel, NULL, "cho_set_level num Set output level of each chorus line to num" }, { "cho_set_speed", "chorus", (fluid_cmd_func_t) fluid_handle_chorusspeed, NULL, "cho_set_speed num Set mod speed of chorus to num (Hz)" }, { "cho_set_depth", "chorus", (fluid_cmd_func_t) fluid_handle_chorusdepth, NULL, "cho_set_depth num Set chorus modulation depth to num (ms)" }, { "chorus", "chorus", (fluid_cmd_func_t) fluid_handle_chorus, NULL, "chorus [0|1|on|off] Turn the chorus on or off" }, { "gain", "general", (fluid_cmd_func_t) fluid_handle_gain, NULL, "gain value Set the master gain (0 < gain < 5)" }, { "voice_count", "general", (fluid_cmd_func_t) fluid_handle_voice_count, NULL, "voice_count Get number of active synthesis voices" }, { "tuning", "tuning", (fluid_cmd_func_t) fluid_handle_tuning, NULL, "tuning name bank prog Create a tuning with name, bank number, \n" " and program number (0 <= bank,prog <= 127)" }, { "tune", "tuning", (fluid_cmd_func_t) fluid_handle_tune, NULL, "tune bank prog key pitch Tune a key" }, { "settuning", "tuning", (fluid_cmd_func_t) fluid_handle_settuning, NULL, "settuning chan bank prog Set the tuning for a MIDI channel" }, { "resettuning", "tuning", (fluid_cmd_func_t) fluid_handle_resettuning, NULL, "resettuning chan Restore the default tuning of a MIDI channel" }, { "tunings", "tuning", (fluid_cmd_func_t) fluid_handle_tunings, NULL, "tunings Print the list of available tunings" }, { "dumptuning", "tuning", (fluid_cmd_func_t) fluid_handle_dumptuning, NULL, "dumptuning bank prog Print the pitch details of the tuning" }, { "reset", "general", (fluid_cmd_func_t) fluid_handle_reset, NULL, "reset System reset (all notes off, reset controllers)" }, { "set", "settings", (fluid_cmd_func_t) fluid_handle_set, NULL, "set name value Set the value of a controller or settings" }, { "get", "settings", (fluid_cmd_func_t) fluid_handle_get, NULL, "get name Get the value of a controller or settings" }, { "info", "settings", (fluid_cmd_func_t) fluid_handle_info, NULL, "info name Get information about a controller or settings" }, { "settings", "settings", (fluid_cmd_func_t) fluid_handle_settings, NULL, "settings Print out all settings" }, { "echo", "general", (fluid_cmd_func_t) fluid_handle_echo, NULL, "echo arg Print arg" }, /* LADSPA-related commands */ #ifdef LADSPA { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL, "ladspa_clear Resets LADSPA effect unit to bypass state"}, { "ladspa_add", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_add, NULL, "ladspa_add lib plugin n1 <- p1 n2 -> p2 ... Loads and connects LADSPA plugin"}, { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_start, NULL, "ladspa_start Starts LADSPA effect unit"}, { "ladspa_declnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_declnode, NULL, "ladspa_declnode node value Declares control node `node' with value `value'"}, { "ladspa_setnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_setnode, NULL, "ladspa_setnode node value Assigns `value' to `node'"}, #endif { "router_clear", "router", (fluid_cmd_func_t) fluid_midi_router_handle_clear, NULL, "router_clear Clears all routing rules from the midi router"}, { "router_default", "router", (fluid_cmd_func_t) fluid_midi_router_handle_default, NULL, "router_default Resets the midi router to default state"}, { "router_begin", "router", (fluid_cmd_func_t) fluid_midi_router_handle_begin, NULL, "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule"}, { "router_chan", "router", (fluid_cmd_func_t) fluid_midi_router_handle_chan, NULL, "router_chan min max mul add filters and maps midi channels on current rule"}, { "router_par1", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par1, NULL, "router_par1 min max mul add filters and maps parameter 1 (key/ctrl nr)"}, { "router_par2", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par2, NULL, "router_par2 min max mul add filters and maps parameter 2 (vel/cc val)"}, { "router_end", "router", (fluid_cmd_func_t) fluid_midi_router_handle_end, NULL, "router_end closes and commits the current routing rule"}, { NULL, NULL, NULL, NULL, NULL } }; /** * Process a string command. * NOTE: FluidSynth 1.0.8 and above no longer modifies the 'cmd' string. * @param handler FluidSynth command handler * @param cmd Command string (NOTE: Gets modified by FluidSynth prior to 1.0.8) * @param out Output stream to display command response to * @return Integer value corresponding to: -1 on command error, 0 on success, * 1 if 'cmd' is a comment or is empty and -2 if quit was issued */ int fluid_command(fluid_cmd_handler_t* handler, const char *cmd, fluid_ostream_t out) { int result, num_tokens = 0; char** tokens = NULL; if (cmd[0] == '#' || cmd[0] == '\0') { return 1; } if (!g_shell_parse_argv(cmd, &num_tokens, &tokens, NULL)) { fluid_ostream_printf(out, "Error parsing command\n"); return -1; } result = fluid_cmd_handler_handle(handler, num_tokens, &tokens[0], out); g_strfreev(tokens); return result; } /** * Create a new FluidSynth command shell. * @param settings Setting parameters to use with the shell * @param handler Command handler * @param in Input stream * @param out Output stream * @param thread TRUE if shell should be run in a separate thread, FALSE to run * it in the current thread (function blocks until "quit") * @return New shell instance or NULL on error */ fluid_shell_t * new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_istream_t in, fluid_ostream_t out, int thread) { fluid_shell_t* shell = FLUID_NEW(fluid_shell_t); if (shell == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } fluid_shell_init(shell, settings, handler, in, out); if (thread) { shell->thread = new_fluid_thread("shell", (fluid_thread_func_t) fluid_shell_run, shell, 0, TRUE); if (shell->thread == NULL) { delete_fluid_shell(shell); return NULL; } } else { shell->thread = NULL; fluid_shell_run(shell); } return shell; } static void fluid_shell_init(fluid_shell_t* shell, fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_istream_t in, fluid_ostream_t out) { shell->settings = settings; shell->handler = handler; shell->in = in; shell->out = out; } /** * Delete a FluidSynth command shell. * @param shell Command shell instance */ void delete_fluid_shell(fluid_shell_t* shell) { if (shell->thread != NULL) { delete_fluid_thread(shell->thread); } FLUID_FREE(shell); } static int fluid_shell_run(fluid_shell_t* shell) { char workline[FLUID_WORKLINELENGTH]; char* prompt = NULL; int cont = 1; int errors = 0; int n; if (shell->settings) fluid_settings_dupstr(shell->settings, "shell.prompt", &prompt); /* ++ alloc prompt */ /* handle user input */ while (cont) { n = fluid_istream_readline(shell->in, shell->out, prompt ? prompt : "", workline, FLUID_WORKLINELENGTH); if (n < 0) { 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)) { case 1: /* empty line or comment */ break; case -1: /* erronous command */ errors++; case 0: /* valid command */ break; case -2: /* quit */ cont = 0; break; } if (n == 0) { break; } } if (prompt) FLUID_FREE (prompt); /* -- free prompt */ return errors; } /** * A convenience function to create a shell interfacing to standard input/output * console streams. * @param settings Settings instance for the shell * @param handler Command handler callback */ void fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler) { fluid_shell_t shell; fluid_shell_init(&shell, settings, handler, fluid_get_stdin(), fluid_get_stdout()); fluid_shell_run(&shell); } /** * Execute shell commands in a file. * @param handler Command handler callback * @param filename File name * @return 0 on success, a value >1 on error */ int fluid_source(fluid_cmd_handler_t* handler, const char *filename) { int file; fluid_shell_t shell; int result; #ifdef WIN32 file = _open(filename, _O_RDONLY); #else file = open(filename, O_RDONLY); #endif if (file < 0) { return file; } fluid_shell_init(&shell, NULL, handler, file, fluid_get_stdout()); result = fluid_shell_run(&shell); #ifdef WIN32 _close(file); #else close(file); #endif return result; } /** * Get the user specific FluidSynth command file name. * @param buf Caller supplied string buffer to store file name to. * @param len Length of \a buf * @return Returns \a buf pointer or NULL if no user command file for this system type. */ char* fluid_get_userconf(char* buf, int len) { #if defined(WIN32) || defined(MACOS9) return NULL; #else char* home = getenv("HOME"); if (home == NULL) { return NULL; } else { snprintf(buf, len, "%s/.fluidsynth", home); return buf; } #endif } /** * Get the system FluidSynth command file name. * @param buf Caller supplied string buffer to store file name to. * @param len Length of \a buf * @return Returns \a buf pointer or NULL if no system command file for this system type. */ char* fluid_get_sysconf(char* buf, int len) { #if defined(WIN32) || defined(MACOS9) return NULL; #else snprintf(buf, len, "/etc/fluidsynth.conf"); return buf; #endif } /* * handlers */ int fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 3) { fluid_ostream_printf(out, "noteon: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { fluid_ostream_printf(out, "noteon: invalid argument\n"); return -1; } return fluid_synth_noteon(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "noteoff: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "noteon: invalid argument\n"); return -1; } return fluid_synth_noteoff(synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_pitch_bend(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "pitch_bend: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "pitch_bend: invalid argument\n"); return -1; } return fluid_synth_pitch_bend(synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_pitch_bend_range(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int channum; int value; if (ac < 2) { fluid_ostream_printf(out, "pitch_bend_range: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "pitch_bend_range: invalid argument\n"); return -1; } channum = atoi(av[0]); value = atoi(av[1]); fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], value); return 0; } int fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 3) { fluid_ostream_printf(out, "cc: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { fluid_ostream_printf(out, "cc: invalid argument\n"); return -1; } return fluid_synth_cc(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "prog: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "prog: invalid argument\n"); return -1; } return fluid_synth_program_change(synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int sfont_id; int chan; int bank; int prog; if (ac < 4) { fluid_ostream_printf(out, "preset: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2]) || !fluid_is_number(av[3])) { fluid_ostream_printf(out, "preset: invalid argument\n"); return -1; } chan = atoi(av[0]); sfont_id = atoi(av[1]); bank = atoi(av[2]); prog = atoi(av[3]); if (sfont_id != 0) { return fluid_synth_program_select(synth, chan, sfont_id, bank, prog); } else { if (fluid_synth_bank_select(synth, chan, bank) == FLUID_OK) { return fluid_synth_program_change(synth, chan, prog); } return FLUID_FAILED; } } int fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int font; fluid_sfont_t* sfont; fluid_preset_t preset; int offset; if (ac < 1) { fluid_ostream_printf(out, "inst: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "inst: invalid argument\n"); return -1; } font = atoi(av[0]); sfont = fluid_synth_get_sfont_by_id(synth, font); offset = fluid_synth_get_bank_offset(synth, font); if (sfont == NULL) { fluid_ostream_printf(out, "inst: invalid font number\n"); return -1; } fluid_sfont_iteration_start(sfont); while (fluid_sfont_iteration_next(sfont, &preset)) { fluid_ostream_printf(out, "%03d-%03d %s\n", fluid_preset_get_banknum(&preset) + offset, fluid_preset_get_num(&preset), fluid_preset_get_name(&preset)); } return 0; } int fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_synth_channel_info_t info; int verbose = 0; int i; if (ac > 0 && strcmp( av[0], "-verbose") == 0) verbose = 1; for (i = 0; i < fluid_synth_count_midi_channels (synth); i++) { fluid_synth_get_channel_info (synth, i, &info); if (!verbose) fluid_ostream_printf (out, "chan %d, %s\n", i, info.assigned ? info.name : "no preset"); else fluid_ostream_printf (out, "chan %d, sfont %d, bank %d, preset %d, %s\n", i, info.sfont_id, info.bank, info.program, info.assigned ? info.name : "no preset"); } return 0; } int fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { char buf[1024]; int id; int reset = 1; int offset = 0; if (ac < 1) { fluid_ostream_printf(out, "load: too few arguments\n"); return -1; } if (ac == 2) { reset = atoi(av[1]); } if (ac == 3) { offset = atoi(av[2]); } /* Load the SoundFont without resetting the programs. The reset will * be done later (if requested). */ id = fluid_synth_sfload(synth, fluid_expand_path(av[0], buf, 1024), 0); if (id == -1) { fluid_ostream_printf(out, "failed to load the SoundFont\n"); return -1; } else { fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id); } if (offset) { fluid_synth_set_bank_offset(synth, id, offset); } /* The reset should be done after the offset is set. */ if (reset) { fluid_synth_program_reset(synth); } return 0; } int fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int reset = 1; if (ac < 1) { fluid_ostream_printf(out, "unload: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "unload: expected a number as argument\n"); return -1; } if (ac == 2) { reset = atoi(av[1]); } if (fluid_synth_sfunload(synth, atoi(av[0]), reset) != 0) { fluid_ostream_printf(out, "failed to unload the SoundFont\n"); return -1; } return 0; } int fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "reload: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "reload: expected a number as argument\n"); return -1; } if (fluid_synth_sfreload(synth, atoi(av[0])) == -1) { fluid_ostream_printf(out, "failed to reload the SoundFont\n"); return -1; } return 0; } int fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int i; fluid_sfont_t* sfont; int num; num = fluid_synth_sfcount(synth); if (num == 0) { fluid_ostream_printf(out, "no SoundFont loaded (try load)\n"); return 0; } fluid_ostream_printf(out, "ID Name\n"); for (i = 0; i < num; i++) { sfont = fluid_synth_get_sfont(synth, i); if (sfont) { fluid_ostream_printf(out, "%2d %s\n", fluid_sfont_get_id(sfont), fluid_sfont_get_name(sfont)); } else { fluid_ostream_printf(out, "sfont is \"NULL\" for index %d\n", i); } } return 0; } int fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { /* fluid_ostream_printf(out, "Dvr=%s, Dev=%s\n", */ /* fluid_midi_handler_get_driver_name(midi), */ /* fluid_midi_handler_get_device_name(midi)); */ /* fluid_ostream_printf(out, "Stat=%s, On=%d, Off=%d, Prog=%d, Pbend=%d, Err=%d\n", */ /* fluid_midi_handler_get_status(midi), */ /* fluid_midi_handler_get_event_count(midi, 0x90), */ /* fluid_midi_handler_get_event_count(midi, 0x80), */ /* fluid_midi_handler_get_event_count(midi, 0xc0), */ /* fluid_midi_handler_get_event_count(midi, 0xe0), */ /* fluid_midi_handler_get_event_count(midi, 0)); */ fluid_ostream_printf(out, "not yet implemented\n"); return -1; } /* Purpose: * Response to 'rev_preset' command. * Load the values from a reverb preset into the reverb unit. */ int fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int reverb_preset_number; if (ac < 1) { fluid_ostream_printf(out, "rev_preset: too few arguments\n"); return -1; } reverb_preset_number = atoi(av[0]); if (fluid_synth_set_reverb_preset(synth, reverb_preset_number)!=FLUID_OK){ fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n"); return -1; }; return 0; } /* Purpose: * Response to 'rev_setroomsize' command. * Load the new room size into the reverb unit. */ int fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t room_size; if (ac < 1) { fluid_ostream_printf(out, "rev_setroomsize: too few arguments.\n"); return -1; } room_size = atof(av[0]); if (room_size < 0){ fluid_ostream_printf(out, "rev_setroomsize: Room size must be positive!\n"); return -1; } if (room_size > 1.2){ fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n"); return -1; } fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ROOMSIZE, room_size, 0.0, 0.0, 0.0); return 0; } /* Purpose: * Response to 'rev_setdamp' command. * Load the new damp factor into the reverb unit. */ int fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t damp; if (ac < 1) { fluid_ostream_printf(out, "rev_setdamp: too few arguments.\n"); return -1; } damp = atof(av[0]); if ((damp < 0.0f) || (damp > 1)){ fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n"); return -1; } fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_DAMPING, 0.0, damp, 0.0, 0.0); return 0; } /* Purpose: * Response to 'rev_setwidth' command. * Load the new width into the reverb unit. */ int fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t width; if (ac < 1) { fluid_ostream_printf(out, "rev_setwidth: too few arguments.\n"); return -1; } width = atof(av[0]); if ((width < 0) || (width > 100)){ fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n"); return 0; } fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_WIDTH, 0.0, 0.0, width, 0.0); return 0; } /* Purpose: * Response to 'rev_setlevel' command. * Load the new level into the reverb unit. */ int fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t level; if (ac < 1) { fluid_ostream_printf(out, "rev_setlevel: too few arguments.\n"); return -1; } level = atof(av[0]); if (fabs(level) > 30){ fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n"); return 0; } fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_LEVEL, 0.0, 0.0, 0.0, level); return 0; } /* Purpose: * Response to 'reverb' command. * Change the FLUID_REVERB flag in the synth */ int fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "reverb: too few arguments.\n"); return -1; } if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { fluid_synth_set_reverb_on(synth,0); } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { fluid_synth_set_reverb_on(synth,1); } else { fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]); return -1; } return 0; } /* Purpose: * Response to 'chorus_setnr' command */ int fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int nr; if (ac < 1) { fluid_ostream_printf(out, "cho_set_nr: too few arguments.\n"); return -1; } nr = atoi(av[0]); return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_NR, nr, 0.0, 0.0, 0.0, 0); } /* Purpose: * Response to 'chorus_setlevel' command */ int fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t level; if (ac < 1) { fluid_ostream_printf(out, "cho_set_level: too few arguments.\n"); return -1; } level = atof(av[0]); return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0.0, 0.0, 0); } /* Purpose: * Response to 'chorus_setspeed' command */ int fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t speed; if (ac < 1) { fluid_ostream_printf(out, "cho_set_speed: too few arguments.\n"); return -1; } speed = atof(av[0]); return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_SPEED, 0, 0.0, speed, 0.0, 0); } /* Purpose: * Response to 'chorus_setdepth' command */ int fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t depth; if (ac < 1) { fluid_ostream_printf(out, "cho_set_depth: too few arguments.\n"); return -1; } depth = atof(av[0]); return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_DEPTH, 0, 0.0, 0.0, depth, 0); } int fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "chorus: too few arguments\n"); return -1; } if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { fluid_synth_set_chorus_on(synth,0); } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { fluid_synth_set_chorus_on(synth,1); } else { fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]); return -1; } return 0; } /* Purpose: * Response to the 'echo' command. * The command itself is useful, when the synth is used via TCP/IP. * It can signal for example, that a list of commands has been processed. */ int fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "echo: too few arguments.\n"); return -1; } fluid_ostream_printf(out, "%s\n",av[0]); return 0; } int fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "source: too few arguments.\n"); return -1; } fluid_source(handler, av[0]); return 0; } /* Purpose: * Response to 'gain' command. */ int fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { float gain; if (ac < 1) { fluid_ostream_printf(out, "gain: too few arguments.\n"); return -1; } gain = atof(av[0]); if ((gain < 0.0f) || (gain > 5.0f)) { fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n"); return -1; }; fluid_synth_set_gain(synth, gain); return 0; } /* Response to voice_count command */ static int fluid_handle_voice_count (fluid_synth_t *synth, int ac, char **av, fluid_ostream_t out) { fluid_ostream_printf (out, "voice_count: %d\n", fluid_synth_get_active_voice_count (synth)); return FLUID_OK; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int interp; int chan=-1; /* -1: Set all channels */ if (ac < 1) { fluid_ostream_printf(out, "interp: too few arguments.\n"); return -1; } interp = atoi(av[0]); if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value\n"); return -1; }; fluid_synth_set_interp_method(synth, chan, interp); return 0; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int interp; int chan; if (ac < 2) { fluid_ostream_printf(out, "interpc: too few arguments.\n"); return -1; } chan = atoi(av[0]); interp = atoi(av[1]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "interp: Bad value for channel number.\n"); return -1; }; if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n"); return -1; }; fluid_synth_set_interp_method(synth, chan, interp); return 0; } int fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { char *name; int bank, prog; if (ac < 3) { fluid_ostream_printf(out, "tuning: too few arguments.\n"); return -1; } name = av[0]; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return -1; } bank = atoi(av[1]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return -1; } prog = atoi(av[2]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tuning: invalid program number.\n"); return -1; }; fluid_synth_create_key_tuning(synth, bank, prog, name, NULL); return 0; } int fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog, key; double pitch; if (ac < 4) { fluid_ostream_printf(out, "tune: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } bank = atoi(av[0]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tune: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n"); return -1; } prog = atoi(av[1]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tune: invalid program number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n"); return -1; } key = atoi(av[2]); if ((key < 0) || (key >= 128)){ fluid_ostream_printf(out, "tune: invalid key number.\n"); return -1; }; pitch = atof(av[3]); if (pitch < 0.0f) { fluid_ostream_printf(out, "tune: invalid pitch.\n"); return -1; }; fluid_synth_tune_notes(synth, bank, prog, 1, &key, &pitch, 0); return 0; } int fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int chan, bank, prog; if (ac < 3) { fluid_ostream_printf(out, "settuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } chan = atoi(av[0]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "tune: invalid channel number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return -1; } bank = atoi(av[1]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return -1; } prog = atoi(av[2]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tuning: invalid program number.\n"); return -1; }; fluid_synth_select_tuning(synth, chan, bank, prog); return 0; } int fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int chan; if (ac < 1) { fluid_ostream_printf(out, "resettuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } chan = atoi(av[0]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "tune: invalid channel number.\n"); return -1; }; fluid_synth_reset_tuning(synth, chan); return 0; } int fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog; char name[256]; int count = 0; fluid_synth_tuning_iteration_start(synth); while (fluid_synth_tuning_iteration_next(synth, &bank, &prog)) { fluid_synth_tuning_dump(synth, bank, prog, name, 256, NULL); fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name); count++; } if (count == 0) { fluid_ostream_printf(out, "No tunings available\n"); } return 0; } int fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog, i, res; double pitch[128]; char name[256]; if (ac < 2) { fluid_ostream_printf(out, "dumptuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n"); return -1; } bank = atoi(av[0]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "dumptuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n"); return -1; } prog = atoi(av[1]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "dumptuning: invalid program number.\n"); return -1; }; res = fluid_synth_tuning_dump(synth, bank, prog, name, 256, pitch); if (FLUID_OK != res) { fluid_ostream_printf(out, "Tuning %03d-%03d does not exist.\n", bank, prog); return -1; } fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name); for (i = 0; i < 128; i++) { fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]); } return 0; } int fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int hints; int ival; if (ac < 2) { fluid_ostream_printf(out, "set: Too few arguments.\n"); return -1; } switch (fluid_settings_get_type (synth->settings, av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf (out, "set: Parameter '%s' not found.\n", av[0]); break; case FLUID_INT_TYPE: hints = fluid_settings_get_hints (synth->settings, av[0]); if (hints & FLUID_HINT_TOGGLED) { if (FLUID_STRCMP (av[1], "yes") == 0 || FLUID_STRCMP (av[1], "True") == 0 || FLUID_STRCMP (av[1], "TRUE") == 0 || FLUID_STRCMP (av[1], "true") == 0 || FLUID_STRCMP (av[1], "T") == 0) ival = 1; else ival = atoi (av[1]); } else ival = atoi (av[1]); fluid_synth_setint (synth, av[0], ival); break; case FLUID_NUM_TYPE: fluid_synth_setnum (synth, av[0], atof (av[1])); break; case FLUID_STR_TYPE: fluid_synth_setstr(synth, av[0], av[1]); break; case FLUID_SET_TYPE: fluid_ostream_printf (out, "set: Parameter '%s' is a node.\n", av[0]); break; } return 0; } int fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "get: too few arguments.\n"); return -1; } switch (fluid_settings_get_type(fluid_synth_get_settings(synth), av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "get: no such setting '%s'.\n", av[0]); return -1; case FLUID_NUM_TYPE: { double value; fluid_synth_getnum(synth, av[0], &value); fluid_ostream_printf(out, "%.3f", value); break; } case FLUID_INT_TYPE: { int value; fluid_synth_getint(synth, av[0], &value); fluid_ostream_printf(out, "%d", value); break; } case FLUID_STR_TYPE: { char* s; fluid_synth_dupstr(synth, av[0], &s); /* ++ alloc string */ fluid_ostream_printf(out, "%s", s ? s : "NULL"); if (s) FLUID_FREE (s); /* -- free string */ break; } case FLUID_SET_TYPE: fluid_ostream_printf(out, "%s is a node", av[0]); break; } return 0; } struct _fluid_handle_settings_data_t { int len; fluid_synth_t* synth; fluid_ostream_t out; }; static void fluid_handle_settings_iter1(void* data, char* name, int type) { struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; int len = FLUID_STRLEN(name); if (len > d->len) { d->len = len; } } static void fluid_handle_settings_iter2(void* data, char* name, int type) { struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; int len = FLUID_STRLEN(name); fluid_ostream_printf(d->out, "%s", name); while (len++ < d->len) { fluid_ostream_printf(d->out, " "); } fluid_ostream_printf(d->out, " "); switch (fluid_settings_get_type(fluid_synth_get_settings(d->synth), name)) { case FLUID_NUM_TYPE: { double value; fluid_synth_getnum(d->synth, name, &value); fluid_ostream_printf(d->out, "%.3f\n", value); break; } case FLUID_INT_TYPE: { int value, hints; fluid_synth_getint(d->synth, name, &value); hints = fluid_settings_get_hints (d->synth->settings, name); if (!(hints & FLUID_HINT_TOGGLED)) fluid_ostream_printf(d->out, "%d\n", value); else fluid_ostream_printf(d->out, "%s\n", value ? "True" : "False"); break; } case FLUID_STR_TYPE: { char* s; fluid_synth_dupstr(d->synth, name, &s); /* ++ alloc string */ fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL"); if (s) FLUID_FREE (s); /* -- free string */ break; } } } int fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { struct _fluid_handle_settings_data_t data; data.len = 0; data.synth = synth; data.out = out; fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter1); fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter2); return 0; } struct _fluid_handle_option_data_t { int first; fluid_ostream_t out; }; void fluid_handle_print_option(void* data, char* name, char* option) { struct _fluid_handle_option_data_t* d = (struct _fluid_handle_option_data_t*) data; if (d->first) { fluid_ostream_printf(d->out, "%s", option); d->first = 0; } else { fluid_ostream_printf(d->out, ", %s", option); } } int fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_settings_t* settings = fluid_synth_get_settings(synth); struct _fluid_handle_option_data_t data; if (ac < 1) { fluid_ostream_printf(out, "info: too few arguments.\n"); return -1; } switch (fluid_settings_get_type(settings, av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "info: no such setting '%s'.\n", av[0]); return -1; case FLUID_NUM_TYPE: { double value, min, max; fluid_settings_getnum_range(settings, av[0], &min, &max); fluid_settings_getnum(settings, av[0], &value); fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: number\n"); fluid_ostream_printf(out, "Value: %.3f\n", value); fluid_ostream_printf(out, "Minimum value: %.3f\n", min); fluid_ostream_printf(out, "Maximum value: %.3f\n", max); fluid_ostream_printf(out, "Default value: %.3f\n", fluid_settings_getnum_default(settings, av[0])); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_INT_TYPE: { int value, min, max, def, hints; fluid_settings_getint_range(settings, av[0], &min, &max); fluid_settings_getint(settings, av[0], &value); hints = fluid_settings_get_hints(settings, av[0]); def = fluid_settings_getint_default (settings, av[0]); fluid_ostream_printf(out, "%s:\n", av[0]); if (!(hints & FLUID_HINT_TOGGLED)) { fluid_ostream_printf(out, "Type: integer\n"); fluid_ostream_printf(out, "Value: %d\n", value); fluid_ostream_printf(out, "Minimum value: %d\n", min); fluid_ostream_printf(out, "Maximum value: %d\n", max); fluid_ostream_printf(out, "Default value: %d\n", def); } else { fluid_ostream_printf(out, "Type: boolean\n"); fluid_ostream_printf(out, "Value: %s\n", value ? "True" : "False"); fluid_ostream_printf(out, "Default value: %s\n", def ? "True" : "False"); } fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_STR_TYPE: { char *s; fluid_settings_dupstr(settings, av[0], &s); /* ++ alloc string */ fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: string\n"); fluid_ostream_printf(out, "Value: %s\n", s ? s : "NULL"); fluid_ostream_printf(out, "Default value: %s\n", fluid_settings_getstr_default(settings, av[0])); if (s) FLUID_FREE (s); data.out = out; data.first = 1; fluid_ostream_printf(out, "Options: "); fluid_settings_foreach_option (settings, av[0], &data, fluid_handle_print_option); fluid_ostream_printf(out, "\n"); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_SET_TYPE: fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: node\n"); break; } return 0; } int fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_synth_system_reset(synth); return 0; } int fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_ostream_printf(out, "cheers!\n"); return -2; } int fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { /* Purpose: * Prints the help text for the command line commands. * Can be used as follows: * - help * - help (topic), where (topic) is 'general', 'chorus', etc. * - help all */ char* topic = "help"; /* default, if no topic is given */ int count = 0; int i; fluid_ostream_printf(out, "\n"); /* 1st argument (optional): help topic */ if (ac >= 1) { topic = av[0]; } if (strcmp(topic,"help") == 0){ /* "help help": Print a list of all topics */ fluid_ostream_printf(out, "*** Help topics:***\n" "help all (prints all topics)\n"); for (i = 0; fluid_commands[i].name != NULL; i++) { int listed_first_time = 1; int ii; for (ii = 0; ii < i; ii++){ if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){ listed_first_time = 0; }; /* if topic has already been listed */ }; /* for all topics (inner loop) */ if (listed_first_time){ fluid_ostream_printf(out, "help %s\n",fluid_commands[i].topic); }; }; /* for all topics (outer loop) */ } else { /* help (arbitrary topic or "all") */ for (i = 0; fluid_commands[i].name != NULL; i++) { fluid_cmd_t cmd = fluid_commands[i]; if (cmd.help != NULL) { if (strcmp(topic,"all") == 0 || strcmp(topic,cmd.topic) == 0){ fluid_ostream_printf(out, "%s\n", fluid_commands[i].help); count++; }; /* if it matches the topic */ }; /* if help text exists */ }; /* foreach command */ if (count == 0){ fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n"); }; }; return 0; } int fluid_is_number(char* a) { while (*a != 0) { if (((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) { return 0; } a++; } return 1; } int fluid_is_empty(char* a) { while (*a != 0) { if ((*a != ' ') && (*a != '\t') && (*a != '\n') && (*a != '\r')) { return 0; } a++; } return 1; } char* fluid_expand_path(char* path, char* new_path, int len) { #if defined(WIN32) || defined(MACOS9) snprintf(new_path, len - 1, "%s", path); #else if ((path[0] == '~') && (path[1] == '/')) { char* home = getenv("HOME"); if (home == NULL) { snprintf(new_path, len - 1, "%s", path); } else { snprintf(new_path, len - 1, "%s%s", home, &path[1]); } } else { snprintf(new_path, len - 1, "%s", path); } #endif new_path[len - 1] = 0; return new_path; } /* * Command */ fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd) { fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t); if (copy == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } copy->name = FLUID_STRDUP(cmd->name); copy->topic = FLUID_STRDUP(cmd->topic); copy->help = FLUID_STRDUP(cmd->help); copy->handler = cmd->handler; copy->data = cmd->data; return copy; } void delete_fluid_cmd(fluid_cmd_t* cmd) { if (cmd->name) { FLUID_FREE(cmd->name); } if (cmd->topic) { FLUID_FREE(cmd->topic); } if (cmd->help) { FLUID_FREE(cmd->help); } FLUID_FREE(cmd); } /* * Command handler */ static void fluid_cmd_handler_destroy_hash_value (void *value) { delete_fluid_cmd ((fluid_cmd_t *)value); } /** * Create a new command handler. * @param synth If not NULL, all the default synthesizer commands will be * added to the new handler. * @return New command handler */ fluid_cmd_handler_t * new_fluid_cmd_handler(fluid_synth_t* synth) { int i; fluid_cmd_handler_t* handler; fluid_cmd_t source = { "source", "general", (fluid_cmd_func_t) fluid_handle_source, NULL, "source filename Load a file and parse every line as a command" }; handler = new_fluid_hashtable_full (fluid_str_hash, fluid_str_equal, NULL, fluid_cmd_handler_destroy_hash_value); if (handler == NULL) { return NULL; } if (synth != NULL) { for (i = 0; fluid_commands[i].name != NULL; i++) { fluid_commands[i].data = synth; fluid_cmd_handler_register(handler, &fluid_commands[i]); fluid_commands[i].data = NULL; } } source.data = handler; fluid_cmd_handler_register(handler, &source); return handler; } /** * Delete a command handler. * @param handler Command handler to delete */ void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler) { delete_fluid_hashtable (handler); } /** * Register a new command to the handler. * @param handler Command handler instance * @param cmd Command info (gets copied) * @return #FLUID_OK if command was inserted, #FLUID_FAILED otherwise */ int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd) { fluid_cmd_t* copy = fluid_cmd_copy(cmd); fluid_hashtable_insert(handler, copy->name, copy); return FLUID_OK; } /** * Unregister a command from a command handler. * @param handler Command handler instance * @param cmd Name of the command * @return TRUE if command was found and unregistered, FALSE otherwise */ int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd) { return fluid_hashtable_remove(handler, cmd); } int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { fluid_cmd_t* cmd; cmd = fluid_hashtable_lookup(handler, av[0]); if (cmd && cmd->handler) return (*cmd->handler)(cmd->data, ac - 1, av + 1, out); fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); return -1; } #if !defined(WITHOUT_SERVER) struct _fluid_server_t { fluid_server_socket_t* socket; fluid_settings_t* settings; fluid_server_newclient_func_t newclient; void* data; fluid_list_t* clients; fluid_mutex_t mutex; }; static int fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr); static void fluid_server_close(fluid_server_t* server); /** * Create a new TCP/IP command shell server. * @param settings Settings instance to use for the shell * @param newclient Callback function to call for each new client connection * @param data User defined data to pass to \a newclient callback * @return New shell server instance or NULL on error */ fluid_server_t* new_fluid_server(fluid_settings_t* settings, fluid_server_newclient_func_t newclient, void* data) { fluid_server_t* server; int port; server = FLUID_NEW(fluid_server_t); if (server == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } server->settings = settings; server->clients = NULL; server->newclient = newclient; server->data = data; fluid_mutex_init(server->mutex); fluid_settings_getint(settings, "shell.port", &port); server->socket = new_fluid_server_socket(port, (fluid_server_func_t) fluid_server_handle_connection, server); if (server->socket == NULL) { FLUID_FREE(server); return NULL; } return server; } /** * Delete a TCP/IP shell server. * @param server Shell server instance */ void delete_fluid_server(fluid_server_t* server) { if (server == NULL) { return; } fluid_server_close(server); FLUID_FREE(server); } static void fluid_server_close(fluid_server_t* server) { fluid_list_t* list; fluid_list_t* clients; fluid_client_t* client; if (server == NULL) { return; } fluid_mutex_lock(server->mutex); clients = server->clients; server->clients = NULL; fluid_mutex_unlock(server->mutex); list = clients; while (list) { client = fluid_list_get(list); fluid_client_quit(client); list = fluid_list_next(list); } delete_fluid_list(clients); if (server->socket) { delete_fluid_server_socket(server->socket); server->socket = NULL; } } static int fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr) { fluid_client_t* client; fluid_cmd_handler_t* handler; handler = server->newclient(server->data, addr); if (handler == NULL) { return -1; } client = new_fluid_client(server, server->settings, handler, client_socket); if (client == NULL) { return -1; } fluid_server_add_client(server, client); return 0; } void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client) { fluid_mutex_lock(server->mutex); server->clients = fluid_list_append(server->clients, client); fluid_mutex_unlock(server->mutex); } void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client) { fluid_mutex_lock(server->mutex); server->clients = fluid_list_remove(server->clients, client); fluid_mutex_unlock(server->mutex); } /** * Join a shell server thread (wait until it quits). * @param server Shell server instance * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_server_join(fluid_server_t* server) { return fluid_server_socket_join(server->socket); } struct _fluid_client_t { fluid_server_t* server; fluid_settings_t* settings; fluid_cmd_handler_t* handler; fluid_socket_t socket; fluid_thread_t* thread; }; static void fluid_client_run(fluid_client_t* client) { fluid_shell_t shell; fluid_shell_init(&shell, client->settings, client->handler, fluid_socket_get_istream(client->socket), fluid_socket_get_ostream(client->socket)); fluid_shell_run(&shell); fluid_server_remove_client(client->server, client); delete_fluid_client(client); } fluid_client_t* new_fluid_client(fluid_server_t* server, fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_socket_t sock) { fluid_client_t* client; client = FLUID_NEW(fluid_client_t); if (client == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } client->server = server; client->socket = sock; client->settings = settings; client->handler = handler; client->thread = new_fluid_thread("client", (fluid_thread_func_t) fluid_client_run, client, 0, FALSE); if (client->thread == NULL) { fluid_socket_close(sock); FLUID_FREE(client); return NULL; } return client; } void fluid_client_quit(fluid_client_t* client) { if (client->socket != INVALID_SOCKET) { fluid_socket_close(client->socket); client->socket = INVALID_SOCKET; } FLUID_LOG(FLUID_DBG, "fluid_client_quit: joining"); fluid_thread_join(client->thread); FLUID_LOG(FLUID_DBG, "fluid_client_quit: done"); } void delete_fluid_client(fluid_client_t* client) { if (client->socket != INVALID_SOCKET) { fluid_socket_close(client->socket); client->socket = INVALID_SOCKET; } if (client->thread != NULL) { delete_fluid_thread(client->thread); client->thread = NULL; } FLUID_FREE(client); } #endif /* WITHOUT_SERVER */ fluidsynth-1.1.9/src/bindings/fluid_cmd.h000066400000000000000000000130131322272076000203750ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_CMD_H #define _FLUID_CMD_H #include "fluidsynth_priv.h" void fluid_shell_settings(fluid_settings_t* settings); /** some help functions */ int fluid_is_number(char* a); int fluid_is_empty(char* a); char* fluid_expand_path(char* path, char* new_path, int len); /** the handlers for the command lines */ int fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_pitch_bend(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_pitch_bend_range(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); int fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); int fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd); void delete_fluid_cmd(fluid_cmd_t* cmd); int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client); void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client); fluid_client_t* new_fluid_client(fluid_server_t* server, fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_socket_t sock); void delete_fluid_client(fluid_client_t* client); void fluid_client_quit(fluid_client_t* client); #endif /* _FLUID_CMD_H */ fluidsynth-1.1.9/src/bindings/fluid_filerenderer.c000066400000000000000000000345141322272076000223040ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* * Low-level routines for file output. */ #include #include "fluidsynth_priv.h" #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_settings.h" #if LIBSNDFILE_SUPPORT #include #endif struct _fluid_file_renderer_t { fluid_synth_t* synth; #if LIBSNDFILE_SUPPORT SNDFILE* sndfile; float* buf; #else FILE* file; short* buf; #endif int period_size; int buf_size; }; #if LIBSNDFILE_SUPPORT /* Default file type used, if none specified and auto extension search fails */ #define FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE SF_FORMAT_WAV /* File audio format names. * !! Keep in sync with format_ids[] */ const char *format_names[] = { "s8", "s16", "s24", "s32", "u8", "float", "double", NULL /* Terminator */ }; /* File audio format IDs. * !! Keep in sync with format_names[] */ const int format_ids[] = { SF_FORMAT_PCM_S8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32, SF_FORMAT_PCM_U8, SF_FORMAT_FLOAT, SF_FORMAT_DOUBLE }; /* File endian byte order names. * !! Keep in sync with endian_ids[] */ const char *endian_names[] = { "auto", "little", "big", "cpu", NULL }; /* File endian byte order ids. * !! Keep in sync with endian_names[] */ const int endian_ids[] = { SF_ENDIAN_FILE, SF_ENDIAN_LITTLE, SF_ENDIAN_BIG, SF_ENDIAN_CPU }; static int fluid_file_renderer_parse_options (char *filetype, char *format, char *endian, char *filename, SF_INFO *info); static int fluid_file_renderer_find_file_type (char *extension, int *type); static int fluid_file_renderer_find_valid_format (SF_INFO *info); #else /* No libsndfile support */ /* File type names. */ const char *type_names[] = { "raw", NULL /* Terminator */ }; /* File audio format names. */ const char *format_names[] = { "s16", NULL /* Terminator */ }; /* File endian byte order names. */ const char *endian_names[] = { "cpu", NULL }; #endif void fluid_file_renderer_settings (fluid_settings_t* settings) { #if LIBSNDFILE_SUPPORT SF_FORMAT_INFO finfo, cmpinfo; int major_count; int i, i2; const char **np; fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.wav", FLUID_HINT_FILENAME, NULL, NULL); fluid_settings_register_str(settings, "audio.file.type", "auto", 0, NULL, NULL); fluid_settings_register_str(settings, "audio.file.format", "s16", 0, NULL, NULL); fluid_settings_register_str(settings, "audio.file.endian", "auto", 0, NULL, NULL); fluid_settings_add_option (settings, "audio.file.type", "auto"); sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)); for (i = 0; i < major_count; i++) { finfo.format = i; sf_command (NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof (finfo)); /* Check for duplicates */ for (i2 = 0; i2 < i; i2++) { cmpinfo.format = i2; sf_command (NULL, SFC_GET_FORMAT_MAJOR, &cmpinfo, sizeof (cmpinfo)); if (FLUID_STRCMP (cmpinfo.extension, finfo.extension) == 0) break; } if (i2 == i) fluid_settings_add_option (settings, "audio.file.type", finfo.extension); } for (np = format_names; *np; np++) fluid_settings_add_option (settings, "audio.file.format", *np); for (np = endian_names; *np; np++) fluid_settings_add_option (settings, "audio.file.endian", *np); #else fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0, NULL, NULL); fluid_settings_register_str(settings, "audio.file.type", "raw", 0, NULL, NULL); fluid_settings_add_option (settings, "audio.file.type", "raw"); fluid_settings_register_str(settings, "audio.file.format", "s16", 0, NULL, NULL); fluid_settings_add_option (settings, "audio.file.format", "s16"); fluid_settings_register_str(settings, "audio.file.endian", "cpu", 0, NULL, NULL); fluid_settings_add_option (settings, "audio.file.endian", "cpu"); #endif } /** * Create a new file renderer and open the file. * @param synth The synth that creates audio data. * @return the new object, or NULL on failure * @since 1.1.0 * * NOTE: Available file types and formats depends on if libfluidsynth was * built with libsndfile support or not. If not then only RAW 16 bit output is * supported. * * Uses the following settings from the synth object: * - audio.file.name: Output filename * - audio.file.type: File type, "auto" tries to determine type from filename * extension with fallback to "wav". * - audio.file.format: Audio format * - audio.file.endian: Endian byte order, "auto" for file type's default byte order * - audio.period-size: Size of audio blocks to process * - synth.sample-rate: Sample rate to use */ fluid_file_renderer_t * new_fluid_file_renderer(fluid_synth_t* synth) { #if LIBSNDFILE_SUPPORT char *type, *format, *endian; SF_INFO info; double samplerate; int retval; #endif char *filename = NULL; fluid_file_renderer_t* dev; fluid_return_val_if_fail (synth != NULL, NULL); fluid_return_val_if_fail (synth->settings != NULL, NULL); dev = FLUID_NEW(fluid_file_renderer_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_file_renderer_t)); dev->synth = synth; fluid_settings_getint (synth->settings, "audio.period-size", &dev->period_size); #if LIBSNDFILE_SUPPORT dev->buf_size = 2 * dev->period_size * sizeof (float); dev->buf = FLUID_ARRAY(float, 2 * dev->period_size); #else dev->buf_size = 2 * dev->period_size * sizeof (short); dev->buf = FLUID_ARRAY(short, 2 * dev->period_size); #endif if (dev->buf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } fluid_settings_dupstr (synth->settings, "audio.file.name", &filename); if (filename == NULL) { FLUID_LOG(FLUID_ERR, "No file name specified"); goto error_recovery; } #if LIBSNDFILE_SUPPORT memset (&info, 0, sizeof (info)); info.format = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE | SF_FORMAT_PCM_16; fluid_settings_dupstr (synth->settings, "audio.file.type", &type); fluid_settings_dupstr (synth->settings, "audio.file.format", &format); fluid_settings_dupstr (synth->settings, "audio.file.endian", &endian); retval = fluid_file_renderer_parse_options (type, format, endian, filename, &info); if (type) FLUID_FREE (type); if (format) FLUID_FREE (format); if (endian) FLUID_FREE (endian); if (!retval) goto error_recovery; fluid_settings_getnum (synth->settings, "synth.sample-rate", &samplerate); info.samplerate = samplerate + 0.5; info.channels = 2; /* Search for valid format for given file type, if invalid and no format was specified. * To handle Ogg/Vorbis and possibly future file types with new formats. * Checking if format is SF_FORMAT_PCM_16 isn't a fool proof way to check if * format was specified or not (if user specifies "s16" itself), but should suffice. */ if (!sf_format_check (&info) && ((info.format & SF_FORMAT_SUBMASK) != SF_FORMAT_PCM_16 || !fluid_file_renderer_find_valid_format (&info))) { FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file format settings"); goto error_recovery; } dev->sndfile = sf_open (filename, SFM_WRITE, &info); if (!dev->sndfile) { FLUID_LOG(FLUID_ERR, "Failed to open audio file '%s' for writing", filename); goto error_recovery; } /* Turn on clipping and normalization of floats (-1.0 - 1.0) */ sf_command (dev->sndfile, SFC_SET_CLIPPING, NULL, SF_TRUE); sf_command (dev->sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE); #else dev->file = fopen(filename, "wb"); if (dev->file == NULL) { FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename); goto error_recovery; } #endif return dev; error_recovery: if (filename) FLUID_FREE (filename); delete_fluid_file_renderer(dev); return NULL; } /** * Set vbr encoding quality (only available with libsndfile support) * @param dev File renderer object. * @return #FLUID_OK if the quality has been successfully set, #FLUID_FAILED otherwise * @since 1.1.7 */ int fluid_file_set_encoding_quality(fluid_file_renderer_t* r, double q) { #if LIBSNDFILE_SUPPORT if (sf_command (r->sndfile, SFC_SET_VBR_ENCODING_QUALITY, &q, sizeof (double)) == SF_TRUE) return FLUID_OK; else #endif return FLUID_FAILED; } /** * Close file and destroy a file renderer object. * @param dev File renderer object. * @since 1.1.0 */ void delete_fluid_file_renderer(fluid_file_renderer_t* dev) { if (dev == NULL) { return; } #if LIBSNDFILE_SUPPORT if (dev->sndfile != NULL) { int retval = sf_close (dev->sndfile); if (retval != 0) FLUID_LOG (FLUID_WARN, "Error closing audio file: %s", sf_error_number (retval)); } #else if (dev->file != NULL) { fclose(dev->file); } #endif if (dev->buf != NULL) { FLUID_FREE(dev->buf); } FLUID_FREE(dev); return; } /** * Write period_size samples to file. * @param dev File renderer instance * @return #FLUID_OK or #FLUID_FAILED if an error occurred * @since 1.1.0 */ int fluid_file_renderer_process_block(fluid_file_renderer_t* dev) { #if LIBSNDFILE_SUPPORT int n; fluid_synth_write_float(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); n = sf_writef_float (dev->sndfile, dev->buf, dev->period_size); if (n != dev->period_size) { FLUID_LOG (FLUID_ERR, "Audio file write error: %s", sf_strerror (dev->sndfile)); return FLUID_FAILED; } return FLUID_OK; #else /* No libsndfile support */ int n, offset; fluid_synth_write_s16(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); for (offset = 0; offset < dev->buf_size; offset += n) { n = fwrite((char*) dev->buf + offset, 1, dev->buf_size - offset, dev->file); if (n < 0) { FLUID_LOG(FLUID_ERR, "Audio output file write error: %s", strerror (errno)); return FLUID_FAILED; } } return FLUID_OK; #endif } #if LIBSNDFILE_SUPPORT /** * Parse a colon separated format string and configure an SF_INFO structure accordingly. * @param filetype File type string (NULL or "auto" to attempt to identify format * by filename extension, with fallback to "wav") * @param format File audio format string or NULL to use "s16" * @param endian File endian string or NULL to use "auto" which uses the file type's * default endian byte order. * @param filename File name (used by "auto" type to determine type, based on extension) * @param info Audio file info structure to configure * @return TRUE on success, FALSE otherwise */ static int fluid_file_renderer_parse_options (char *filetype, char *format, char *endian, char *filename, SF_INFO *info) { int type = -1; /* -1 indicates "auto" type */ char *s; int i; /* If "auto" type, then use extension to search for a match */ if (!filetype || FLUID_STRCMP (filetype, "auto") == 0) { type = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE; s = FLUID_STRRCHR (filename, '.'); if (s && s[1] != '\0') { if (!fluid_file_renderer_find_file_type (s + 1, &type)) FLUID_LOG (FLUID_WARN, "Failed to determine audio file type from filename, defaulting to WAV"); } } else if (!fluid_file_renderer_find_file_type (filetype, &type)) { FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file type '%s'", filetype); return FALSE; } info->format = (info->format & ~SF_FORMAT_TYPEMASK) | type; /* Look for subtype */ if (format) { for (i = 0; format_names[i]; i++) if (FLUID_STRCMP (format, format_names[i]) == 0) break; if (!format_names[i]) { FLUID_LOG (FLUID_ERR, "Invalid or unsupported file audio format '%s'", format); return FALSE; } info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_ids[i]; } #if LIBSNDFILE_HASVORBIS /* Force subformat to vorbis as nothing else would make sense currently */ if ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) { info->format = (info->format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS; } #endif /* Look for endian */ if (endian) { for (i = 0; endian_names[i]; i++) if (FLUID_STRCMP (endian, endian_names[i]) == 0) break; if (!endian_names[i]) { FLUID_LOG (FLUID_ERR, "Invalid or unsupported endian byte order '%s'", endian); return FALSE; } info->format = (info->format & ~SF_FORMAT_ENDMASK) | endian_ids[i]; } return TRUE; } /** * Searches for a supported libsndfile file type by extension. * @param extension The extension string * @param ext_len Length of the extension string * @param type Location to store the type (unmodified if not found) * @return TRUE if found, FALSE otherwise */ static int fluid_file_renderer_find_file_type (char *extension, int *type) { SF_FORMAT_INFO finfo; int major_count; int i; sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)); for (i = 0; i < major_count; i++) { finfo.format = i; sf_command (NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof (finfo)); if (FLUID_STRCMP (extension, finfo.extension) == 0) break; } if (i < major_count) { *type = finfo.format; return TRUE; } return FALSE; } /* Search for a valid audio format for a given file type */ static int fluid_file_renderer_find_valid_format (SF_INFO *info) { SF_FORMAT_INFO format_info; int count, i; sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)); for (i = 0; i < count; i++) { format_info.format = i; sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)); info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_info.format; if (sf_format_check (info)) return TRUE; } return FALSE; } #endif fluidsynth-1.1.9/src/bindings/fluid_ladspa.c000066400000000000000000001161631322272076000211030ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* This module: 3/2002 * Author: Markus Nentwig, nentwig@users.sourceforge.net */ #define PrintErrorMessage -1 #include "fluidsynth_priv.h" #ifdef LADSPA #include /* Dynamic library functions */ #include #include "fluid_ladspa.h" #include "fluid_synth.h" /* Logging to stdout. */ //#define L(x) x;printf("\n"); #define L(x); fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth){ fluid_LADSPA_FxUnit_t* FxUnit=FLUID_NEW(fluid_LADSPA_FxUnit_t); assert(FxUnit); assert(synth); /* The default state is 'bypassed'. The Fx unit has to be turned on explicitly by the user. */ /* Those settings have to be done in order to allow fluid_LADSPA_clean. */ FxUnit->Bypass=fluid_LADSPA_Bypassed; FxUnit->NumberNodes=0; FxUnit->NumberPlugins=0; FxUnit->NumberLibs=0; FxUnit->NumberCommands=0; FxUnit->NumberUserControlNodes=0; FxUnit->synth=synth; pthread_cond_init(&FxUnit->cond,NULL); return FxUnit; }; /* Purpose: * Creates the system nodes to get data into and out of the Fx unit. */ void fluid_LADSPA_CreateSystemNodes(fluid_LADSPA_FxUnit_t* FxUnit){ char str[99]; int nr_input_nodes; int nr_fx_input_nodes; int nr_output_nodes; int temp; int i; /* Retrieve the number of synth / audio out / Fx send nodes */ assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-groups", &temp)); nr_input_nodes=(int) temp; printf("%i audio groups\n", nr_input_nodes); assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-channels", &temp)); nr_output_nodes=temp; assert(fluid_settings_getint(FxUnit->synth->settings, "synth.effects-channels", &temp)); nr_fx_input_nodes=temp; /* Create regular input nodes (associated with audio groups) */ for (i=0; i < nr_input_nodes; i++){ sprintf(str, "in%i_L",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); sprintf(str, "in%i_R",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); }; /* Create effects send nodes (for example reverb, chorus send) */ for (i=0; i < nr_fx_input_nodes; i++){ sprintf(str, "send%i_L",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); sprintf(str, "send%i_R",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); }; /* Create output nodes (usually towards the sound card) */ for (i=0; i < nr_input_nodes; i++){ sprintf(str, "out%i_L",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); sprintf(str, "out%i_R",(i+1)); fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); }; }; /* Purpose: * Creates predeclared nodes for control of the Fx unit during operation. */ void fluid_LADSPA_CreateUserControlNodes(fluid_LADSPA_FxUnit_t* FxUnit){ int i; fluid_LADSPA_Node_t* CurrentNode; for (i=0; iNumberUserControlNodes; i++){ CurrentNode=fluid_LADSPA_CreateNode(FxUnit,FxUnit->UserControlNodeNames[i],fluid_LADSPA_node_is_control); assert(CurrentNode); CurrentNode->buf[0]=FxUnit->UserControlNodeValues[i]; CurrentNode->InCount++; /* The constant counts as input */ CurrentNode->flags=fluid_LADSPA_node_is_source | fluid_LADSPA_node_is_user_ctrl; /* It is a user control node */ }; }; /* Purpose: * Returns the pointer to the shared library loaded using 'LibraryFilename' (if it has been loaded). * Return NULL otherwise. * If not: Clear Fx and abort. */ void * fluid_LADSPA_RetrieveSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ void * CurrentLib=NULL; int LibCount; for (LibCount=0; LibCountNumberLibs; LibCount++){ assert(FxUnit->ppvPluginLibNames[LibCount]); if (FLUID_STRCMP(FxUnit->ppvPluginLibNames[LibCount],LibraryFilename)==0){ CurrentLib=FxUnit->ppvPluginLibs[LibCount]; }; }; return CurrentLib; }; /* Purpose: * Loads a shared LADSPA library. * Return NULL, if failed. * TODO: use LADSPA_PATH */ void * fluid_LADSPA_LoadSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ void * LoadedLib; assert(LibraryFilename); LoadedLib=dlopen(LibraryFilename,RTLD_NOW); if (!LoadedLib){ return NULL; }; FxUnit->ppvPluginLibs[FxUnit->NumberLibs]=LoadedLib; FxUnit->ppvPluginLibNames[FxUnit->NumberLibs]=FLUID_STRDUP(LibraryFilename); FxUnit->NumberLibs++; return LoadedLib; }; /* Purpose: * Retrieves a descriptor to the plugin labeled 'PluginLabel' from the shared library. */ const LADSPA_Descriptor * fluid_LADSPA_Retrieve_Plugin_Descriptor(void * CurrentLib, char * PluginLabel){ LADSPA_Descriptor_Function pfDescriptorFunction; unsigned long lPluginIndex=0; const LADSPA_Descriptor * psDescriptor; pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); while (pfDescriptorFunction(lPluginIndex)){ psDescriptor = pfDescriptorFunction(lPluginIndex); if (FLUID_STRCMP(psDescriptor->Label, PluginLabel) == 0){ return psDescriptor; }; lPluginIndex++; }; return NULL; }; /* Purpose: * Finds out, if 'PortName' starts with 'PluginPort'. * Spaces and underscore mean the same. * The comparison is not case sensitive. * The result distinguishes between a full match (strings are equal), and a partial match (PortName starts with Plugin_Port, but is longer). */ fluid_LADSPA_Stringmatch_t fluid_LADSPA_Check_SubString_Match(const char * Plugin_Port, const char * PortName){ unsigned int CharCount; char a; char b; for(CharCount=0; CharCount='a' && a <='z'){a-=32;} if (b>='a' && b <='z'){b-=32;} if (a == ' '){a='_';}; if (b == ' '){b='_';}; if ( a != b){ return fluid_LADSPA_NoMatch; }; }; if (FLUID_STRLEN(Plugin_Port) == FLUID_STRLEN(PortName)){ return fluid_LADSPA_FullMatch; }; return fluid_LADSPA_PartialMatch; }; /* Purpose: * Load the plugins added with 'ladspa_add' and then start the Fx unit. */ int fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ fluid_LADSPA_FxUnit_t* FxUnit; int CommandLineCount; char * LibraryFilename; char * PluginLabel; char ** TokenSequence; void * CurrentLib; int NodeCount; //x int IngoingSignalCount; // Count signals going into LADSPA Fx section int OutgoingSignalCount; // Count signals going out of LADSPA Fx section int ReturnVal=FLUID_OK; /* If warnings occur, this is set to -1. */ char * LADSPA_Path = getenv("LADSPA_PATH"); L(fluid_ostream_printf(out,"ladspa_start: starting...")); assert(synth); FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); /* When calling fluid_ladspastart, the Fx unit must be 'cleared' (no plugins, no libs, no nodes). Verify this here. */ if (FxUnit->NumberPlugins || FxUnit->NumberLibs){ fluid_ostream_printf(out, "***Error006***\n" "Fx unit is currently in use!\n" "Please run the ladspa_clear command before attempting to use ladspa_add!\n"); /* In this case do _not_ clear the Fx unit. */ return(PrintErrorMessage); }; if (!FxUnit->NumberCommands){ fluid_ostream_printf(out, "***Error007***\n" "Refusing to start the Fx unit without any plugin.\n" "Use ladspa_add first!\n"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* Create predefined nodes */ L(fluid_ostream_printf(out,"ladspa_start: creating predefined nodes...")); fluid_LADSPA_CreateSystemNodes(FxUnit); /* Create predeclared nodes, that will allow to control the Fx unit during operation */ L(fluid_ostream_printf(out,"ladspa_start: creating user control nodes...")); fluid_LADSPA_CreateUserControlNodes(FxUnit); L(fluid_ostream_printf(out,"ladspa_start: Processing command lines...")); for (CommandLineCount=0; CommandLineCountNumberCommands; CommandLineCount++){ int CurrentPlugin_PortConnected[FLUID_LADSPA_MaxTokens/3]; /* For example: 100 tokens corresponds to roughly 30 ports. */ char LibFullPath[FLUID_LADSPA_MaxPathLength]; const LADSPA_Descriptor * CurrentPluginDescriptor; LADSPA_Handle CurrentPlugin; unsigned long PortCount; int TokenCount=0; char * Search; int HasASlash=0; if (FxUnit->NumberPlugins>=FLUID_LADSPA_MaxPlugins){ fluid_ostream_printf(out, "***Error003***\n" "Too many plugins at the same time (%i).\n" "Change FLUID_LADSPA_MaxPlugins!\n", FxUnit->NumberPlugins); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; L(fluid_ostream_printf(out,"Processing plugin nr. %i",FxUnit->NumberPlugins)); TokenSequence=FxUnit->LADSPA_Command_Sequence[CommandLineCount]; assert(TokenSequence); /* Check, if the library is already loaded. If not, load. */ LibraryFilename=TokenSequence[TokenCount++]; assert(LibraryFilename); L(fluid_ostream_printf(out,"Library name from ladspa_add: %s",LibraryFilename)); /* A slash-free filename refers to the LADSPA_PATH directory. Add that path, if needed. */ /* Determine, if the library filename contains a slash. * If no, try to retrieve the environment variable LADSPA_PATH. * If that fails, signal error. * Otherwise leave the filename as it is. */; /* Determine, if the library name is just the filename, or a path (including a slash) */ Search=LibraryFilename; while (*Search != '\0') { if ((*Search)== '/'){ HasASlash=1; }; Search++; }; if (!HasASlash){ if (!LADSPA_Path){ fluid_ostream_printf(out, "***Error018***\n" "The library file name %s does not include a path.\n" "The environment variable LADSPA_PATH is not set.\n" "- Use an absolute path (i.e. /home/myself/mylib.so)\n" "- For the current directory use ./mylib.so\n" "- set the environment variable LADSPA_PATH (export LADSPA_PATH=/usr/lib/ladspa)\n" "- depending on your shell, try 'setenv' instead of 'export'\n", LibraryFilename); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* if no LADSPA_PATH */ snprintf(LibFullPath,FLUID_LADSPA_MaxPathLength,"%s/%s",LADSPA_Path,LibraryFilename); /* If no slash in filename */ } else { snprintf(LibFullPath,FLUID_LADSPA_MaxPathLength,"%s",LibraryFilename); }; L(fluid_ostream_printf(out,"Full Library path name: %s",LibFullPath)); CurrentLib=fluid_LADSPA_RetrieveSharedLibrary(FxUnit, LibFullPath); if (!CurrentLib){ LADSPA_Descriptor_Function pfDescriptorFunction; L(fluid_ostream_printf(out,"Library %s not yet loaded. Loading.",LibFullPath)); if (FxUnit->NumberLibs>=FLUID_LADSPA_MaxLibs){ fluid_ostream_printf(out, "***Error004***\n" "Too many libraries open (%i)\n" "Change FLUID_LADSPA_MaxLibs",FxUnit->NumberPlugins); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* Load a LADSPA plugin library and store it for future use.*/ CurrentLib=fluid_LADSPA_LoadSharedLibrary(FxUnit, LibFullPath); if (!CurrentLib){ fluid_ostream_printf(out, "***Error008***\n" "Failed to load plugin library %s.", LibraryFilename); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; dlerror(); pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); if (!pfDescriptorFunction) { const char * pcError = dlerror(); if (!pcError) {pcError="Huh?! No error from lib!";}; fluid_ostream_printf(out, "***Error015***\n" "Unable to find ladspa_descriptor() function in plugin library file \"%s\": %s.\n" "Are you sure this is a LADSPA plugin file?\n", LibraryFilename, pcError); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; L(fluid_ostream_printf(out,"Library loaded.")); }; PluginLabel=TokenSequence[TokenCount++]; assert(PluginLabel); L(fluid_ostream_printf(out,"Plugin Label from ladspa_add: %s",PluginLabel)); /* Retrieve a 'plugin descriptor' from the library. */ L(fluid_ostream_printf(out,"Looking for the plugin labeled %s",PluginLabel)); CurrentPluginDescriptor=fluid_LADSPA_Retrieve_Plugin_Descriptor(CurrentLib, PluginLabel); /* Check, that the plugin was actually found.*/ if (CurrentPluginDescriptor==NULL){ fluid_ostream_printf(out, "***Error016***\n" "Unable to find the plugin labeled \"%s\" in plugin library file \"%s\".\n" "Hint: run analyzeplugin %s from the command line to get a list of valid plugin labels.\n", PluginLabel, LibraryFilename, LibraryFilename); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* Create an instance of the plugin type from the descriptor */ L(fluid_ostream_printf(out,"instantiating plugin %s",PluginLabel)); CurrentPlugin=CurrentPluginDescriptor ->instantiate(CurrentPluginDescriptor,44100); /* Sample rate hardcoded */ assert(CurrentPlugin); /* The descriptor ("type of plugin") and the instance are stored for each plugin instantiation. If one plugin type is instantiated several times, they will have the same descriptor, only different instances.*/ FxUnit->PluginDescriptorTable[FxUnit->NumberPlugins]=CurrentPluginDescriptor; FxUnit->PluginInstanceTable[FxUnit->NumberPlugins]=CurrentPlugin; /* * * Wire up the inputs and outputs * */ /* List for checking, that each plugin port is exactly connected once */ for (PortCount=0; PortCountPortCount; PortCount++){ CurrentPlugin_PortConnected[PortCount]=0; }; /* Note: There are three NULL tokens at the end. The last condition may be evaluated even if the first one already detects the end. */ while (TokenSequence[TokenCount] && TokenSequence[TokenCount+1] && TokenSequence[TokenCount+2]){ int CurrentPort_StringMatchCount; LADSPA_PortDescriptor CurrentPort_Descriptor; char * Plugin_Port=TokenSequence[TokenCount++]; char * Direction=TokenSequence[TokenCount++]; char * FLUID_Node=TokenSequence[TokenCount++]; fluid_LADSPA_Node_t* Current_Node; const char * PortName=NULL; int CurrentPort_Index=-1; fluid_LADSPA_Stringmatch_t StringMatchType=fluid_LADSPA_NoMatch; fluid_LADSPA_Stringmatch_t CurrentPort_StringMatchType=fluid_LADSPA_NoMatch; CurrentPort_StringMatchCount=0; L(fluid_ostream_printf(out,"Wiring %s %s %s",Plugin_Port, Direction, FLUID_Node)); /* Find the port number on the plugin, that belongs to "Plugin_Port". * Match the identifier specified by the user against the first characters * of the port name. * * If the given identifier matches several port names only partly, then the input is ambiguous, an error * message results. * * Example: cmt.so, limit_peak: This plugin uses the labels * - Output Envelope Attack (s) * - Output Envelope Decay (s) * - Output * * The user input 'Output' matches the first two labels partly, the third fully. This will be accepted. * The user input 'Out' matches all three only partly, this results in an error message. */ for (PortCount=0; PortCountPortCount; PortCount++){ PortName=CurrentPluginDescriptor->PortNames[PortCount]; StringMatchType=fluid_LADSPA_Check_SubString_Match(Plugin_Port, PortName); /* If a full-string match has been found earlier, reject all partial matches. */ if (StringMatchType==fluid_LADSPA_FullMatch || (StringMatchType==fluid_LADSPA_PartialMatch && CurrentPort_StringMatchType != fluid_LADSPA_FullMatch)){ if(StringMatchType==fluid_LADSPA_FullMatch && CurrentPort_StringMatchType==fluid_LADSPA_FullMatch){ fluid_ostream_printf(out, "***Error027***\n" "While processing plugin %s: The port label %s appears more than once!\n" "This is an error in the plugin itself. Please correct it or use another plugin.",PluginLabel, Plugin_Port); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; CurrentPort_Index=PortCount; CurrentPort_StringMatchType=StringMatchType; CurrentPort_StringMatchCount++; }; /* if suitable match */ }; /* For port count */ /* Several partial matches? Then the identifier is not unique. */ if (CurrentPort_StringMatchCount > 1 && CurrentPort_StringMatchType == fluid_LADSPA_PartialMatch){ fluid_ostream_printf(out, "***Error019***\n" "While processing plugin %s: The identifier %s matches more than one plugin port.\n" "Please use more letters for the port name. If needed, replace spaces with underscores (_).\n" "This error will not occur, if you use the full name of a port.\n",PluginLabel, Plugin_Port); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; if (CurrentPort_Index<0){ fluid_ostream_printf(out, "***Error017***\n" "Unable to find port '%s' on plugin %s\n" "Port names are:\n",Plugin_Port,PluginLabel); for (PortCount=0; PortCountPortCount; PortCount++){ printf("- `%s'\n",CurrentPluginDescriptor->PortNames[PortCount]); }; fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; CurrentPort_Descriptor=CurrentPluginDescriptor->PortDescriptors[CurrentPort_Index]; assert(CurrentPort_Descriptor); /* Retrieve the node with the right name. */ Current_Node=fluid_LADSPA_RetrieveNode(FxUnit,FLUID_Node); #define PortIsAudio LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor)) #define PortIsControl LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor)) if (!Current_Node){ /* Doesn't exist? Then create it. */ if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ fluid_ostream_printf(out, "***Error005***\n" "Too many nodes (%i)\n" "Change FLUID_LADSPA_MaxNodes",FxUnit->NumberNodes); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; if (PortIsAudio){ Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_audio); } else if (PortIsControl){ Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_control); } else { fluid_ostream_printf(out, "***Error025***\n" "Plugin port number %i is neither input nor output!\n" "This is an error in the plugin.\n" "Please check plugin sourcecode.\n", CurrentPort_Index); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; }; assert(Current_Node); /* * * Check flowgraph for some possible errors. * */ if (FLUID_STRCMP(Direction,"->")==0){ /* Data from plugin to FLUID, into node * * *** Rule: **** * A node may not have more than one data source.*/ if (Current_Node->InCount !=0){ fluid_ostream_printf(out, "***Error009***\n" "Plugin %s tries to feed data from output %s into node %s, which is already connected to a data source.\n",PluginLabel,Plugin_Port,FLUID_Node); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; Current_Node->InCount++; } else if (FLUID_STRCMP(Direction,"<-")==0){ /* Data from FLUID to plugin, out of node * * This check verifies the integrity of the flow graph: * *** Rule *** * The execution order of the plugins is the order, in which they are programmed. * The plugins must be ordered so, that the input of a plugin is already computed at the time of its execution. * If the user tries to read data out of a node that has not yet an input, then something is wrong.*/ assert(Current_Node->InCount<=1); if (Current_Node->InCount !=1){ fluid_ostream_printf(out, "***Error010***\n" "Plugin %s tries to read data through input %s from node %s.\n" "But at this point there is no valid data at that node.\n" "Please check the flowgraph and especially the execution order!\n",PluginLabel,Plugin_Port,FLUID_Node); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; Current_Node->OutCount++; } else { fluid_ostream_printf(out, "***Error024***\n" "Syntax error: Illegal `arrow' `%s', expecting -> or <-\n", Direction); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* In any case, there must be a valid data source for the port at this time. */ assert(Current_Node->InCount==1); /* Keep track on the number of connections to each port. * This error occurs only, if an attempt is made to connect one port twice (i.e. ladspa_add libname pluginname port <- nodex port <- nodey) */ if (CurrentPlugin_PortConnected[CurrentPort_Index]){ fluid_ostream_printf(out, "***Error011***\n" "Refusing to connect twice to port %s on plugin %s.\n",CurrentPluginDescriptor->PortNames[CurrentPort_Index], PluginLabel); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* * * Connect the port * */ L(fluid_ostream_printf(out,"Connecting %i",CurrentPort_Index)); CurrentPluginDescriptor->connect_port (CurrentPlugin, CurrentPort_Index, Current_Node->buf ); CurrentPlugin_PortConnected[CurrentPort_Index]++; }; /* While Tokensequence (more connections) */ /* * * Check for left-over tokens * */ if (TokenSequence[TokenCount]){ char * T1="";char * T2="";char * T3=""; if (TokenSequence[TokenCount]){T1=TokenSequence[TokenCount];}; if (TokenSequence[TokenCount+1]){T2=TokenSequence[TokenCount+1];}; if (TokenSequence[TokenCount+2]){T3=TokenSequence[TokenCount+2];}; fluid_ostream_printf(out, "***Error012***\n" "Leftover tokens: %s %s %s...\n",T1,T2,T3); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* * * Check, that all plugin ports are connected * */ L(fluid_ostream_printf(out,"Checking left-over ports")); assert(CurrentPluginDescriptor); for (PortCount=0; PortCountPortCount; PortCount++){ assert(CurrentPlugin_PortConnected[PortCount] <=1); if (CurrentPlugin_PortConnected[PortCount] !=1){ fluid_ostream_printf(out, "***Error013***\nPlugin: %s. Port %s is unconnected!\n",PluginLabel, CurrentPluginDescriptor->PortNames[PortCount]); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; }; /* * *Run activate function on plugin, where possible * */ if (CurrentPluginDescriptor->activate !=NULL){ CurrentPluginDescriptor->activate(CurrentPlugin); }; FxUnit->NumberPlugins++; }; /* For CommandLineCount: once for each new command (i.e. plugin instantiation request from user */ /* * * Further flow graph checks * */ L(fluid_ostream_printf(out,"Checking flow graph")); IngoingSignalCount=0; OutgoingSignalCount=0; for (NodeCount=0; NodeCountNumberNodes; NodeCount++){ fluid_LADSPA_Node_t* Current_Node; Current_Node=FxUnit->Nodelist[NodeCount]; assert(Current_Node); if (Current_Node->flags & fluid_LADSPA_node_is_source){ IngoingSignalCount+=Current_Node->OutCount; } else if (Current_Node->flags & fluid_LADSPA_node_is_sink){ OutgoingSignalCount+=Current_Node->InCount; } else { /* A node without any input doesn't make sense. * The flow graph check aborts with an error. This case cannot happen. */ if (Current_Node->InCount==0 && !Current_Node->flags && fluid_LADSPA_node_is_dummy){ /* There can only be one warning at a time. */ fluid_ostream_printf(out, "***Warning020***" "No input into node %s.\n" "Use '_' as first char in nodename to suppress this warning.\n" "Hint: Check for typos in the node name.\n",Current_Node->Name); /* A warning can also be printed as an error message (check fluid_cmd.c). The only difference between the return values -1 and 0 is, that -1 prints the result. */ }; ReturnVal=PrintErrorMessage; }; /* A node without any output doesn't make sense. */ if (Current_Node->OutCount==0 && !Current_Node->flags && fluid_LADSPA_node_is_dummy){ fluid_ostream_printf(out, "***Warning021***\n" "No output from node %s.\n" "Use '_' as first char in nodename to suppress this warning.\n" "Hint: Check for typos in the node name.\n",Current_Node->Name); ReturnVal=PrintErrorMessage; }; /* A free-flying node simply cannot happen. */ assert(Current_Node->OutCount+Current_Node->InCount); }; /* Foreach node */ /* Issue a warning, if no signal goes into the Fx section. */ if (IngoingSignalCount==0){ fluid_ostream_printf(out, "***Warning022***\n" "You have not connected anything to the synthesizer section (in1_L, in1_R).\n"); ReturnVal=PrintErrorMessage; }; /* Issue a warning, if no signal leaves the Fx section. */ if (OutgoingSignalCount==0){ fluid_ostream_printf(out, "***Warning023***\n" "You have not connected anything to the output (out1_L, out1_R).\n"); }; /* Finally turn on the Fx unit. */ FxUnit->Bypass=fluid_LADSPA_Active; L(fluid_ostream_printf(out,"LADSPA Init OK")); return(ReturnVal); }; void fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* FxUnit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]){ int i; int ii; int nr_audio_channels; int nr_fx_sends; int nr_groups; int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); char str[99]; fluid_LADSPA_Node_t* n; int temp; /* Retrieve the number of synth / audio out / Fx send nodes */ assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-groups", &temp)); nr_groups=(int) temp; assert(fluid_settings_getint(FxUnit->synth->settings, "synth.audio-channels", &temp)); nr_audio_channels=temp; assert(fluid_settings_getint(FxUnit->synth->settings, "synth.effects-channels", &temp)); nr_fx_sends=temp; /* Fixme: Retrieving nodes via names is inefficient * (but not that bad, because the interesting nodes are always at the start of the list). */ /* Input and output are processed via the same buffers. Therefore the effect is bypassed by just skipping everything else. */ if (FxUnit->Bypass==fluid_LADSPA_Bypassed){ return; }; if (FxUnit->Bypass==fluid_LADSPA_BypassRequest){ FxUnit->Bypass=fluid_LADSPA_Bypassed; pthread_mutex_lock(&FxUnit->mutex); pthread_cond_broadcast(&FxUnit->cond); pthread_mutex_unlock(&FxUnit->mutex); L(printf("LADSPA_Run: Command line asked for bypass of Fx unit. Acknowledged.")); return; }; assert(FxUnit); /* Clear the output buffers, if they are not connected anywhere */ for (ii=0; ii < nr_audio_channels; ii++){ sprintf(str, "out%i_L",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); if (n->InCount == 0){ /* printf("Output node %s is not connected -> clear buffer\n", str); */ FLUID_MEMSET(n->buf, 0, byte_size); }; sprintf(str, "out%i_R",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); if (n->InCount == 0){ /* printf("Output node %s is not connected -> clear buffer\n", str); */ FLUID_MEMSET(n->buf, 0, byte_size); }; }; /* Prepare the incoming data: * Convert fluid_real_t data type to LADSPA_Data type */ for (ii=0; ii < nr_groups; ii++){ fluid_real_t* src_buf=left_buf[ii]; sprintf(str, "in%i_L",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); assert(FLUID_BUFSIZE % 2 == 0); /* Add a very small high frequency signal. This avoids denormal number problems. */ for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); i++; n->buf[i]=(LADSPA_Data)(src_buf[i]); i++; }; src_buf=right_buf[ii]; sprintf(str, "in%i_R",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); /* Add a very small high frequency signal. This avoids denormal number problems. */ for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); i++; n->buf[i]=(LADSPA_Data)(src_buf[i]); i++; }; }; /* Effect send paths */ for (ii=0; ii < nr_fx_sends; ii++){ sprintf(str, "send%i_L",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); for (i=0; ibuf[i]=(LADSPA_Data)(fx_left_buf[ii][i]); }; sprintf(str, "send%i_R",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); for (i=0; ibuf[i]=(LADSPA_Data)(fx_right_buf[ii][i]); }; }; /* Run each plugin on a block of data. * The execution order has been checked during setup.*/ for (i=0; iNumberPlugins; i++){ FxUnit->PluginDescriptorTable[i]->run(FxUnit->PluginInstanceTable[i],FLUID_BUFSIZE); }; /* Copy the data from the output nodes back to the synth. */ for (ii=0; ii < nr_audio_channels; ii++){ fluid_real_t* dest_buf=left_buf[ii]; sprintf(str, "out%i_L",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); for (i=0; ibuf[i]; }; dest_buf=right_buf[ii]; sprintf(str, "out%i_R",(ii+1)); n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); for (i=0; ibuf[i]; }; }; }; fluid_LADSPA_Node_t* fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name){ int i=0; assert(FxUnit);assert(Name); for (i=0; iNumberNodes; i++){ assert(FxUnit->Nodelist[i]); if (FLUID_STRCMP(FxUnit->Nodelist[i]->Name,Name)==0){ return FxUnit->Nodelist[i]; }; }; return NULL; }; /* Purpose: * Creates a new node from the node name given by the user. */ fluid_LADSPA_Node_t* fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags){ int Dummy=0; fluid_LADSPA_Node_t* NewNode; assert(FxUnit); assert(Name); // printf("Flags is %i\n",flags); L(printf("Create node: %s",Name)); if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ printf( "***Error014***\n" "Too many nodes (%i)\n" "Change FLUID_LADSPA_MaxNodes.\n",FxUnit->NumberNodes); fluid_LADSPA_clear(FxUnit); return NULL; }; /* Don't allow node names, which start with -, 0..9 */ if (Name[0] == '-' || (Name[0]>='0' && Name[0]<='9')){ printf( "***Error026***\n" "The node name %s starts with a digit / minus sign!\n" "Please use a letter to start a node name.\n" "A constant node is created by using `#' as first character,\n" "for example #-2.5.\n", Name); fluid_LADSPA_clear(FxUnit); return NULL; }; /* A nodename starting with "_" is a possible dummy node, which may (but need not) act as a data sink or source (dummy node). */ if (Name[0] == ' '){ /* ??? Should be '_' ??? */ Dummy=1; }; NewNode=FLUID_NEW(fluid_LADSPA_Node_t);assert(NewNode); if (flags && fluid_LADSPA_node_is_audio){ /* Audio node contains buffer. */ NewNode->buf=FLUID_ARRAY(LADSPA_Data, (FLUID_BUFSIZE));assert(NewNode->buf); /* It is permitted to use a dummy node without input. Therefore clear all node buffers at startup. */ FLUID_MEMSET(NewNode->buf, 0, (FLUID_BUFSIZE*sizeof(LADSPA_Data))); } else if (flags & fluid_LADSPA_node_is_control){ /* Control node contains single value. */ NewNode->buf=FLUID_ARRAY(LADSPA_Data, 1);assert(NewNode->buf); } else { assert(0); }; NewNode->Name=FLUID_STRDUP(Name);assert(NewNode->Name); if (Dummy){ flags |= fluid_LADSPA_node_is_dummy; }; NewNode->InCount=0; NewNode->OutCount=0; NewNode->flags=flags; /* A nodename starting with "#" means that the node holds a constant value. */ if (NewNode->Name[0] == '#'){ assert(flags & fluid_LADSPA_node_is_control); /* Skip the first character => +1 */ NewNode->buf[0]=(LADSPA_Data)atof(NewNode->Name+1); NewNode->InCount++; }; if (flags & fluid_LADSPA_node_is_source){ NewNode->InCount++; // printf("****************************** Source!\n"); } else if (flags & fluid_LADSPA_node_is_sink){ NewNode->OutCount++; }; FxUnit->Nodelist[FxUnit->NumberNodes++]=NewNode; L(printf("Node %s created.",Name)); return NewNode; }; void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit){ int i; int ii; L(printf("ladspa_clear")); assert(FxUnit); if (FxUnit->Bypass==fluid_LADSPA_Active){ L(printf("clear: Requesting bypass from synthesis thread")); /* Bypass the Fx unit before anything else. * Reason: Not a good idea to release plugins, while another thread runs them. */ FxUnit->Bypass=fluid_LADSPA_BypassRequest; pthread_mutex_lock(&FxUnit->mutex); pthread_cond_wait(&FxUnit->cond,&FxUnit->mutex); pthread_mutex_unlock(&FxUnit->mutex); L(printf("clear: Synthesis thread has switched to bypass.")); } else { L(printf("clear: Fx unit was already bypassed. No action needed.")); }; L(printf("Clear all user control node declarations")); for (i=0; iNumberUserControlNodes; i++){ FLUID_FREE(FxUnit->UserControlNodeNames[i]); }; FxUnit->NumberUserControlNodes=0; L(printf("Clear all plugin instances")); for (i=0; iNumberPlugins; i++){ assert(FxUnit->PluginDescriptorTable[i]); assert(FxUnit->PluginInstanceTable[i]); /* Run deactivate function on plugin, if possible */ if (FxUnit->PluginDescriptorTable[i]->deactivate){ FxUnit->PluginDescriptorTable[i]->deactivate(FxUnit->PluginInstanceTable[i]); }; FxUnit->PluginDescriptorTable[i]->cleanup(FxUnit->PluginInstanceTable[i]); }; FxUnit->NumberPlugins=0; L(printf("Clear all nodes")); /* Only after removing plugins! */ for (i=0; iNumberNodes; i++){ FLUID_FREE(FxUnit->Nodelist[i]->buf); FLUID_FREE(FxUnit->Nodelist[i]); }; FxUnit->NumberNodes=0; L(printf("Clear all plugin libraries")); for (i=0; iNumberLibs; i++){ assert(FxUnit->ppvPluginLibs[i]); dlclose(FxUnit->ppvPluginLibs[i]); assert(FxUnit->ppvPluginLibNames[i]); FLUID_FREE(FxUnit->ppvPluginLibNames[i]); }; FxUnit->NumberLibs=0; L(printf("Clear all command lines")); for (i=0; iNumberCommands;i++){ ii=0; assert(FxUnit->LADSPA_Command_Sequence[i]); while (FxUnit->LADSPA_Command_Sequence[i][ii]){ FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i][ii]); ii++; }; FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i]); }; FxUnit->NumberCommands=0; }; int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ int i; //char * Token; char ** CommandLine; fluid_LADSPA_FxUnit_t* FxUnit; assert(synth); FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); if (ac>=FLUID_LADSPA_MaxTokens){ /* Can't be tested. fluidsynth limits the number of tokens. */ printf("***Error001***\n" "Too many ports.\nChange FLUID_LADSPA_MaxTokens!"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; if (ac<2){ printf("***Error002***\n" "ladspa_add needs at least two arguments - libname and plugin name!"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; if (FxUnit->NumberCommands>=FLUID_LADSPA_MaxPlugins){ printf("***Error032***\n" "Too many plugins.\nChange FLUID_LADSPA_MaxPlugins!"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; /* CommandLine (token sequence) is terminated with NULL. * Add two more NULLs, so that a chunk of three tokens can be checked later without risk.*/ CommandLine=FLUID_ARRAY(char*, (ac+3));assert(CommandLine); for (i=0; iLADSPA_Command_Sequence[FxUnit->NumberCommands]=CommandLine; FxUnit->NumberCommands++; return(FLUID_OK); }; int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ //int i; //char * Token; //char ** CommandLine; char * NodeName; fluid_real_t NodeValue; fluid_LADSPA_FxUnit_t* FxUnit; assert(synth); FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); if (ac<2){ printf("***Error028***\n" "ladspa_declnode needs two arguments - node name and value!\n"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; if (FxUnit->NumberUserControlNodes>=FLUID_LADSPA_MaxNodes){ printf("***Error033***\n" "Too many user-control nodes.\nChange FLUID_LADSPA_MaxNodes!"); fluid_LADSPA_clear(FxUnit); return(PrintErrorMessage); }; NodeName=FLUID_STRDUP(av[0]); assert(NodeName); NodeValue=atof(av[1]); FxUnit->UserControlNodeNames[FxUnit->NumberUserControlNodes]=NodeName; FxUnit->UserControlNodeValues[FxUnit->NumberUserControlNodes]=NodeValue; FxUnit->NumberUserControlNodes++; return(FLUID_OK); }; int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ //int i; //char * Token; char * NodeName; fluid_real_t NodeValue; fluid_LADSPA_FxUnit_t* FxUnit; fluid_LADSPA_Node_t* CurrentNode; assert(synth); FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); if (ac!=2){ printf("***Error029***\n" "ladspa_setnode needs two arguments - node name and value!\n"); /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ return(PrintErrorMessage); }; NodeName=av[0]; assert(NodeName); NodeValue=atof(av[1]); CurrentNode=fluid_LADSPA_RetrieveNode(FxUnit,NodeName); if (!CurrentNode){ printf("***Error030***\n" "The node %s was not found. Please use the full name of a node, that was\n" "previously declared with ladspa_declnode.\n",NodeName); /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ return(PrintErrorMessage); }; if (!(CurrentNode->flags & fluid_LADSPA_node_is_user_ctrl)){ printf("***Error031***\n" "The node %s is an ordinary control node.\n" "Only user control nodes can be modified with ladspa_setnode.\n",NodeName); /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ return(PrintErrorMessage); }; L(printf("ladspa_setnode: Assigning value %f",NodeValue)); CurrentNode->buf[0]=NodeValue; return(FLUID_OK); }; int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ fluid_LADSPA_FxUnit_t* FxUnit; assert(synth); FxUnit=synth->LADSPA_FxUnit; assert(FxUnit); fluid_LADSPA_clear(FxUnit); return(FLUID_OK); }; void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit){ /* The synthesis thread is not running anymore. * Set the bypass switch, so that fluid_LADSPA_clear can proceed.*/ FxUnit->Bypass=fluid_LADSPA_Bypassed; fluid_LADSPA_clear(FxUnit); pthread_cond_destroy(&FxUnit->cond); /* pro forma */ }; #endif /*LADSPA*/ fluidsynth-1.1.9/src/bindings/fluid_ladspa.h000066400000000000000000000203631322272076000211040ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* Author: Markus Nentwig, nentwig@users.sourceforge.net */ #ifndef _FLUID_LADSPA_H #define _FLUID_LADSPA_H /*************************************************************** * * INCLUDES */ #include "fluidsynth_priv.h" #ifdef LADSPA #include "fluid_list.h" #include #include /*************************************************************** * * DEFINES */ /* How many different plugin libraries may be used at the same time? */ #define FLUID_LADSPA_MaxLibs 100 /* How many plugin instances may be used at the same time? */ #define FLUID_LADSPA_MaxPlugins 100 /* How many nodes are allowed? */ #define FLUID_LADSPA_MaxNodes 100 /* How many tokens are allowed in one command line? (for example 152 => max. 50 port plugin allowed) */ #define FLUID_LADSPA_MaxTokens 152 /* What is the maximum path length? */ #define FLUID_LADSPA_MaxPathLength 512 /*************************************************************** * * ENUM */ typedef enum { fluid_LADSPA_NoMatch, fluid_LADSPA_PartialMatch, fluid_LADSPA_FullMatch } fluid_LADSPA_Stringmatch_t; /* Bypass state of the Fx unit */ typedef enum { fluid_LADSPA_Active, fluid_LADSPA_Bypassed, fluid_LADSPA_BypassRequest } fluid_LADSPA_BypassState; typedef enum { fluid_LADSPA_node_is_source=1, fluid_LADSPA_node_is_sink=2, fluid_LADSPA_node_is_audio=4, fluid_LADSPA_node_is_control=8, fluid_LADSPA_node_is_dummy=16, fluid_LADSPA_node_is_user_ctrl=32 } fluid_LADSPA_nodeflags; /* fluid_LADSPA_Node_t * An internal node of the Fx unit. * A 'node' is the 'glue' that connects several LADSPA plugins. * Basically it's a real-valued variable (control node) or a real-valued buffer (audio node). */ typedef struct { LADSPA_Data * buf; /*Either the buffer (Audio node) or a single control value (Control node)*/ char * Name; /* Unique identifier*/ int InCount; /* How many sources feed into this node? (0 or 1) */ int OutCount; /* How many other elements take data out of this node? */ int flags; } fluid_LADSPA_Node_t; /* * fluid_LADSPA_Fx_t * Fx unit using LADSPA. * This includes a number of LADSPA plugins, their libraries, nodes etc. * The Fx unit connects its input to Fluidsynth and its output to the soundcard. */ typedef struct { /* LADSPA-plugins are in shared libraries (for example aw.so). * Pointers to them are stored here. A library is uniquely identified through * its filename (full path).*/ fluid_synth_t* synth; int NumberLibs; void * ppvPluginLibs[FLUID_LADSPA_MaxLibs]; char * ppvPluginLibNames[FLUID_LADSPA_MaxLibs]; /*List of plugins (descriptor and instance) * A LADSPA plugin descriptor points to the code, which is executed, when a plugin is run. * The plugin instance is given as a parameter, when calling. */ int NumberPlugins; const LADSPA_Descriptor * PluginDescriptorTable[FLUID_LADSPA_MaxPlugins]; LADSPA_Handle * PluginInstanceTable[FLUID_LADSPA_MaxPlugins]; /* List of nodes */ int NumberNodes; fluid_LADSPA_Node_t * Nodelist[FLUID_LADSPA_MaxNodes]; /* List of Command lines * During the setup phase, each ladspa_add command creates one command sequence. For example: * ./aw.so alienwah_stereo Input <- Master_L_Synth Output -> Master_R_Synth Parameter <- #42.0 * Those lists are stored in LADSPA_Command_Sequence. * One command line results in one plugin => size MaxPlugins. */ int NumberCommands; char ** LADSPA_Command_Sequence[FLUID_LADSPA_MaxPlugins]; /* User control nodes * A user control node is declared at any time before the ladspa_start command. * It acts as a constant node, but it has a name and can be changed with the ladspa_nodeset command. */ int NumberUserControlNodes; char * UserControlNodeNames[FLUID_LADSPA_MaxNodes]; fluid_real_t UserControlNodeValues[FLUID_LADSPA_MaxNodes]; /* Bypass switch * If set, the LADSPA Fx unit does not touch the signal.*/ fluid_LADSPA_BypassState Bypass; /* Communication between the 'command line' process and the synthesis process. * A possible conflict situation arises, when fluid_clear is called, and starts to destroy * the plugins. But the synthesis thread still processes plugins at the same time. The consequences are ugly. * Therefore ladspa_clear waits for acknowledgement from the synthesis thread, that the Fx unit is bypassed. * 'cond' is used for the communication, the mutex is required for changing the condition. */ pthread_cond_t cond; pthread_mutex_t mutex; } fluid_LADSPA_FxUnit_t; /* * misc */ /* Purpose: * Creates a new Fx unit in bypass mode with default settings. * It is ready for further calls (add, clear, start). */ fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth); /* Purpose: * Applies the master gain (from command line option --gain or gain command). * Processes one block of sound data (generated from the synthesizer) through * the LADSPA Fx unit. * Acknowledges a bypass request. */ void fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* Fx_unit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]); /* Purpose: * Returns the node belonging to Name or NULL, if not found */ fluid_LADSPA_Node_t* fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name); /* Purpose: * Creates a new node with the given characteristics. */ fluid_LADSPA_Node_t* fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags); /* Purpose: * - Resets LADSPA Fx unit to bypass. * - Removes all plugins from the reverb unit. * - Releases all libraries. * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time * a plugin is recompiled. */ void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit); /* Purpose: * Frees all memory and shuts down the Fx block. * The synthesis thread must be stopped, when calling. */ void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit); /* * fluid_handle_LADSPA_XXX * Those functions are called from fluid_cmd, when a command is entered on the command line. */ /* Purpose: * - Resets LADSPA Fx unit to bypass. * - Removes all plugins from the reverb unit. * - Releases all libraries. * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time * a plugin is recompiled. */ int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); /* Purpose: * Loads the plugins added with 'ladspa_add' and then start the Fx unit. * Internal processes: * - load the LADSPA plugin libraries * - instantiate the plugins * - connect the plugins * - set the bypass switch to 'not bypassed' */ int fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); /* Purpose: * Adds one plugin into the list of the LADSPA Fx unit. * This is only allowed, while the Fx block is in 'bypass' state (after clear). */ int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); /* Purpose: * Declares a user control node and a value; for further processing in ladspa_start. */ int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); /* Purpose: * Assigns a value to the a user control node */ int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); #endif /* LADSPA */ #endif /* _FLUID_LADSPA_H */ fluidsynth-1.1.9/src/bindings/fluid_lash.c000066400000000000000000000167341322272076000205710ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_lash.h" #include "fluid_synth.h" #include /* for usleep() */ #include #include #include #include #include static void fluid_lash_save (fluid_synth_t * synth); static void fluid_lash_load (fluid_synth_t * synth, const char * filename); static void *fluid_lash_run (void * data); /* * lash client - this symbol needs to be in the library else * all clients would need a fluid_lash_client symbol. */ #ifdef HAVE_LASH lash_client_t * fluid_lash_client; #else cca_client_t * fluid_lash_client; #endif static pthread_t fluid_lash_thread; #ifdef HAVE_LASH fluid_lash_args_t * fluid_lash_extract_args (int * pargc, char *** pargv) { return lash_extract_args (pargc, pargv); } int fluid_lash_connect (fluid_lash_args_t * args) { fluid_lash_client = lash_init (args, PACKAGE, LASH_Config_Data_Set | LASH_Terminal, LASH_PROTOCOL (2,0)); return fluid_lash_client && lash_enabled (fluid_lash_client); } void fluid_lash_create_thread (fluid_synth_t * synth) { pthread_create (&fluid_lash_thread, NULL, fluid_lash_run, synth); } static void fluid_lash_save (fluid_synth_t * synth) { int i; int sfcount; fluid_sfont_t * sfont; lash_config_t * config; char num[32]; sfcount = fluid_synth_sfcount (synth); config = lash_config_new (); lash_config_set_key (config, "soundfont count"); lash_config_set_value_int (config, sfcount); lash_send_config (fluid_lash_client, config); for (i = sfcount - 1; i >= 0; i--) { sfont = fluid_synth_get_sfont (synth, i); config = lash_config_new (); sprintf (num, "%d", i); lash_config_set_key (config, num); lash_config_set_value_string (config, sfont->get_name (sfont)); lash_send_config (fluid_lash_client, config); } } static void fluid_lash_load (fluid_synth_t * synth, const char * filename) { fluid_synth_sfload (synth, filename, 1); } static void * fluid_lash_run (void * data) { lash_event_t * event; lash_config_t * config; fluid_synth_t * synth; int done = 0; int err; int pending_restores = 0; synth = (fluid_synth_t *) data; while (!done) { while ( (event = lash_get_event (fluid_lash_client)) ) { switch (lash_event_get_type (event)) { case LASH_Save_Data_Set: fluid_lash_save (synth); lash_send_event (fluid_lash_client, event); break; case LASH_Restore_Data_Set: lash_event_destroy (event); break; case LASH_Quit: err = kill (getpid(), SIGQUIT); if (err) fprintf (stderr, "%s: error sending signal: %s", __FUNCTION__, strerror (errno)); lash_event_destroy (event); done = 1; break; case LASH_Server_Lost: lash_event_destroy (event); done = 1; break; default: fprintf (stderr, "Received unknown LASH event of type %d\n", lash_event_get_type (event)); lash_event_destroy (event); break; } } while ( (config = lash_get_config (fluid_lash_client)) ) { if (strcmp (lash_config_get_key (config), "soundfont count") == 0) pending_restores = lash_config_get_value_int (config); else { fluid_lash_load (synth, lash_config_get_value_string (config)); pending_restores--; } lash_config_destroy (config); if (!pending_restores) { event = lash_event_new_with_type (LASH_Restore_Data_Set); lash_send_event (fluid_lash_client, event); } } usleep (10000); } return NULL; } #else /* deprecated LADCCA support, will remove someday */ fluid_lash_args_t * fluid_lash_extract_args (int * pargc, char *** pargv) { return cca_extract_args (pargc, pargv); } int fluid_lash_connect (fluid_lash_args_t * args) { fluid_lash_client = cca_init (args, PACKAGE, CCA_Config_Data_Set | CCA_Terminal, CCA_PROTOCOL (2,0)); return fluid_lash_client && cca_enabled (fluid_lash_client); } void fluid_lash_create_thread (fluid_synth_t * synth) { pthread_create (&fluid_lash_thread, NULL, fluid_lash_run, synth); } static void fluid_lash_save (fluid_synth_t * synth) { int i; int sfcount; fluid_sfont_t * sfont; cca_config_t * config; char num[32]; sfcount = fluid_synth_sfcount (synth); config = cca_config_new (); cca_config_set_key (config, "soundfont count"); cca_config_set_value_int (config, sfcount); cca_send_config (fluid_lash_client, config); for (i = sfcount - 1; i >= 0; i--) { sfont = fluid_synth_get_sfont (synth, i); config = cca_config_new (); sprintf (num, "%d", i); cca_config_set_key (config, num); cca_config_set_value_string (config, sfont->get_name (sfont)); cca_send_config (fluid_lash_client, config); } } static void fluid_lash_load (fluid_synth_t * synth, const char * filename) { fluid_synth_sfload (synth, filename, 1); } /* LADCCA thread */ static void * fluid_lash_run (void * data) { cca_event_t * event; cca_config_t * config; fluid_synth_t * synth; int done = 0; int err; int pending_restores = 0; synth = (fluid_synth_t *) data; while (!done) { while ( (event = cca_get_event (fluid_lash_client)) ) { switch (cca_event_get_type (event)) { case CCA_Save_Data_Set: fluid_lash_save (synth); cca_send_event (fluid_lash_client, event); break; case CCA_Restore_Data_Set: cca_event_destroy (event); break; case CCA_Quit: err = kill (getpid(), SIGQUIT); if (err) fprintf (stderr, "%s: error sending signal: %s", __FUNCTION__, strerror (errno)); cca_event_destroy (event); done = 1; break; case CCA_Server_Lost: cca_event_destroy (event); done = 1; break; default: fprintf (stderr, "Received unknown LADCCA event of type %d\n", cca_event_get_type (event)); cca_event_destroy (event); break; } } while ( (config = cca_get_config (fluid_lash_client)) ) { if (strcmp (cca_config_get_key (config), "soundfont count") == 0) pending_restores = cca_config_get_value_int (config); else { fluid_lash_load (synth, cca_config_get_value_string (config)); pending_restores--; } cca_config_destroy (config); if (!pending_restores) { event = cca_event_new_with_type (CCA_Restore_Data_Set); cca_send_event (fluid_lash_client, event); } } usleep (10000); } return NULL; } #endif /* #if HAVE_LASH #else */ fluidsynth-1.1.9/src/bindings/fluid_lash.h000066400000000000000000000034201322272076000205620ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #if HAVE_CONFIG_H #include "config.h" #endif #if defined(HAVE_LASH) || defined(HAVE_LADCCA) #include "fluid_synth.h" #define LASH_ENABLED 1 #ifdef HAVE_LASH #include extern lash_client_t * fluid_lash_client; #define fluid_lash_args_t lash_args_t #define fluid_lash_alsa_client_id lash_alsa_client_id #define fluid_lash_jack_client_name lash_jack_client_name #else /* old deprecated LADCCA support which will be removed someday */ #include extern cca_client_t * fluid_lash_client; #define fluid_lash_args_t cca_args_t #define fluid_lash_alsa_client_id cca_alsa_client_id #define fluid_lash_jack_client_name cca_jack_client_name #endif FLUIDSYNTH_API fluid_lash_args_t *fluid_lash_extract_args (int * pargc, char *** pargv); FLUIDSYNTH_API int fluid_lash_connect (fluid_lash_args_t * args); FLUIDSYNTH_API void fluid_lash_create_thread (fluid_synth_t * synth); #endif /* defined(HAVE_LASH) || defined(HAVE_LADCCA) */ fluidsynth-1.1.9/src/bindings/fluid_rtkit.c000066400000000000000000000251121322272076000207650ustar00rootroot00000000000000/*-*- Mode: C; c-basic-offset: 8 -*-*/ /*** Copyright 2009 Lennart Poettering Copyright 2010 David Henningsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***/ #include "fluidsynth_priv.h" #ifdef DBUS_SUPPORT #include #include "fluid_rtkit.h" #if defined(__linux__) || defined(__APPLE__) #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include static pid_t _gettid(void) { return (pid_t) syscall(SYS_gettid); } static int translate_error(const char *name) { if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0) return -ENOMEM; if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 || strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) return -ENOENT; if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 || strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0) return -EACCES; return -EIO; } static long long rtkit_get_int_property(DBusConnection *connection, const char* propname, long long* propval) { DBusMessage *m = NULL, *r = NULL; DBusMessageIter iter, subiter; dbus_int64_t i64; dbus_int32_t i32; DBusError error; int current_type; long long ret; const char * interfacestr = "org.freedesktop.RealtimeKit1"; dbus_error_init(&error); if (!(m = dbus_message_new_method_call( RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.DBus.Properties", "Get"))) { ret = -ENOMEM; goto finish; } if (!dbus_message_append_args( m, DBUS_TYPE_STRING, &interfacestr, DBUS_TYPE_STRING, &propname, DBUS_TYPE_INVALID)) { ret = -ENOMEM; goto finish; } if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { ret = translate_error(error.name); goto finish; } if (dbus_set_error_from_message(&error, r)) { ret = translate_error(error.name); goto finish; } ret = -EBADMSG; dbus_message_iter_init(r, &iter); while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) { if (current_type == DBUS_TYPE_VARIANT) { dbus_message_iter_recurse(&iter, &subiter); while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) { if (current_type == DBUS_TYPE_INT32) { dbus_message_iter_get_basic(&subiter, &i32); *propval = i32; ret = 0; } if (current_type == DBUS_TYPE_INT64) { dbus_message_iter_get_basic(&subiter, &i64); *propval = i64; ret = 0; } dbus_message_iter_next (&subiter); } } dbus_message_iter_next (&iter); } finish: if (m) dbus_message_unref(m); if (r) dbus_message_unref(r); dbus_error_free(&error); return ret; } int rtkit_get_max_realtime_priority(DBusConnection *connection) { long long retval = 0; int err; err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval); return err < 0 ? err : retval; } int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) { long long retval = 0; int err; err = rtkit_get_int_property(connection, "MinNiceLevel", &retval); if (err >= 0) *min_nice_level = retval; return err; } long long rtkit_get_rttime_nsec_max(DBusConnection *connection) { long long retval = 0; int err; err = rtkit_get_int_property(connection, "RTTimeNSecMax", &retval); return err < 0 ? err : retval; } int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) { DBusMessage *m = NULL, *r = NULL; dbus_uint64_t u64; dbus_uint32_t u32; DBusError error; int ret; dbus_error_init(&error); if (thread == 0) thread = _gettid(); if (!(m = dbus_message_new_method_call( RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.RealtimeKit1", "MakeThreadRealtime"))) { ret = -ENOMEM; goto finish; } u64 = (dbus_uint64_t) thread; u32 = (dbus_uint32_t) priority; if (!dbus_message_append_args( m, DBUS_TYPE_UINT64, &u64, DBUS_TYPE_UINT32, &u32, DBUS_TYPE_INVALID)) { ret = -ENOMEM; goto finish; } if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { ret = translate_error(error.name); goto finish; } if (dbus_set_error_from_message(&error, r)) { ret = translate_error(error.name); goto finish; } ret = 0; finish: if (m) dbus_message_unref(m); if (r) dbus_message_unref(r); dbus_error_free(&error); return ret; } int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) { DBusMessage *m = NULL, *r = NULL; dbus_uint64_t u64; dbus_int32_t s32; DBusError error; int ret; dbus_error_init(&error); if (thread == 0) thread = _gettid(); if (!(m = dbus_message_new_method_call( RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority"))) { ret = -ENOMEM; goto finish; } u64 = (dbus_uint64_t) thread; s32 = (dbus_int32_t) nice_level; if (!dbus_message_append_args( m, DBUS_TYPE_UINT64, &u64, DBUS_TYPE_INT32, &s32, DBUS_TYPE_INVALID)) { ret = -ENOMEM; goto finish; } if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) { ret = translate_error(error.name); goto finish; } if (dbus_set_error_from_message(&error, r)) { ret = translate_error(error.name); goto finish; } ret = 0; finish: if (m) dbus_message_unref(m); if (r) dbus_message_unref(r); dbus_error_free(&error); return ret; } #ifndef RLIMIT_RTTIME # define RLIMIT_RTTIME 15 #endif #define MAKE_REALTIME_RETURN(_value) \ do { \ dbus_connection_close(conn); \ dbus_connection_unref(conn); \ return _value; \ } while (0) int fluid_rtkit_make_realtime(pid_t thread, int priority) { DBusConnection *conn = NULL; DBusError error; int max_prio, res; long long max_rttime; struct rlimit old_limit, new_limit; if (!dbus_threads_init_default()) return -ENOMEM; /* Initialize system bus connection */ dbus_error_init(&error); conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); if (conn == NULL) { res = translate_error(error.name); dbus_error_free(&error); return res; } dbus_error_free(&error); /* Make sure we don't fail by wanting too much */ max_prio = rtkit_get_max_realtime_priority(conn); if (max_prio < 0) MAKE_REALTIME_RETURN(max_prio); if (priority >= max_prio) priority = max_prio; /* Enforce RLIMIT_RTTIME, also a must for obtaining rt prio through rtkit */ max_rttime = rtkit_get_rttime_nsec_max(conn); if (max_rttime < 0) MAKE_REALTIME_RETURN(max_rttime); new_limit.rlim_cur = new_limit.rlim_max = max_rttime; if (getrlimit(RLIMIT_RTTIME, &old_limit) < 0) MAKE_REALTIME_RETURN(-1); if (setrlimit(RLIMIT_RTTIME, &new_limit) < 0) MAKE_REALTIME_RETURN(-1); /* Finally, let's try */ res = rtkit_make_realtime(conn, thread, priority); if (res != 0) { setrlimit(RLIMIT_RTTIME, &old_limit); } MAKE_REALTIME_RETURN(res); } #else int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) { return -ENOTSUP; } int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) { return -ENOTSUP; } int rtkit_get_max_realtime_priority(DBusConnection *connection) { return -ENOTSUP; } int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) { return -ENOTSUP; } long long rtkit_get_rttime_nsec_max(DBusConnection *connection) { return -ENOTSUP; } int fluid_rtkit_make_realtime(pid_t thread, int priority) { return -ENOTSUP; } #endif /* defined(__linux__) || defined(__APPLE__) */ #endif /* DBUS_SUPPORT */ fluidsynth-1.1.9/src/bindings/fluid_rtkit.h000066400000000000000000000040011322272076000207640ustar00rootroot00000000000000/*-*- Mode: C; c-basic-offset: 8 -*-*/ #ifndef foortkithfoo #define foortkithfoo /*** Copyright 2009 Lennart Poettering Copyright 2010 David Henningsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ***/ #ifdef DBUS_SUPPORT #include #include #ifdef __cplusplus extern "C" { #endif /* This is the reference implementation for a client for * RealtimeKit. You don't have to use this, but if do, just copy these * sources into your repository */ #define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" #define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" /* This is mostly equivalent to sched_setparam(thread, SCHED_RR, { * .sched_priority = priority }). 'thread' needs to be a kernel thread * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the * current thread is used. The returned value is a negative errno * style error code, or 0 on success. */ int fluid_rtkit_make_realtime(pid_t thread, int priority); #ifdef __cplusplus } #endif #endif #endif fluidsynth-1.1.9/src/config.cmake000066400000000000000000000160611322272076000167560ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* Define to enable ALSA driver */ #cmakedefine ALSA_SUPPORT @ALSA_SUPPORT@ /* Define to activate sound output to files */ #cmakedefine AUFILE_SUPPORT @AUFILE_SUPPORT@ /* whether or not we are supporting CoreAudio */ #cmakedefine COREAUDIO_SUPPORT @COREAUDIO_SUPPORT@ /* whether or not we are supporting CoreMIDI */ #cmakedefine COREMIDI_SUPPORT @COREMIDI_SUPPORT@ /* whether or not we are supporting DART */ #cmakedefine DART_SUPPORT @DART_SUPPORT@ /* Define if building for Mac OS X Darwin */ #cmakedefine DARWIN @DARWIN@ /* Define if D-Bus support is enabled */ #cmakedefine DBUS_SUPPORT @DBUS_SUPPORT@ /* Soundfont to load automatically in some use cases */ #cmakedefine DEFAULT_SOUNDFONT "@DEFAULT_SOUNDFONT@" /* Define to enable FPE checks */ #cmakedefine FPE_CHECK @FPE_CHECK@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_INET_H @HAVE_ARPA_INET_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H @HAVE_ERRNO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H @HAVE_FCNTL_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@ /* whether or not we are supporting ladcca */ #cmakedefine HAVE_LADCCA @HAVE_LADCCA@ /* whether or not we are supporting lash */ #cmakedefine HAVE_LASH @HAVE_LASH@ /* Define to 1 if you have the `dl' library (-ldl). */ #cmakedefine HAVE_LIBDL @HAVE_LIBDL@ /* Define to 1 if you have the `MidiShare' library (-lMidiShare). */ #cmakedefine HAVE_LIBMIDISHARE @HAVE_LIBMIDISHARE@ /* Define to 1 if you have the `pthread' library (-lpthread). */ #cmakedefine HAVE_LIBPTHREAD @HAVE_LIBPTHREAD@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MACHINE_SOUNDCARD_H @HAVE_MACHINE_SOUNDCARD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MATH_H @HAVE_MATH_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H @HAVE_MEMORY_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MIDISHARE_H @HAVE_MIDISHARE_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IN_H @HAVE_NETINET_IN_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_TCP_H @HAVE_NETINET_TCP_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTHREAD_H @HAVE_PTHREAD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SIGNAL_H @HAVE_SIGNAL_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H @HAVE_STDARG_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDIO_H @HAVE_STDIO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H @HAVE_STRINGS_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H @HAVE_STRING_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_IOCTL_H @HAVE_SYS_IOCTL_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_MMAN_H @HAVE_SYS_MMAN_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H @HAVE_SYS_SOCKET_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOUNDCARD_H @HAVE_SYS_SOUNDCARD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H @HAVE_WINDOWS_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_GETOPT_H @HAVE_GETOPT_H@ /* Define to 1 if you have the inet_ntop() function. */ #cmakedefine HAVE_INETNTOP @HAVE_INETNTOP@ /* Define to enable JACK driver */ #cmakedefine JACK_SUPPORT @JACK_SUPPORT@ /* Include the LADSPA Fx unit */ #cmakedefine LADSPA @LADSPA_SUPPORT@ /* Define to enable IPV6 support */ #cmakedefine IPV6_SUPPORT @IPV6_SUPPORT@ /* libsndfile has ogg vorbis support */ #cmakedefine LIBSNDFILE_HASVORBIS @LIBSNDFILE_HASVORBIS@ /* Define to enable libsndfile support */ #cmakedefine LIBSNDFILE_SUPPORT @LIBSNDFILE_SUPPORT@ /* Define to enable MidiShare driver */ #cmakedefine MIDISHARE_SUPPORT @MIDISHARE_SUPPORT@ /* Define if using the MinGW32 environment */ #cmakedefine MINGW32 @MINGW32@ /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #cmakedefine NO_MINUS_C_MINUS_O @NO_MINUS_C_MINUS_O@ /* Define to enable OSS driver */ #cmakedefine OSS_SUPPORT @OSS_SUPPORT@ /* Name of package */ #cmakedefine PACKAGE "@PACKAGE@" /* Define to the address where bug reports for this package should be sent. */ #cmakedefine PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@ /* Define to the full name of this package. */ #cmakedefine PACKAGE_NAME @PACKAGE_NAME@ /* Define to the full name and version of this package. */ #cmakedefine PACKAGE_STRING @PACKAGE_STRING@ /* Define to the one symbol short name of this package. */ #cmakedefine PACKAGE_TARNAME @PACKAGE_TARNAME@ /* Define to the version of this package. */ #cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* Define to enable PortAudio driver */ #cmakedefine PORTAUDIO_SUPPORT @PORTAUDIO_SUPPORT@ /* Define to enable PulseAudio driver */ #cmakedefine PULSE_SUPPORT @PULSE_SUPPORT@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ /* Define to enable SIGFPE assertions */ #cmakedefine TRAP_ON_FPE @TRAP_ON_FPE@ /* Version number of package */ #cmakedefine VERSION @FLUIDSYNTH_VERSION@ /* Define to do all DSP in single floating point precision */ #cmakedefine WITH_FLOAT @WITH_FLOAT@ /* Define to profile the DSP code */ #cmakedefine WITH_PROFILING @WITH_PROFILING@ /* Define to use the readline library for line editing */ #cmakedefine WITH_READLINE @WITH_READLINE@ /* Define if the compiler supports VLA */ #cmakedefine SUPPORTS_VLA @SUPPORTS_VLA@ /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #cmakedefine WORDS_BIGENDIAN @WORDS_BIGENDIAN@ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #cmakedefine inline @INLINE_KEYWORD@ #endif #endif /* CONFIG_H */ fluidsynth-1.1.9/src/config_macos.h000077500000000000000000000011441322272076000173060ustar00rootroot00000000000000#define VERSION FLUIDSYNTH_VERSION #define MACOS9 #define MACINTOSH #define HAVE_STRING_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STDIO_H 1 #define HAVE_MATH_H 1 #define HAVE_STDARG_H 1 #define WORDS_BIGENDIAN 1 #define HAVE_LIMITS_H 1 #define HAVE_FCNTL_H 1 #undef WITH_PROFILING #define WITHOUT_SERVER 1 /**** define to use the macintosh sound manager driver*/ #define SNDMAN_SUPPORT 1 /**** define to use the portaudio driver */ /* #define PORTAUDIO_SUPPORT 1 */ /**** define to use the MidiShare driver */ /* #define MIDISHARE_SUPPORT 1 */ /* #define MIDISHARE_DRIVER 1 */ /* #define MidiSharePPC_68k */ fluidsynth-1.1.9/src/config_macosx.h000077500000000000000000000006551322272076000175040ustar00rootroot00000000000000#define MACINTOSH #define HAVE_STRING_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STDIO_H 1 #define HAVE_MATH_H 1 #define HAVE_STDARG_H 1 #define WORDS_BIGENDIAN 1 #undef WITH_PROFILING /* define to support the MidiShare driver */ #define MIDISHARE_SUPPORT 1 #define MIDISHARE_DRIVER 1 #define PORTAUDIO_SUPPORT 1 #define PORTMIDI_SUPPORT 1 #define __Types__ /* define to support DARWIN */ #define DARWIN typedef int socklen_t fluidsynth-1.1.9/src/config_macosx_pb.h000066400000000000000000000012211322272076000201500ustar00rootroot00000000000000 #define VERSION "1.0.x" #define MACINTOSH /* define to support DARWIN */ #define DARWIN #define HAVE_STRING_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STDIO_H 1 #define HAVE_MATH_H 1 #define HAVE_STDARG_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_FCNTL_H 1 #define HAVE_UNISTD_H 1 #define HAVE_LIMITS_H 1 #define HAVE_PTHREAD_H 1 #define WORDS_BIGENDIAN 1 #define DEBUG 1 #undef WITH_PROFILING #define WITHOUT_SERVER 1 #define COREAUDIO_SUPPORT 1 #define COREMIDI_SUPPORT 1 /* define to support the MidiShare driver */ /* #define MIDISHARE_SUPPORT 1 #define MIDISHARE_DRIVER 1 #define PORTAUDIO_SUPPORT 1 #define __Types__ */ typedef int socklen_t fluidsynth-1.1.9/src/config_win32.cmake000066400000000000000000000007241322272076000177770ustar00rootroot00000000000000#pragma once #cmakedefine HAVE_IO_H @HAVE_IO_H@ #define DSOUND_SUPPORT 1 #define WINMIDI_SUPPORT 1 #if _MSC_VER < 1900 #define snprintf g_snprintf #endif #define strcasecmp _stricmp #if _MSC_VER < 1500 #define vsnprintf g_vsnprintf #endif #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #pragma warning(disable : 4244) #pragma warning(disable : 4101) #pragma warning(disable : 4305) #pragma warning(disable : 4996) typedef int socklen_t; fluidsynth-1.1.9/src/config_win32.h.in000066400000000000000000000013611322272076000175510ustar00rootroot00000000000000#define VERSION "@VERSION@" #define HAVE_STRING_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STDIO_H 1 #define HAVE_MATH_H 1 #define HAVE_STDARG_H 1 #define HAVE_FCNTL_H 1 #define HAVE_LIMITS_H 1 #define HAVE_IO_H 1 #define HAVE_WINDOWS_H 1 #define DSOUND_SUPPORT 1 #define WINMIDI_SUPPORT 1 #define WITH_FLOAT 1 #if _MSC_VER < 1900 #define snprintf g_snprintf #endif #define strcasecmp _stricmp #if _MSC_VER < 1500 #define vsnprintf g_vsnprintf #endif #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #define WITH_PROFILING 0 #pragma warning(disable : 4244) #pragma warning(disable : 4101) #pragma warning(disable : 4305) #pragma warning(disable : 4996) #ifndef inline #define inline __inline #endif typedef int socklen_t; fluidsynth-1.1.9/src/drivers/000077500000000000000000000000001322272076000161615ustar00rootroot00000000000000fluidsynth-1.1.9/src/drivers/fluid_adriver.c000066400000000000000000000404211322272076000211450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_adriver.h" #include "fluid_settings.h" /* * fluid_adriver_definition_t */ typedef struct _fluid_audriver_definition_t { char* name; fluid_audio_driver_t* (*new)(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* (*new2)(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int (*free)(fluid_audio_driver_t* driver); void (*settings)(fluid_settings_t* settings); } fluid_audriver_definition_t; #if PULSE_SUPPORT fluid_audio_driver_t* new_fluid_pulse_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_pulse_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_pulse_audio_driver(fluid_audio_driver_t* p); void fluid_pulse_audio_driver_settings(fluid_settings_t* settings); #endif #if ALSA_SUPPORT fluid_audio_driver_t* new_fluid_alsa_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p); void fluid_alsa_audio_driver_settings(fluid_settings_t* settings); #endif #if OSS_SUPPORT fluid_audio_driver_t* new_fluid_oss_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_oss_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_oss_audio_driver(fluid_audio_driver_t* p); void fluid_oss_audio_driver_settings(fluid_settings_t* settings); #endif #if COREAUDIO_SUPPORT fluid_audio_driver_t* new_fluid_core_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_core_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_core_audio_driver(fluid_audio_driver_t* p); void fluid_core_audio_driver_settings(fluid_settings_t* settings); #endif #if DSOUND_SUPPORT fluid_audio_driver_t* new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* p); void fluid_dsound_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, fluid_synth_t* synth); int delete_fluid_portaudio_driver(fluid_audio_driver_t* p); #endif #if JACK_SUPPORT fluid_audio_driver_t* new_fluid_jack_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_jack_audio_driver(fluid_audio_driver_t* p); void fluid_jack_audio_driver_settings(fluid_settings_t* settings); #endif #if SNDMAN_SUPPORT fluid_audio_driver_t* new_fluid_sndmgr_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p); #endif #if DART_SUPPORT fluid_audio_driver_t* new_fluid_dart_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_dart_audio_driver(fluid_audio_driver_t* p); void fluid_dart_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); int delete_fluid_file_audio_driver(fluid_audio_driver_t* p); #endif /* Available audio drivers, listed in order of preference */ static const fluid_audriver_definition_t fluid_audio_drivers[] = { #if JACK_SUPPORT { "jack", new_fluid_jack_audio_driver, new_fluid_jack_audio_driver2, delete_fluid_jack_audio_driver, fluid_jack_audio_driver_settings }, #endif #if ALSA_SUPPORT { "alsa", new_fluid_alsa_audio_driver, new_fluid_alsa_audio_driver2, delete_fluid_alsa_audio_driver, fluid_alsa_audio_driver_settings }, #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", new_fluid_pulse_audio_driver, new_fluid_pulse_audio_driver2, delete_fluid_pulse_audio_driver, fluid_pulse_audio_driver_settings }, #endif #if COREAUDIO_SUPPORT { "coreaudio", new_fluid_core_audio_driver, new_fluid_core_audio_driver2, delete_fluid_core_audio_driver, fluid_core_audio_driver_settings }, #endif #if DSOUND_SUPPORT { "dsound", new_fluid_dsound_audio_driver, NULL, delete_fluid_dsound_audio_driver, fluid_dsound_audio_driver_settings }, #endif #if PORTAUDIO_SUPPORT { "portaudio", new_fluid_portaudio_driver, NULL, delete_fluid_portaudio_driver, fluid_portaudio_driver_settings }, #endif #if SNDMAN_SUPPORT { "sndman", new_fluid_sndmgr_audio_driver, new_fluid_sndmgr_audio_driver2, delete_fluid_sndmgr_audio_driver, NULL }, #endif #if DART_SUPPORT { "dart", new_fluid_dart_audio_driver, NULL, delete_fluid_dart_audio_driver, fluid_dart_audio_driver_settings }, #endif #if AUFILE_SUPPORT { "file", new_fluid_file_audio_driver, NULL, delete_fluid_file_audio_driver, NULL }, #endif }; #define ENABLE_AUDIO_DRIVER(_drv, _idx) \ _drv[(_idx) / (sizeof(*(_drv))*8)] &= ~(1 << ((_idx) % (sizeof((*_drv))*8))) #define IS_AUDIO_DRIVER_ENABLED(_drv, _idx) \ (!(_drv[(_idx) / (sizeof(*(_drv))*8)] & (1 << ((_idx) % (sizeof((*_drv))*8))))) static uint8 fluid_adriver_disable_mask[(FLUID_N_ELEMENTS(fluid_audio_drivers)+7)/8] = {0}; void fluid_audio_driver_settings(fluid_settings_t* settings) { unsigned int i; fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0, NULL, NULL); fluid_settings_add_option(settings, "audio.sample-format", "16bits"); fluid_settings_add_option(settings, "audio.sample-format", "float"); fluid_settings_register_int(settings, "audio.output-channels", 2, 2, 32, 0, NULL, NULL); fluid_settings_register_int(settings, "audio.input-channels", 0, 0, 2, 0, NULL, NULL); #if defined(WIN32) fluid_settings_register_int(settings, "audio.period-size", 512, 64, 8192, 0, NULL, NULL); fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0, NULL, NULL); #elif defined(MACOS9) fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0, NULL, NULL); fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0, NULL, NULL); #else fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0, NULL, NULL); fluid_settings_register_int(settings, "audio.periods", 16, 2, 64, 0, NULL, NULL); #endif fluid_settings_register_int (settings, "audio.realtime-prio", FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0, NULL, NULL); /* Set the default driver */ #if JACK_SUPPORT fluid_settings_register_str(settings, "audio.driver", "jack", 0, NULL, NULL); #elif ALSA_SUPPORT fluid_settings_register_str(settings, "audio.driver", "alsa", 0, NULL, NULL); #elif PULSE_SUPPORT fluid_settings_register_str(settings, "audio.driver", "pulseaudio", 0, NULL, NULL); #elif OSS_SUPPORT fluid_settings_register_str(settings, "audio.driver", "oss", 0, NULL, NULL); #elif COREAUDIO_SUPPORT fluid_settings_register_str(settings, "audio.driver", "coreaudio", 0, NULL, NULL); #elif DSOUND_SUPPORT fluid_settings_register_str(settings, "audio.driver", "dsound", 0, NULL, NULL); #elif SNDMAN_SUPPORT fluid_settings_register_str(settings, "audio.driver", "sndman", 0, NULL, NULL); #elif PORTAUDIO_SUPPORT fluid_settings_register_str(settings, "audio.driver", "portaudio", 0, NULL, NULL); #elif DART_SUPPORT fluid_settings_register_str(settings, "audio.driver", "dart", 0, NULL, NULL); #elif AUFILE_SUPPORT fluid_settings_register_str(settings, "audio.driver", "file", 0, NULL, NULL); #else fluid_settings_register_str(settings, "audio.driver", "", 0, NULL, NULL); #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++) { if (fluid_audio_drivers[i].settings != NULL && IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) { fluid_audio_drivers[i].settings(settings); } } } static const fluid_audriver_definition_t* find_fluid_audio_driver(fluid_settings_t* settings) { unsigned int i; char* name; char *allnames; for (i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++) { /* If this driver is de-activated, just ignore it */ if (!IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) continue; if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name)) { FLUID_LOG(FLUID_DBG, "Using '%s' audio driver", fluid_audio_drivers[i].name); return &fluid_audio_drivers[i]; } } 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_FREE (name); if (allnames) FLUID_FREE (allnames); return NULL; } /** * Create a new audio driver. * @param settings Configuration settings used to select and create the audio * driver. * @param synth Synthesizer instance for which the audio driver is created for. * @return The new audio driver instance. * * Creates a new audio driver for a given 'synth' instance with a defined set * of configuration 'settings'. */ fluid_audio_driver_t* new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { const fluid_audriver_definition_t *def = find_fluid_audio_driver(settings); if (def) { fluid_audio_driver_t *driver = (*def->new)(settings, synth); if (driver) driver->name = def->name; return driver; } return NULL; } /** * Create a new audio driver. * @param settings Configuration settings used to select and create the audio * driver. * @param func Function called to fill audio buffers for audio playback * @param data User defined data pointer to pass to 'func' * @return The new audio driver instance. * * Like new_fluid_audio_driver() but allows for custom audio processing before * audio is sent to audio driver. It is the responsibility of the callback * 'func' to render the audio into the buffers. * * NOTE: Not as efficient as new_fluid_audio_driver(). */ fluid_audio_driver_t* new_fluid_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { const fluid_audriver_definition_t *def = find_fluid_audio_driver(settings); if (def) { fluid_audio_driver_t *driver = NULL; if (def->new2 == NULL) FLUID_LOG(FLUID_DBG, "Callback mode unsupported on '%s' audio driver", def->name); else { driver = (*def->new2)(settings, func, data); if (driver) driver->name = def->name; } return driver; } return NULL; } /** * Deletes an audio driver instance. * @param driver Audio driver instance to delete * * Shuts down an audio driver and deletes its instance. */ void delete_fluid_audio_driver(fluid_audio_driver_t* driver) { unsigned int i; /* 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; } } } /** * @brief Registers audio drivers to use * * When creating a settings instance with new_fluid_settings(), all audio drivers are initialized once. * In the past this has caused segfaults and application crashes due to buggy soundcard drivers. * * This function enables the user to only initialize specific audio drivers when settings instances are created. * Therefore pass a NULL-terminated array of C-strings containing the \c names of audio drivers to register * for the usage with fluidsynth. * The \c names are the same as being used for the \c audio.driver setting. * * By default all audio drivers fluidsynth has been compiled with are registered, so calling this function is optional. * * @warning This function may only be called if no thread is residing in fluidsynth's API and no instances of any kind * are alive (e.g. as it would be the case right after fluidsynth's inital creation). Else the behaviour is undefined. * Furtermore any attempt of using audio drivers that have not been registered is undefined behaviour! * * @param adrivers NULL-terminated array of audio drivers to register. Pass NULL to register all available drivers. * @return #FLUID_OK if all the audio drivers requested by the user are supported by fluidsynth and have been * successfully registered. Otherwise #FLUID_FAILED is returned and this function has no effect. * * @note This function is not thread safe and will never be! * @since 1.1.9 */ int fluid_audio_driver_register(const char** adrivers) { unsigned int i; uint8 disable_mask[FLUID_N_ELEMENTS(fluid_adriver_disable_mask)]; if (adrivers == NULL) { /* Pass NULL to register all available drivers. */ FLUID_MEMSET(fluid_adriver_disable_mask, 0, sizeof(fluid_adriver_disable_mask)); return FLUID_OK; } FLUID_MEMSET(disable_mask, 0xFF, sizeof(disable_mask)); for(i=0; adrivers[i] != NULL; i++) { 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++) { if (FLUID_STRCMP(adrivers[i], fluid_audio_drivers[j].name) == 0) { ENABLE_AUDIO_DRIVER(disable_mask, j); break; } } if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers)) { /* 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)); return FLUID_OK; } fluidsynth-1.1.9/src/drivers/fluid_adriver.h000066400000000000000000000021111322272076000211440ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_AUDRIVER_H #define _FLUID_AUDRIVER_H #include "fluid_sys.h" void fluid_audio_driver_settings(fluid_settings_t* settings); /* * fluid_audio_driver_t */ struct _fluid_audio_driver_t { char* name; }; #endif /* _FLUID_AUDRIVER_H */ fluidsynth-1.1.9/src/drivers/fluid_alsa.c000066400000000000000000000707721322272076000204450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_alsa.c * * Driver for the Advanced Linux Sound Architecture * */ #include "fluid_synth.h" #include "fluid_midi.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #if ALSA_SUPPORT #define ALSA_PCM_NEW_HW_PARAMS_API #include #include #include #include #include #include #include "config.h" #include "fluid_lash.h" #define FLUID_ALSA_DEFAULT_MIDI_DEVICE "default" #define FLUID_ALSA_DEFAULT_SEQ_DEVICE "default" #define BUFFER_LENGTH 512 /** fluid_alsa_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; snd_pcm_t* pcm; fluid_audio_func_t callback; void* data; int buffer_size; fluid_thread_t *thread; int cont; } fluid_alsa_audio_driver_t; fluid_audio_driver_t* new_fluid_alsa_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p); void fluid_alsa_audio_driver_settings(fluid_settings_t* settings); static void fluid_alsa_audio_run_float(void* d); static void fluid_alsa_audio_run_s16(void* d); struct fluid_alsa_formats_t { char* name; snd_pcm_format_t format; snd_pcm_access_t access; fluid_thread_func_t run; }; struct fluid_alsa_formats_t fluid_alsa_formats[] = { { "s16, rw, interleaved", SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, fluid_alsa_audio_run_s16 }, { "float, rw, non interleaved", SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_NONINTERLEAVED, fluid_alsa_audio_run_float }, { NULL, 0, 0, NULL } }; /* * fluid_alsa_rawmidi_driver_t * */ typedef struct { fluid_midi_driver_t driver; snd_rawmidi_t *rawmidi_in; struct pollfd *pfd; int npfd; fluid_thread_t *thread; gint should_quit; unsigned char buffer[BUFFER_LENGTH]; fluid_midi_parser_t* parser; } fluid_alsa_rawmidi_driver_t; fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p); static void fluid_alsa_midi_run(void* d); /* * fluid_alsa_seq_driver_t * */ typedef struct { fluid_midi_driver_t driver; snd_seq_t *seq_handle; struct pollfd *pfd; int npfd; fluid_thread_t *thread; gint should_quit; int port_count; } fluid_alsa_seq_driver_t; fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data); int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p); static void fluid_alsa_seq_run(void* d); /************************************************************** * * Alsa audio driver * */ void fluid_alsa_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.alsa.device", "default", 0, NULL, NULL); } fluid_audio_driver_t* new_fluid_alsa_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { return new_fluid_alsa_audio_driver2(settings, NULL, synth); } fluid_audio_driver_t* new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { fluid_alsa_audio_driver_t* dev; double sample_rate; int periods, period_size; char* device = NULL; int realtime_prio = 0; int i, err, dir = 0; snd_pcm_hw_params_t* hwparams; snd_pcm_sw_params_t* swparams = NULL; snd_pcm_uframes_t uframes; unsigned int tmp; dev = FLUID_NEW(fluid_alsa_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_audio_driver_t)); fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_dupstr(settings, "audio.alsa.device", &device); /* ++ dup device name */ fluid_settings_getint (settings, "audio.realtime-prio", &realtime_prio); dev->data = data; dev->callback = func; dev->cont = 1; dev->buffer_size = period_size; /* Open the PCM device */ if ((err = snd_pcm_open(&dev->pcm, device ? device : "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) != 0) { if (err == -EBUSY) { FLUID_LOG(FLUID_ERR, "The \"%s\" audio device is used by another application", device ? device : "default"); goto error_recovery; } else { FLUID_LOG(FLUID_ERR, "Failed to open the \"%s\" audio device", device ? device : "default"); goto error_recovery; } } snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); /* Set hardware parameters. We continue trying access methods and sample formats until we have one that works. For example, if memory mapped access fails we try regular IO methods. (not finished, yet). */ for (i = 0; fluid_alsa_formats[i].name != NULL; i++) { snd_pcm_hw_params_any(dev->pcm, hwparams); if (snd_pcm_hw_params_set_access(dev->pcm, hwparams, fluid_alsa_formats[i].access) < 0) { continue; } if (snd_pcm_hw_params_set_format(dev->pcm, hwparams, fluid_alsa_formats[i].format) < 0) { continue; } if ((err = snd_pcm_hw_params_set_channels(dev->pcm, hwparams, 2)) < 0) { FLUID_LOG(FLUID_ERR, "Failed to set the channels: %s", snd_strerror (err)); goto error_recovery; } tmp = (unsigned int) sample_rate; if ((err = snd_pcm_hw_params_set_rate_near(dev->pcm, hwparams, &tmp, NULL)) < 0) { FLUID_LOG(FLUID_ERR, "Failed to set the sample rate: %s", snd_strerror (err)); goto error_recovery; } if (tmp != sample_rate) { /* There's currently no way to change the sampling rate of the synthesizer after it's been created. */ FLUID_LOG(FLUID_WARN, "Requested sample rate of %d, got %d instead, " "synthesizer likely out of tune!", (unsigned int) sample_rate, tmp); } uframes = period_size; if (snd_pcm_hw_params_set_period_size_near(dev->pcm, hwparams, &uframes, &dir) < 0) { FLUID_LOG(FLUID_ERR, "Failed to set the period size"); goto error_recovery; } if (uframes != (unsigned long) period_size) { FLUID_LOG(FLUID_WARN, "Requested a period size of %d, got %d instead", period_size, (int) uframes); dev->buffer_size = (int) uframes; period_size = uframes; /* period size is used below, so set it to the real value */ } tmp = periods; if (snd_pcm_hw_params_set_periods_near(dev->pcm, hwparams, &tmp, &dir) < 0) { FLUID_LOG(FLUID_ERR, "Failed to set the number of periods"); goto error_recovery; } if (tmp != (unsigned int) periods) { FLUID_LOG(FLUID_WARN, "Requested %d periods, got %d instead", periods, (int) tmp); } if (snd_pcm_hw_params(dev->pcm, hwparams) < 0) { FLUID_LOG(FLUID_WARN, "Audio device hardware configuration failed"); continue; } break; } if (fluid_alsa_formats[i].name == NULL) { FLUID_LOG(FLUID_ERR, "Failed to find a workable audio format"); goto error_recovery; } /* Set the software params */ snd_pcm_sw_params_current(dev->pcm, swparams); if (snd_pcm_sw_params_set_start_threshold(dev->pcm, swparams, period_size) != 0) { FLUID_LOG(FLUID_ERR, "Failed to set start threshold."); } if (snd_pcm_sw_params_set_avail_min(dev->pcm, swparams, period_size) != 0) { FLUID_LOG(FLUID_ERR, "Software setup for minimum available frames failed."); } if (snd_pcm_sw_params(dev->pcm, swparams) != 0) { FLUID_LOG(FLUID_ERR, "Software setup failed."); } if (snd_pcm_nonblock(dev->pcm, 0) != 0) { FLUID_LOG(FLUID_ERR, "Failed to set the audio device to blocking mode"); goto error_recovery; } /* Create the audio thread */ dev->thread = new_fluid_thread ("alsa-audio", fluid_alsa_formats[i].run, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (device) FLUID_FREE (device); /* -- free device name */ return (fluid_audio_driver_t*) dev; error_recovery: if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_alsa_audio_driver((fluid_audio_driver_t*) dev); return NULL; } int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p) { fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } dev->cont = 0; if (dev->thread) fluid_thread_join (dev->thread); if (dev->pcm) snd_pcm_close (dev->pcm); FLUID_FREE(dev); return FLUID_OK; } /* handle error after an ALSA write call */ static int fluid_alsa_handle_write_error (snd_pcm_t *pcm, int errval) { switch (errval) { case -EAGAIN: snd_pcm_wait(pcm, 1); break; // on some BSD variants ESTRPIPE is defined as EPIPE. // not sure why, maybe because this version of alsa doesnt support // suspending pcm streams. anyway, since EPIPE seems to be more // likely than ESTRPIPE, so ifdef it out in case. #if ESTRPIPE == EPIPE #warning "ESTRPIPE defined as EPIPE. This may cause trouble with ALSA playback." #else case -ESTRPIPE: if (snd_pcm_resume(pcm) != 0) { FLUID_LOG(FLUID_ERR, "Failed to resume the audio device"); return FLUID_FAILED; } #endif /* fall through ... */ /* ... since the stream got resumed, but still has to be prepared */ case -EPIPE: case -EBADFD: if (snd_pcm_prepare(pcm) != 0) { FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device"); return FLUID_FAILED; } break; default: FLUID_LOG(FLUID_ERR, "The audio device error: %s", snd_strerror(errval)); return FLUID_FAILED; } return FLUID_OK; } static void fluid_alsa_audio_run_float (void *d) { fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d; fluid_synth_t *synth = (fluid_synth_t *)(dev->data); float* left; float* right; float* handle[2]; int n, buffer_size, offset; buffer_size = dev->buffer_size; left = FLUID_ARRAY(float, buffer_size); right = FLUID_ARRAY(float, buffer_size); if ((left == NULL) || (right == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } if (snd_pcm_prepare(dev->pcm) != 0) { FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device"); goto error_recovery; } /* use separate loops depending on if callback supplied or not (overkill?) */ if (dev->callback) { while (dev->cont) { handle[0] = left; handle[1] = right; (*dev->callback)(synth, buffer_size, 0, NULL, 2, handle); offset = 0; while (offset < buffer_size) { handle[0] = left + offset; handle[1] = right + offset; n = snd_pcm_writen(dev->pcm, (void *)handle, buffer_size - offset); if (n < 0) /* error occurred? */ { if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK) goto error_recovery; } else offset += n; /* no error occurred */ } /* while (offset < buffer_size) */ } /* while (dev->cont) */ } else /* no user audio callback (faster) */ { while (dev->cont) { fluid_synth_write_float(dev->data, buffer_size, left, 0, 1, right, 0, 1); offset = 0; while (offset < buffer_size) { handle[0] = left + offset; handle[1] = right + offset; n = snd_pcm_writen(dev->pcm, (void *)handle, buffer_size - offset); if (n < 0) /* error occurred? */ { if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK) goto error_recovery; } else offset += n; /* no error occurred */ } /* while (offset < buffer_size) */ } /* while (dev->cont) */ } error_recovery: FLUID_FREE(left); FLUID_FREE(right); } static void fluid_alsa_audio_run_s16 (void *d) { fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d; float* left; float* right; short* buf; float* handle[2]; int n, buffer_size, offset; buffer_size = dev->buffer_size; left = FLUID_ARRAY(float, buffer_size); right = FLUID_ARRAY(float, buffer_size); buf = FLUID_ARRAY(short, 2 * buffer_size); if ((left == NULL) || (right == NULL) || (buf == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_recovery; } handle[0] = left; handle[1] = right; if (snd_pcm_prepare(dev->pcm) != 0) { FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device"); goto error_recovery; } /* use separate loops depending on if callback supplied or not */ if (dev->callback) { int dither_index = 0; while (dev->cont) { (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, handle); /* convert floating point data to 16 bit (with dithering) */ fluid_synth_dither_s16 (&dither_index, buffer_size, left, right, buf, 0, 2, buf, 1, 2); offset = 0; while (offset < buffer_size) { n = snd_pcm_writei (dev->pcm, (void*) (buf + 2 * offset), buffer_size - offset); if (n < 0) /* error occurred? */ { if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK) goto error_recovery; } else offset += n; /* no error occurred */ } /* while (offset < buffer_size) */ } /* while (dev->cont) */ } else /* no user audio callback, dev->data is the synth instance */ { fluid_synth_t* synth = (fluid_synth_t *)(dev->data); while (dev->cont) { fluid_synth_write_s16 (synth, buffer_size, buf, 0, 2, buf, 1, 2); offset = 0; while (offset < buffer_size) { n = snd_pcm_writei (dev->pcm, (void*) (buf + 2 * offset), buffer_size - offset); if (n < 0) /* error occurred? */ { if (fluid_alsa_handle_write_error (dev->pcm, n) != FLUID_OK) goto error_recovery; } else offset += n; /* no error occurred */ } /* while (offset < buffer_size) */ } /* while (dev->cont) */ } error_recovery: FLUID_FREE(left); FLUID_FREE(right); FLUID_FREE(buf); } /************************************************************** * * Alsa MIDI driver * */ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "midi.alsa.device", "default", 0, NULL, NULL); } /* * new_fluid_alsa_rawmidi_driver */ fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { int i, err; fluid_alsa_rawmidi_driver_t* dev; int realtime_prio = 0; int count; struct pollfd *pfd = NULL; char* device = NULL; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } /* allocate the device */ dev = FLUID_NEW(fluid_alsa_rawmidi_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_rawmidi_driver_t)); dev->driver.handler = handler; dev->driver.data = data; /* allocate one event to store the input data */ dev->parser = new_fluid_midi_parser(); if (dev->parser == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio); /* get the device name. if none is specified, use the default device. */ fluid_settings_dupstr(settings, "midi.alsa.device", &device); /* ++ alloc device name */ /* open the hardware device. only use midi in. */ if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device ? device : "default", SND_RAWMIDI_NONBLOCK)) < 0) { FLUID_LOG(FLUID_ERR, "Error opening ALSA raw MIDI port"); goto error_recovery; } snd_rawmidi_nonblock(dev->rawmidi_in, 1); /* get # of MIDI file descriptors */ count = snd_rawmidi_poll_descriptors_count(dev->rawmidi_in); if (count > 0) { /* make sure there are some */ pfd = FLUID_MALLOC(sizeof (struct pollfd) * count); dev->pfd = FLUID_MALLOC(sizeof (struct pollfd) * count); /* grab file descriptor POLL info structures */ count = snd_rawmidi_poll_descriptors(dev->rawmidi_in, pfd, count); } /* copy the input FDs */ for (i = 0; i < count; i++) { /* loop over file descriptors */ if (pfd[i].events & POLLIN) { /* use only the input FDs */ dev->pfd[dev->npfd].fd = pfd[i].fd; dev->pfd[dev->npfd].events = POLLIN; dev->pfd[dev->npfd].revents = 0; dev->npfd++; } } FLUID_FREE(pfd); g_atomic_int_set(&dev->should_quit, 0); /* create the MIDI thread */ dev->thread = new_fluid_thread ("alsa-midi-raw", fluid_alsa_midi_run, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (device) FLUID_FREE (device); /* -- free device name */ return (fluid_midi_driver_t*) dev; error_recovery: if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_alsa_rawmidi_driver((fluid_midi_driver_t*) dev); return NULL; } /* * delete_fluid_alsa_rawmidi_driver */ int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p) { fluid_alsa_rawmidi_driver_t* dev; dev = (fluid_alsa_rawmidi_driver_t*) p; if (dev == NULL) { return FLUID_OK; } /* cancel the thread and wait for it before cleaning up */ g_atomic_int_set(&dev->should_quit, 1); if (dev->thread) fluid_thread_join (dev->thread); if (dev->rawmidi_in) { snd_rawmidi_close(dev->rawmidi_in); } if (dev->parser != NULL) { delete_fluid_midi_parser(dev->parser); } FLUID_FREE(dev); return FLUID_OK; } /* * fluid_alsa_midi_run */ void fluid_alsa_midi_run(void* d) { fluid_midi_event_t* evt; fluid_alsa_rawmidi_driver_t* dev = (fluid_alsa_rawmidi_driver_t*) d; int n, i; /* go into a loop until someone tells us to stop */ while (!g_atomic_int_get(&dev->should_quit)) { /* is there something to read? */ n = poll(dev->pfd, dev->npfd, 100); /* use a 100 milliseconds timeout */ if (n < 0) { perror("poll"); } else if (n > 0) { /* read new data */ n = snd_rawmidi_read(dev->rawmidi_in, dev->buffer, BUFFER_LENGTH); if ((n < 0) && (n != -EAGAIN)) { FLUID_LOG(FLUID_ERR, "Failed to read the midi input"); g_atomic_int_set(&dev->should_quit, 1); } /* let the parser convert the data into events */ for (i = 0; i < n; i++) { evt = fluid_midi_parser_parse(dev->parser, dev->buffer[i]); if (evt != NULL) { (*dev->driver.handler)(dev->driver.data, evt); } } } } } /************************************************************** * * Alsa sequencer * */ void fluid_alsa_seq_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "midi.alsa_seq.device", "default", 0, NULL, NULL); fluid_settings_register_str(settings, "midi.alsa_seq.id", "pid", 0, NULL, NULL); } static char* fluid_alsa_seq_full_id(char* id, char* buf, int len) { if (id != NULL) { if (FLUID_STRCMP(id, "pid") == 0) { snprintf(buf, len, "FLUID Synth (%d)", getpid()); } else { snprintf(buf, len, "FLUID Synth (%s)", id); } } else { snprintf(buf, len, "FLUID Synth"); } return buf; } static char* fluid_alsa_seq_full_name(char* id, int port, char* buf, int len) { if (id != NULL) { if (FLUID_STRCMP(id, "pid") == 0) { snprintf(buf, len, "Synth input port (%d:%d)", getpid(), port); } else { snprintf(buf, len, "Synth input port (%s:%d)", id, port); } } else { snprintf(buf, len, "Synth input port"); } return buf; } /* * new_fluid_alsa_seq_driver */ fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { int i, err; fluid_alsa_seq_driver_t* dev; int realtime_prio = 0; int count; struct pollfd *pfd = NULL; char *device = NULL; char *id = NULL; char *portname = NULL; char full_id[64]; char full_name[64]; snd_seq_port_info_t *port_info = NULL; int midi_channels; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } /* allocate the device */ dev = FLUID_NEW(fluid_alsa_seq_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_alsa_seq_driver_t)); dev->driver.data = data; dev->driver.handler = handler; fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio); /* get the device name. if none is specified, use the default device. */ if (fluid_settings_dupstr(settings, "midi.alsa_seq.device", &device) == 0) /* ++ alloc device name */ goto error_recovery; if (fluid_settings_dupstr(settings, "midi.alsa_seq.id", &id) == 0) /* ++ alloc id string */ goto error_recovery; if (id == NULL) { id = FLUID_MALLOC (32); if (!id) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } sprintf(id, "%d", getpid()); } /* get the midi portname */ fluid_settings_dupstr(settings, "midi.portname", &portname); if (portname && FLUID_STRLEN (portname) == 0) { FLUID_FREE (portname); /* -- free port name */ portname = NULL; } /* open the sequencer INPUT only */ err = snd_seq_open(&dev->seq_handle, device ? device : "default", SND_SEQ_OPEN_INPUT, 0); if (err < 0) { FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer"); goto error_recovery; } snd_seq_nonblock (dev->seq_handle, 1); /* get # of MIDI file descriptors */ count = snd_seq_poll_descriptors_count(dev->seq_handle, POLLIN); if (count > 0) { /* make sure there are some */ pfd = FLUID_MALLOC(sizeof (struct pollfd) * count); dev->pfd = FLUID_MALLOC(sizeof (struct pollfd) * count); /* grab file descriptor POLL info structures */ count = snd_seq_poll_descriptors(dev->seq_handle, pfd, count, POLLIN); } /* copy the input FDs */ for (i = 0; i < count; i++) { /* loop over file descriptors */ if (pfd[i].events & POLLIN) { /* use only the input FDs */ dev->pfd[dev->npfd].fd = pfd[i].fd; dev->pfd[dev->npfd].events = POLLIN; dev->pfd[dev->npfd].revents = 0; dev->npfd++; } } FLUID_FREE(pfd); /* set the client name */ if (!portname) { snd_seq_set_client_name(dev->seq_handle, fluid_alsa_seq_full_id(id, full_id, 64)); } else { snd_seq_set_client_name(dev->seq_handle, portname); } /* create the ports */ snd_seq_port_info_alloca(&port_info); FLUID_MEMSET(port_info, 0, snd_seq_port_info_sizeof()); fluid_settings_getint(settings, "synth.midi-channels", &midi_channels); dev->port_count = midi_channels / 16; snd_seq_port_info_set_capability(port_info, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE); snd_seq_port_info_set_type(port_info, SND_SEQ_PORT_TYPE_MIDI_GM | SND_SEQ_PORT_TYPE_SYNTHESIZER | SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC); snd_seq_port_info_set_midi_channels(port_info, 16); snd_seq_port_info_set_port_specified(port_info, 1); for (i = 0; i < dev->port_count; i++) { if (!portname) { snd_seq_port_info_set_name(port_info, fluid_alsa_seq_full_name(id, i, full_name, 64)); } else { snd_seq_port_info_set_name(port_info, portname); } snd_seq_port_info_set_port(port_info, i); err = snd_seq_create_port(dev->seq_handle, port_info); if (err < 0) { FLUID_LOG(FLUID_ERR, "Error creating ALSA sequencer port"); goto error_recovery; } } /* tell the lash server our client id */ #ifdef LASH_ENABLED { int enable_lash = 0; fluid_settings_getint (settings, "lash.enable", &enable_lash); if (enable_lash) fluid_lash_alsa_client_id (fluid_lash_client, snd_seq_client_id (dev->seq_handle)); } #endif /* LASH_ENABLED */ g_atomic_int_set(&dev->should_quit, 0); /* create the MIDI thread */ dev->thread = new_fluid_thread ("alsa-midi-seq", fluid_alsa_seq_run, dev, realtime_prio, FALSE); if (portname) FLUID_FREE (portname); if (id) FLUID_FREE (id); if (device) FLUID_FREE (device); return (fluid_midi_driver_t*) dev; error_recovery: if (portname) FLUID_FREE (portname); if (id) FLUID_FREE (id); if (device) FLUID_FREE (device); delete_fluid_alsa_seq_driver((fluid_midi_driver_t*) dev); return NULL; } /* * delete_fluid_alsa_seq_driver */ int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p) { fluid_alsa_seq_driver_t* dev; dev = (fluid_alsa_seq_driver_t*) p; if (dev == NULL) { return FLUID_OK; } /* cancel the thread and wait for it before cleaning up */ g_atomic_int_set(&dev->should_quit, 1); if (dev->thread) fluid_thread_join (dev->thread); if (dev->seq_handle) { snd_seq_close(dev->seq_handle); } if (dev->pfd) FLUID_FREE (dev->pfd); FLUID_FREE(dev); return FLUID_OK; } /* * fluid_alsa_seq_run */ void fluid_alsa_seq_run(void* d) { int n, ev; snd_seq_event_t *seq_ev; fluid_midi_event_t evt; fluid_alsa_seq_driver_t* dev = (fluid_alsa_seq_driver_t*) d; /* go into a loop until someone tells us to stop */ while (!g_atomic_int_get(&dev->should_quit)) { /* is there something to read? */ n = poll(dev->pfd, dev->npfd, 100); /* use a 100 milliseconds timeout */ if (n < 0) { perror("poll"); } else if (n > 0) { /* check for pending events */ do { ev = snd_seq_event_input(dev->seq_handle, &seq_ev); /* read the events */ if (ev == -EAGAIN) break; /* Negative value indicates an error, ignore interrupted system call * (-EPERM) and input event buffer overrun (-ENOSPC) */ if (ev < 0) { /* FIXME - report buffer overrun? */ if (ev != -EPERM && ev != -ENOSPC) { FLUID_LOG(FLUID_ERR, "Error while reading ALSA sequencer (code=%d)", ev); g_atomic_int_set(&dev->should_quit, 1); } break; } switch (seq_ev->type) { case SND_SEQ_EVENT_NOTEON: evt.type = NOTE_ON; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel; evt.param1 = seq_ev->data.note.note; evt.param2 = seq_ev->data.note.velocity; break; case SND_SEQ_EVENT_NOTEOFF: evt.type = NOTE_OFF; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel; evt.param1 = seq_ev->data.note.note; evt.param2 = seq_ev->data.note.velocity; break; case SND_SEQ_EVENT_KEYPRESS: evt.type = KEY_PRESSURE; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.note.channel; evt.param1 = seq_ev->data.note.note; evt.param2 = seq_ev->data.note.velocity; break; case SND_SEQ_EVENT_CONTROLLER: evt.type = CONTROL_CHANGE; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel; evt.param1 = seq_ev->data.control.param; evt.param2 = seq_ev->data.control.value; break; case SND_SEQ_EVENT_PITCHBEND: evt.type = PITCH_BEND; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel; /* ALSA pitch bend is -8192 - 8191, we adjust it here */ evt.param1 = seq_ev->data.control.value + 8192; break; case SND_SEQ_EVENT_PGMCHANGE: evt.type = PROGRAM_CHANGE; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel; evt.param1 = seq_ev->data.control.value; break; case SND_SEQ_EVENT_CHANPRESS: evt.type = CHANNEL_PRESSURE; evt.channel = seq_ev->dest.port * 16 + seq_ev->data.control.channel; evt.param1 = seq_ev->data.control.value; break; case SND_SEQ_EVENT_SYSEX: if (seq_ev->data.ext.len < 2) continue; fluid_midi_event_set_sysex (&evt, (char *)(seq_ev->data.ext.ptr) + 1, seq_ev->data.ext.len - 2, FALSE); break; default: continue; /* unhandled event, next loop iteration */ } /* send the events to the next link in the chain */ (*dev->driver.handler)(dev->driver.data, &evt); } while (ev > 0); } /* if poll() > 0 */ } /* while (!dev->should_quit) */ } #endif /* #if ALSA_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_aufile.c000066400000000000000000000070451322272076000207630ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_aufile.c * * Audio driver, outputs the audio to a file (non real-time) * */ #include "fluid_adriver.h" #include "fluid_settings.h" #include "fluid_sys.h" #include "config.h" #include /** fluid_file_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; fluid_audio_func_t callback; void* data; fluid_file_renderer_t* renderer; int period_size; double sample_rate; fluid_timer_t* timer; unsigned int samples; } fluid_file_audio_driver_t; fluid_audio_driver_t* new_fluid_file_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_file_audio_driver(fluid_audio_driver_t* p); static int fluid_file_audio_run_s16(void* d, unsigned int msec); /************************************************************** * * 'file' audio driver * */ fluid_audio_driver_t* new_fluid_file_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { fluid_file_audio_driver_t* dev; int msec; dev = FLUID_NEW(fluid_file_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_file_audio_driver_t)); fluid_settings_getint(settings, "audio.period-size", &dev->period_size); fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate); dev->data = synth; dev->callback = (fluid_audio_func_t) fluid_synth_process; dev->samples = 0; dev->renderer = new_fluid_file_renderer(synth); if (dev->renderer == NULL) goto error_recovery; msec = (int) (0.5 + dev->period_size / dev->sample_rate * 1000.0); dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, TRUE, FALSE, TRUE); if (dev->timer == NULL) { FLUID_LOG(FLUID_PANIC, "Couldn't create the audio thread."); goto error_recovery; } return (fluid_audio_driver_t*) dev; error_recovery: delete_fluid_file_audio_driver((fluid_audio_driver_t*) dev); return NULL; } int delete_fluid_file_audio_driver(fluid_audio_driver_t* p) { fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } if (dev->timer != NULL) { delete_fluid_timer(dev->timer); } if (dev->renderer != NULL) { delete_fluid_file_renderer(dev->renderer); } FLUID_FREE(dev); return FLUID_OK; } static int fluid_file_audio_run_s16(void* d, unsigned int clock_time) { fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) d; unsigned int sample_time; sample_time = (unsigned int) (dev->samples / dev->sample_rate * 1000.0); if (sample_time > clock_time) { return 1; } dev->samples += dev->period_size; return fluid_file_renderer_process_block(dev->renderer) == FLUID_OK ? 1 : 0; } fluidsynth-1.1.9/src/drivers/fluid_coreaudio.c000066400000000000000000000306301322272076000214640ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_coreaudio.c * * Driver for the Apple's CoreAudio on MacOS X * */ #include "fluid_synth.h" #include "fluid_midi.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #include "config.h" #if COREAUDIO_SUPPORT #include #include #include #include /* * fluid_core_audio_driver_t * */ typedef struct { fluid_audio_driver_t driver; AudioUnit outputUnit; AudioStreamBasicDescription format; fluid_audio_func_t callback; void* data; unsigned int buffer_size; float* buffers[2]; double phase; } fluid_core_audio_driver_t; fluid_audio_driver_t* new_fluid_core_audio_driver (fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_core_audio_driver2 (fluid_settings_t* settings, fluid_audio_func_t func, void* data); OSStatus fluid_core_audio_callback (void *data, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); int delete_fluid_core_audio_driver (fluid_audio_driver_t* p); /************************************************************** * * CoreAudio audio driver * */ #define OK(x) (x == noErr) int get_num_outputs (AudioDeviceID deviceID) { int i, total = 0; UInt32 size; AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyStreamConfiguration; pa.mScope = kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { int num = size / (int) sizeof (AudioBufferList); AudioBufferList bufList[num]; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) { int numStreams = bufList->mNumberBuffers; for (i = 0; i < numStreams; ++i) { AudioBuffer b = bufList->mBuffers[i]; total += b.mNumberChannels; } } } return total; } void fluid_core_audio_driver_settings(fluid_settings_t* settings) { int i; UInt32 size; AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; fluid_settings_register_str (settings, "audio.coreaudio.device", "default", 0, NULL, NULL); fluid_settings_add_option (settings, "audio.coreaudio.device", "default"); if (OK (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, 0, &size))) { int num = size / (int) sizeof (AudioDeviceID); AudioDeviceID devs [num]; if (OK (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, devs))) { for (i = 0; i < num; ++i) { char name [1024]; size = sizeof (name); pa.mSelector = kAudioDevicePropertyDeviceName; if (OK (AudioObjectGetPropertyData (devs[i], &pa, 0, 0, &size, name))) { if ( get_num_outputs (devs[i]) > 0) { fluid_settings_add_option (settings, "audio.coreaudio.device", name); } } } } } } /* * new_fluid_core_audio_driver */ 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_audio_func_t) fluid_synth_process, (void*) synth ); } /* * new_fluid_core_audio_driver2 */ fluid_audio_driver_t* new_fluid_core_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { char* devname = NULL; fluid_core_audio_driver_t* dev = NULL; int period_size, periods; double sample_rate; OSStatus status; UInt32 size; int i; dev = FLUID_NEW(fluid_core_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_core_audio_driver_t)); dev->callback = func; dev->data = data; // Open the default output unit ComponentDescription desc; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_HALOutput; //kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; Component comp = FindNextComponent(NULL, &desc); if (comp == NULL) { FLUID_LOG(FLUID_ERR, "Failed to get the default audio device"); goto error_recovery; } status = OpenAComponent(comp, &dev->outputUnit); if (status != noErr) { FLUID_LOG(FLUID_ERR, "Failed to open the default audio device. Status=%ld\n", (long int)status); goto error_recovery; } // Set up a callback function to generate output AURenderCallbackStruct render; render.inputProc = fluid_core_audio_callback; render.inputProcRefCon = (void *) dev; status = AudioUnitSetProperty (dev->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &render, sizeof(render)); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Error setting the audio callback. Status=%ld\n", (long int)status); goto error_recovery; } 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); /* get the selected device name. if none is specified, use NULL for the default device. */ if (fluid_settings_dupstr(settings, "audio.coreaudio.device", &devname) /* alloc device name */ && devname && strlen (devname) > 0) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioHardwarePropertyDevices; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; if (OK (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, 0, &size))) { int num = size / (int) sizeof (AudioDeviceID); AudioDeviceID devs [num]; if (OK (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, devs))) { for (i = 0; i < num; ++i) { char name [1024]; size = sizeof (name); pa.mSelector = kAudioDevicePropertyDeviceName; if (OK (AudioObjectGetPropertyData (devs[i], &pa, 0, 0, &size, name))) { if (get_num_outputs (devs[i]) > 0 && strcasecmp(devname, name) == 0) { AudioDeviceID selectedID = devs[i]; status = AudioUnitSetProperty (dev->outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &selectedID, sizeof(AudioDeviceID)); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Error setting the selected output device. Status=%ld\n", (long int)status); goto error_recovery; } } } } } } } if (devname) FLUID_FREE (devname); /* free device name */ dev->buffer_size = period_size * periods; // The DefaultOutputUnit should do any format conversions // necessary from our format to the device's format. dev->format.mSampleRate = sample_rate; // sample rate of the audio stream dev->format.mFormatID = kAudioFormatLinearPCM; // encoding type of the audio stream dev->format.mFormatFlags = kLinearPCMFormatFlagIsFloat; dev->format.mBytesPerPacket = 2*sizeof(float); dev->format.mFramesPerPacket = 1; dev->format.mBytesPerFrame = 2*sizeof(float); dev->format.mChannelsPerFrame = 2; dev->format.mBitsPerChannel = 8*sizeof(float); FLUID_LOG (FLUID_DBG, "mSampleRate %g", dev->format.mSampleRate); FLUID_LOG (FLUID_DBG, "mFormatFlags %08X", dev->format.mFormatFlags); FLUID_LOG (FLUID_DBG, "mBytesPerPacket %d", dev->format.mBytesPerPacket); FLUID_LOG (FLUID_DBG, "mFramesPerPacket %d", dev->format.mFramesPerPacket); FLUID_LOG (FLUID_DBG, "mChannelsPerFrame %d", dev->format.mChannelsPerFrame); FLUID_LOG (FLUID_DBG, "mBytesPerFrame %d", dev->format.mBytesPerFrame); FLUID_LOG (FLUID_DBG, "mBitsPerChannel %d", dev->format.mBitsPerChannel); status = AudioUnitSetProperty (dev->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dev->format, sizeof(AudioStreamBasicDescription)); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Error setting the audio format. Status=%ld\n", (long int)status); goto error_recovery; } status = AudioUnitSetProperty (dev->outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Input, 0, &dev->buffer_size, sizeof(unsigned int)); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Failed to set the MaximumFramesPerSlice. Status=%ld\n", (long int)status); goto error_recovery; } FLUID_LOG (FLUID_DBG, "MaximumFramesPerSlice = %d", dev->buffer_size); dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size); dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size); // Initialize the audio unit status = AudioUnitInitialize(dev->outputUnit); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Error calling AudioUnitInitialize(). Status=%ld\n", (long int)status); goto error_recovery; } // Start the rendering status = AudioOutputUnitStart (dev->outputUnit); if (status != noErr) { FLUID_LOG (FLUID_ERR, "Error calling AudioOutputUnitStart(). Status=%ld\n", (long int)status); goto error_recovery; } return (fluid_audio_driver_t*) dev; error_recovery: delete_fluid_core_audio_driver((fluid_audio_driver_t*) dev); return NULL; } /* * delete_fluid_core_audio_driver */ int delete_fluid_core_audio_driver(fluid_audio_driver_t* p) { fluid_core_audio_driver_t* dev = (fluid_core_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } CloseComponent (dev->outputUnit); if (dev->buffers[0]) { FLUID_FREE(dev->buffers[0]); } if (dev->buffers[1]) { FLUID_FREE(dev->buffers[1]); } FLUID_FREE(dev); return FLUID_OK; } OSStatus fluid_core_audio_callback ( void *data, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { int i, k; fluid_core_audio_driver_t* dev = (fluid_core_audio_driver_t*) data; int len = inNumberFrames; float* buffer = ioData->mBuffers[0].mData; if (dev->callback) { float* left = dev->buffers[0]; float* right = dev->buffers[1]; (*dev->callback)(dev->data, len, 0, NULL, 2, dev->buffers); for (i = 0, k = 0; i < len; i++) { buffer[k++] = left[i]; buffer[k++] = right[i]; } } else fluid_synth_write_float((fluid_synth_t*) dev->data, len, buffer, 0, 2, buffer, 1, 2); return noErr; } #endif /* #if COREAUDIO_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_coremidi.c000066400000000000000000000131061322272076000213040ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_coremidi.c * * Driver for Mac OSX native MIDI * Pedro Lopez-Cabanillas, Jan 2009 */ #include "fluidsynth_priv.h" #if COREMIDI_SUPPORT #include "fluid_midi.h" #include "fluid_mdriver.h" #include "fluid_settings.h" /* Work around for OSX 10.4 */ /* enum definition in OpenTransportProviders.h defines these tokens which are #defined from */ #ifdef TCP_NODELAY #undef TCP_NODELAY #endif #ifdef TCP_MAXSEG #undef TCP_MAXSEG #endif #ifdef TCP_KEEPALIVE #undef TCP_KEEPALIVE #endif /* End work around */ #include #include #include typedef struct { fluid_midi_driver_t driver; MIDIClientRef client; MIDIEndpointRef endpoint; fluid_midi_parser_t* parser; } fluid_coremidi_driver_t; fluid_midi_driver_t* new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data); int delete_fluid_coremidi_driver(fluid_midi_driver_t* p); void fluid_coremidi_callback(const MIDIPacketList *list, void *p, void *src); void fluid_coremidi_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "midi.coremidi.id", "pid", 0, NULL, NULL); } /* * new_fluid_coremidi_driver */ fluid_midi_driver_t* new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { fluid_coremidi_driver_t* dev; MIDIClientRef client; MIDIEndpointRef endpoint; char clientid[32]; char * portname; char * id; CFStringRef str_portname; CFStringRef str_clientname; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } dev = FLUID_MALLOC(sizeof(fluid_coremidi_driver_t)); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } dev->client = 0; dev->endpoint = 0; dev->parser = 0; dev->driver.handler = handler; dev->driver.data = data; dev->parser = new_fluid_midi_parser(); if (dev->parser == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } fluid_settings_dupstr(settings, "midi.coremidi.id", &id); /* ++ alloc id string */ memset (clientid, 0, sizeof(clientid)); if (id != NULL) { if (FLUID_STRCMP (id, "pid") == 0) { snprintf (clientid, sizeof(clientid), " (%d)", getpid()); } else { snprintf (clientid, sizeof(clientid), " (%s)", id); } FLUID_FREE (id); /* -- free id string */ } str_clientname = CFStringCreateWithFormat (NULL, NULL, CFSTR("FluidSynth%s"), clientid); fluid_settings_dupstr(settings, "midi.portname", &portname); /* ++ alloc port name */ if (!portname || strlen(portname) == 0) str_portname = CFStringCreateWithFormat (NULL, NULL, CFSTR("FluidSynth virtual port%s"), clientid); else str_portname = CFStringCreateWithCString (NULL, portname, kCFStringEncodingASCII); if (portname) FLUID_FREE (portname); /* -- free port name */ OSStatus result = MIDIClientCreate( str_clientname, NULL, NULL, &client ); if ( result != noErr ) { FLUID_LOG(FLUID_ERR, "Failed to create the MIDI input client"); goto error_recovery; } dev->client = client; result = MIDIDestinationCreate( client, str_portname, fluid_coremidi_callback, (void *)dev, &endpoint ); if ( result != noErr ) { FLUID_LOG(FLUID_ERR, "Failed to create the MIDI input port. MIDI input not available."); goto error_recovery; } dev->endpoint = endpoint; return (fluid_midi_driver_t*) dev; error_recovery: delete_fluid_coremidi_driver((fluid_midi_driver_t*) dev); return NULL; } /* * delete_fluid_coremidi_driver */ int delete_fluid_coremidi_driver(fluid_midi_driver_t* p) { fluid_coremidi_driver_t* dev = (fluid_coremidi_driver_t*) p; if (dev->client != NULL) { MIDIClientDispose(dev->client); } if (dev->endpoint != NULL) { MIDIEndpointDispose(dev->endpoint); } if (dev->parser != NULL) { delete_fluid_midi_parser(dev->parser); } FLUID_FREE(dev); return 0; } void fluid_coremidi_callback(const MIDIPacketList *list, void *p, void *src) { unsigned int i, j; fluid_midi_event_t* event; fluid_coremidi_driver_t* dev = (fluid_coremidi_driver_t *)p; const MIDIPacket *packet = &list->packet[0]; for ( i = 0; i < list->numPackets; ++i ) { for ( j = 0; j < packet->length; ++j ) { event = fluid_midi_parser_parse(dev->parser, packet->data[j]); if (event != NULL) { (*dev->driver.handler)(dev->driver.data, event); } } packet = MIDIPacketNext(packet); } } #endif /* COREMIDI_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_dart.c000066400000000000000000000207401322272076000204450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_dart.c * * Driver for OS/2 DART * */ #include "fluid_adriver.h" #include "fluid_settings.h" #include "fluid_sys.h" #if DART_SUPPORT /* To avoid name conflict */ #undef VERSION #define INCL_DOS #include #define INCL_OS2MM #include #define NUM_MIX_BUFS 2 /** fluid_dart_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; fluid_synth_t* synth; int frame_size; USHORT usDeviceID; /* Amp Mixer device id */ MCI_MIX_BUFFER MixBuffers[NUM_MIX_BUFS]; /* Device buffers */ MCI_MIXSETUP_PARMS MixSetupParms; /* Mixer parameters */ MCI_BUFFER_PARMS BufferParms; /* Device buffer parms */ } fluid_dart_audio_driver_t; static HMODULE m_hmodMDM = NULLHANDLE; static ULONG (APIENTRY *m_pfnmciSendCommand)(USHORT, USHORT, ULONG, PVOID, USHORT) = NULL; fluid_audio_driver_t* new_fluid_dart_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_dart_audio_driver(fluid_audio_driver_t* p); void fluid_dart_audio_driver_settings(fluid_settings_t* settings); static LONG APIENTRY fluid_dart_audio_run( ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags ); /************************************************************** * * DART audio driver * */ void fluid_dart_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.dart.device", "default", 0, NULL, NULL); } fluid_audio_driver_t* new_fluid_dart_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { fluid_dart_audio_driver_t* dev; double sample_rate; int periods, period_size; UCHAR szFailedName[ 256 ]; MCI_AMP_OPEN_PARMS AmpOpenParms; int i; ULONG rc; dev = FLUID_NEW(fluid_dart_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_dart_audio_driver_t)); 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); /* check the format */ if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); goto error_recovery; } dev->synth = synth; dev->frame_size = 2 * sizeof(short); /* Load only once */ if( m_hmodMDM == NULLHANDLE ) { rc = DosLoadModule(szFailedName, sizeof(szFailedName), "MDM", &m_hmodMDM); if (rc != 0 ) { FLUID_LOG(FLUID_ERR, "Cannot load MDM.DLL for DART due to %s", szFailedName); goto error_recovery; } rc = DosQueryProcAddr(m_hmodMDM, 1, NULL, (PFN *)&m_pfnmciSendCommand); if (rc != 0 ) { FLUID_LOG(FLUID_ERR, "Cannot find mciSendCommand() in MDM.DLL"); DosFreeModule(m_hmodMDM); m_hmodMDM = NULLHANDLE; goto error_recovery; } } /* open the mixer device */ FLUID_MEMSET(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); AmpOpenParms.usDeviceID = (USHORT)0; AmpOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX; rc = m_pfnmciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, (PVOID)&AmpOpenParms, 0); if (rc != MCIERR_SUCCESS) { FLUID_LOG(FLUID_ERR, "Cannot open DART, rc = %lu", rc); goto error_recovery; } dev->usDeviceID = AmpOpenParms.usDeviceID; /* Set the MixSetupParms data structure to match the requirements. * This is a global that is used to setup the mixer. */ dev->MixSetupParms.ulBitsPerSample = BPS_16; dev->MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; dev->MixSetupParms.ulSamplesPerSec = sample_rate; dev->MixSetupParms.ulChannels = 2; /* Setup the mixer for playback of wave data */ dev->MixSetupParms.ulFormatMode = MCI_PLAY; dev->MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; dev->MixSetupParms.pmixEvent = fluid_dart_audio_run; rc = m_pfnmciSendCommand(dev->usDeviceID, MCI_MIXSETUP, MCI_WAIT | MCI_MIXSETUP_INIT, (PVOID)&dev->MixSetupParms, 0); if (rc != MCIERR_SUCCESS) { FLUID_LOG(FLUID_ERR, "Cannot setup DART, rc = %lu", rc); goto error_recovery; } /* Set up the BufferParms data structure and allocate * device buffers from the Amp-Mixer */ dev->BufferParms.ulNumBuffers = NUM_MIX_BUFS; dev->BufferParms.ulBufferSize = periods * period_size * dev->frame_size; dev->BufferParms.pBufList = dev->MixBuffers; rc = m_pfnmciSendCommand(dev->usDeviceID, MCI_BUFFER, MCI_WAIT | MCI_ALLOCATE_MEMORY, (PVOID)&dev->BufferParms, 0); if ((USHORT)rc != MCIERR_SUCCESS) { FLUID_LOG(FLUID_ERR, "Cannot allocate memory for DART, rc = %lu", rc); goto error_recovery; } /* Initialize all device buffers. */ for (i = 0; i < NUM_MIX_BUFS; i++) { FLUID_MEMSET(dev->MixBuffers[i].pBuffer, 0, dev->BufferParms.ulBufferSize); dev->MixBuffers[i].ulBufferLength = dev->BufferParms.ulBufferSize; dev->MixBuffers[i].ulFlags = 0; dev->MixBuffers[i].ulUserParm = (ULONG)dev; fluid_synth_write_s16(dev->synth, dev->MixBuffers[i].ulBufferLength / dev->frame_size, dev->MixBuffers[i].pBuffer, 0, 2, dev->MixBuffers[i].pBuffer, 1, 2 ); } /* Write buffers to kick off the amp mixer. */ dev->MixSetupParms.pmixWrite(dev->MixSetupParms.ulMixHandle, dev->MixBuffers, NUM_MIX_BUFS); return (fluid_audio_driver_t*) dev; error_recovery: delete_fluid_dart_audio_driver((fluid_audio_driver_t*) dev); return NULL; } int delete_fluid_dart_audio_driver(fluid_audio_driver_t* p) { fluid_dart_audio_driver_t* dev = (fluid_dart_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } if (dev->usDeviceID != 0) { MCI_GENERIC_PARMS GenericParms; /* Send message to stop the audio device */ m_pfnmciSendCommand(dev->usDeviceID, MCI_STOP, MCI_WAIT, (PVOID)&GenericParms, 0); /* Deallocate device buffers */ m_pfnmciSendCommand(dev->usDeviceID, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, (PVOID)&dev->BufferParms, 0); /* Close device the mixer device */ m_pfnmciSendCommand(dev->usDeviceID, MCI_CLOSE, MCI_WAIT, (PVOID)&GenericParms, 0); } FLUID_FREE(dev); return FLUID_OK; } static LONG APIENTRY fluid_dart_audio_run( ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags ) { fluid_dart_audio_driver_t* dev=(fluid_dart_audio_driver_t*)pBuffer->ulUserParm; switch( ulFlags ) { case MIX_STREAM_ERROR | MIX_WRITE_COMPLETE: /* error occur in device */ case MIX_WRITE_COMPLETE: /* for playback */ FLUID_MEMSET(pBuffer->pBuffer, 0, pBuffer->ulBufferLength); fluid_synth_write_s16(dev->synth, pBuffer->ulBufferLength / dev->frame_size, pBuffer->pBuffer, 0, 2, pBuffer->pBuffer, 1, 2 ); dev->MixSetupParms.pmixWrite(dev->MixSetupParms.ulMixHandle, pBuffer, 1); break; } return TRUE; } #endif /* #if DART_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_dsound.c000066400000000000000000000265171322272076000210170ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #define INITGUID #include "fluidsynth_priv.h" #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_adriver.h" #include "fluid_settings.h" #include #include fluid_audio_driver_t* new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* data); DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter); char* fluid_win32_error(HRESULT hr); typedef struct { fluid_audio_driver_t driver; LPDIRECTSOUND direct_sound; LPDIRECTSOUNDBUFFER prim_buffer; LPDIRECTSOUNDBUFFER sec_buffer; WAVEFORMATEX* format; HANDLE thread; DWORD threadID; fluid_synth_t* synth; fluid_audio_callback_t write; int cont; DWORD buffer_byte_size; DWORD queue_byte_size; DWORD frame_size; } fluid_dsound_audio_driver_t; typedef struct { LPGUID devGUID; char* devname; } fluid_dsound_devsel_t; BOOL CALLBACK fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context) { fluid_settings_t* settings = (fluid_settings_t*) context; fluid_settings_add_option(settings, "audio.dsound.device", (const char *)description); return TRUE; } BOOL CALLBACK fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context) { fluid_dsound_devsel_t* devsel = (fluid_dsound_devsel_t*) context; FLUID_LOG(FLUID_DBG, "Testing audio device: %s", description); if (strcasecmp(devsel->devname, description) == 0) { devsel->devGUID = FLUID_NEW(GUID); if(devsel->devGUID) { memcpy(devsel->devGUID, guid, sizeof(GUID)); FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID); } } return TRUE; } void fluid_dsound_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.dsound.device", "default", 0, NULL, NULL); fluid_settings_add_option(settings, "audio.dsound.device", "default"); DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback, settings); } /* * new_fluid_dsound_audio_driver */ fluid_audio_driver_t* new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { HRESULT hr; DSBUFFERDESC desc; fluid_dsound_audio_driver_t* dev = NULL; DSCAPS caps; char *buf1; DWORD bytes1; double sample_rate; int periods, period_size; fluid_dsound_devsel_t devsel; /* create and clear the driver data */ dev = FLUID_NEW(fluid_dsound_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t)); dev->synth = synth; dev->cont = 1; 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); /* check the format */ if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); goto error_recovery; } dev->frame_size = 2 * sizeof(short); dev->buffer_byte_size = period_size * dev->frame_size; dev->queue_byte_size = periods * dev->buffer_byte_size; dev->write = fluid_synth_write_s16; /* create and initialize the buffer format */ dev->format = (WAVEFORMATEX*) FLUID_MALLOC(sizeof(WAVEFORMATEX)); if (dev->format == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } ZeroMemory(dev->format, sizeof(WAVEFORMATEX)); dev->format->wFormatTag = WAVE_FORMAT_PCM; dev->format->nChannels = 2; dev->format->wBitsPerSample = 16; dev->format->nSamplesPerSec = (DWORD) sample_rate; dev->format->nBlockAlign = (WORD) dev->frame_size; dev->format->nAvgBytesPerSec = dev->format->nSamplesPerSec * dev->frame_size; dev->format->cbSize = 0; devsel.devGUID = NULL; /* get the selected device name. if none is specified, use NULL for the default device. */ if(fluid_settings_dupstr(settings, "audio.dsound.device", &devsel.devname) /* ++ alloc device name */ && devsel.devname && strlen (devsel.devname) > 0) { /* look for the GUID of the selected device */ DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel); } if (devsel.devname) FLUID_FREE (devsel.devname); /* -- free device name */ /* open DirectSound */ hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL); if (hr != DS_OK) { FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object"); goto error_recovery; } hr = IDirectSound_SetCooperativeLevel(dev->direct_sound, GetDesktopWindow(), DSSCL_PRIORITY); if (hr != DS_OK) { FLUID_LOG(FLUID_ERR, "Failed to set the cooperative level"); goto error_recovery; } caps.dwSize = sizeof(caps); hr = IDirectSound_GetCaps(dev->direct_sound, &caps); if (hr != DS_OK) { FLUID_LOG(FLUID_ERR, "Failed to query the device capacities"); goto error_recovery; } /* create primary buffer */ ZeroMemory(&desc, sizeof(DSBUFFERDESC)); desc.dwSize = sizeof(DSBUFFERDESC); desc.dwFlags = DSBCAPS_PRIMARYBUFFER; if (caps.dwFreeHwMixingStreamingBuffers > 0) { desc.dwFlags |= DSBCAPS_LOCHARDWARE; } hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->prim_buffer, NULL); if (hr != DS_OK) { FLUID_LOG(FLUID_ERR, "Failed to allocate the primary buffer"); goto error_recovery; } /* set the primary sound buffer to this format. if it fails, just print a warning. */ hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, dev->format); if (hr != DS_OK) { FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer", fluid_win32_error(hr)); } /* initialize the buffer description */ ZeroMemory(&desc, sizeof(DSBUFFERDESC)); desc.dwSize = sizeof(DSBUFFERDESC); desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; desc.lpwfxFormat = dev->format; desc.dwBufferBytes = dev->queue_byte_size; desc.dwReserved = 0; if (caps.dwFreeHwMixingStreamingBuffers > 0) { desc.dwFlags |= DSBCAPS_LOCHARDWARE; } /* create the secondary sound buffer */ hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->sec_buffer, NULL); if (hr != DS_OK) { FLUID_LOG(FLUID_ERR, "dsound: Can't create sound buffer: %s", fluid_win32_error(hr)); goto error_recovery; } /* Lock */ hr = IDirectSoundBuffer_Lock(dev->sec_buffer, 0, 0, (void*) &buf1, &bytes1, 0, 0, DSBLOCK_ENTIREBUFFER); if ((hr != DS_OK) || (buf1 == NULL)) { FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. Exiting."); goto error_recovery; } /* fill the buffer with silence */ memset(buf1, 0, bytes1); /* Unlock */ IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0); /* start the audio thread */ dev->thread = CreateThread(NULL, 0, &fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID); if (dev->thread == NULL) { goto error_recovery; } return (fluid_audio_driver_t*) dev; error_recovery: delete_fluid_dsound_audio_driver((fluid_audio_driver_t*) dev); return NULL; } int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* d) { fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) d; if (dev == NULL) { return FLUID_OK; } /* tell the audio thread to stop its loop */ dev->cont = 0; /* wait till the audio thread exits */ if (dev->thread != 0) { if (WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) { /* on error kill the thread mercilessly */ FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it."); TerminateThread(dev->thread, 0); } } /* release all the allocated ressources */ if (dev->format != NULL) { FLUID_FREE(dev->format); } if (dev->sec_buffer != NULL) { IDirectSoundBuffer_Stop(dev->sec_buffer); IDirectSoundBuffer_Release(dev->sec_buffer); } if (dev->prim_buffer != NULL) { IDirectSoundBuffer_Release(dev->prim_buffer); } if (dev->direct_sound != NULL) { IDirectSound_Release(dev->direct_sound); } FLUID_FREE(dev); // fluid_win32_destroy_window(); return 0; } DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter) { fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) lpParameter; short *buf1, *buf2; DWORD bytes1, bytes2; DWORD cur_position, frames, play_position, write_position, bytes; HRESULT res; cur_position = 0; /* boost the priority of the audio thread */ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING); while (dev->cont) { IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position); if (cur_position <= play_position) { bytes = play_position - cur_position; } else if ((play_position < cur_position) && (write_position <= cur_position)) { bytes = dev->queue_byte_size + play_position - cur_position; } else { bytes = 0; } if (bytes >= dev->buffer_byte_size) { /* Lock */ res = IDirectSoundBuffer_Lock(dev->sec_buffer, cur_position, bytes, (void*) &buf1, &bytes1, (void*) &buf2, &bytes2, 0); if ((res != DS_OK) || (buf1 == NULL)) { FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. System lockup might follow. Exiting."); ExitProcess(0); } /* fill the first part of the buffer */ if (bytes1 > 0) { frames = bytes1 / dev->frame_size; dev->write(dev->synth, frames, buf1, 0, 2, buf1, 1, 2); cur_position += frames * dev->frame_size; } /* fill the second part of the buffer */ if ((buf2 != NULL) && (bytes2 > 0)) { frames = bytes2 / dev->frame_size; dev->write(dev->synth, frames, buf2, 0, 2, buf2, 1, 2); cur_position += frames * dev->frame_size; } /* Unlock */ IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, buf2, bytes2); if (cur_position >= dev->queue_byte_size) { cur_position -= dev->queue_byte_size; } } else { Sleep(1); } } ExitThread(0); return 0; /* never reached */ } char* fluid_win32_error(HRESULT hr) { char *s = "Don't know why"; switch (hr) { case E_NOINTERFACE: s = "No such interface"; break; case DSERR_GENERIC: s = "Generic error"; break; case DSERR_ALLOCATED: s = "Required resources already allocated"; break; case DSERR_BADFORMAT: s = "The format is not supported"; break; case DSERR_INVALIDPARAM: s = "Invalid parameter"; break; case DSERR_NOAGGREGATION: s = "No aggregation"; break; case DSERR_OUTOFMEMORY: s = "Out of memory"; break; case DSERR_UNINITIALIZED: s = "Uninitialized"; break; case DSERR_UNSUPPORTED: s = "Function not supported"; break; } return s; } fluidsynth-1.1.9/src/drivers/fluid_jack.c000066400000000000000000000507771322272076000204400ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_jack.c * * Driver for the JACK * * This code is derived from the simple_client example in the JACK * source distribution. Many thanks to Paul Davis. * */ #include "fluidsynth_priv.h" #include "fluid_sys.h" #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #include #include #include "config.h" #include "fluid_lash.h" typedef struct _fluid_jack_audio_driver_t fluid_jack_audio_driver_t; typedef struct _fluid_jack_midi_driver_t fluid_jack_midi_driver_t; /* Clients are shared for drivers using the same server. */ typedef struct { jack_client_t *client; char *server; /* Jack server name used */ fluid_jack_audio_driver_t *audio_driver; fluid_jack_midi_driver_t *midi_driver; } fluid_jack_client_t; /* Jack audio driver instance */ struct _fluid_jack_audio_driver_t { fluid_audio_driver_t driver; fluid_jack_client_t *client_ref; int audio_channels; jack_port_t **output_ports; int num_output_ports; float **output_bufs; jack_port_t **fx_ports; int num_fx_ports; float **fx_bufs; fluid_audio_func_t callback; void* data; }; /* Jack MIDI driver instance */ struct _fluid_jack_midi_driver_t { fluid_midi_driver_t driver; fluid_jack_client_t *client_ref; jack_port_t *midi_port; fluid_midi_parser_t *parser; }; static fluid_jack_client_t *new_fluid_jack_client (fluid_settings_t *settings, int isaudio, void *driver); static int fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *client, fluid_settings_t *settings); fluid_audio_driver_t* new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_jack_audio_driver(fluid_audio_driver_t* p); 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); int delete_fluid_jack_midi_driver(fluid_midi_driver_t *p); static fluid_mutex_t last_client_mutex = G_STATIC_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. */ void fluid_jack_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.jack.id", "fluidsynth", 0, NULL, NULL); fluid_settings_register_int(settings, "audio.jack.multi", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "audio.jack.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_str(settings, "audio.jack.server", "", 0, NULL, NULL); } /* * Create Jack client as necessary, share clients of the same server. * @param settings Settings object * @param isaudio TRUE if audio driver, FALSE if MIDI * @param driver fluid_jack_audio_driver_t or fluid_jack_midi_driver_t * @param data The user data instance associated with the driver (fluid_synth_t for example) * @return New or paired Audio/MIDI Jack client */ static fluid_jack_client_t * new_fluid_jack_client (fluid_settings_t *settings, int isaudio, void *driver) { fluid_jack_client_t *client_ref = NULL; char *server = NULL; char* client_name; char name[64]; fluid_settings_dupstr (settings, isaudio ? "audio.jack.server" /* ++ alloc server name */ : "midi.jack.server", &server); fluid_mutex_lock (last_client_mutex); /* ++ lock last_client */ /* If the last client uses the same server and is not the same type (audio or MIDI), * then re-use the client. */ if (last_client && ((!last_client->server && !server) || FLUID_STRCMP (last_client->server, server) == 0) && ((!isaudio && !last_client->midi_driver) || (isaudio && !last_client->audio_driver))) { client_ref = last_client; last_client = NULL; /* No more pairing for this client */ /* Register ports */ if (fluid_jack_client_register_ports (driver, isaudio, client_ref->client, settings) != FLUID_OK) goto error_recovery; if (isaudio) fluid_atomic_pointer_set (&client_ref->audio_driver, driver); else fluid_atomic_pointer_set (&client_ref->midi_driver, driver); fluid_mutex_unlock (last_client_mutex); /* -- unlock last_client */ if (server) FLUID_FREE (server); return client_ref; } /* No existing client for this Jack server */ client_ref = FLUID_NEW (fluid_jack_client_t); if (!client_ref) { FLUID_LOG (FLUID_ERR, "Out of memory"); goto error_recovery; } FLUID_MEMSET (client_ref, 0, sizeof (fluid_jack_client_t)); fluid_settings_dupstr (settings, isaudio ? "audio.jack.id" /* ++ alloc client name */ : "midi.jack.id", &client_name); if (client_name != NULL && client_name[0] != 0) snprintf(name, 64, "%s", client_name); else strcpy (name, "fluidsynth"); name[63] = '\0'; if (client_name) FLUID_FREE (client_name); /* -- free client name */ /* Open a connection to the Jack server and use the server name if specified */ if (server && server[0] != '\0') client_ref->client = jack_client_open (name, JackServerName, NULL, server); else client_ref->client = jack_client_open (name, JackNullOption, NULL); if (!client_ref->client) { FLUID_LOG (FLUID_ERR, "Failed to connect to Jack server."); goto error_recovery; } 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); jack_on_shutdown (client_ref->client, fluid_jack_driver_shutdown, client_ref); /* Register ports */ if (fluid_jack_client_register_ports (driver, isaudio, client_ref->client, settings) != FLUID_OK) goto error_recovery; /* tell the JACK server that we are ready to roll */ if (jack_activate (client_ref->client)) { FLUID_LOG (FLUID_ERR, "Failed to activate Jack client"); goto error_recovery; } /* tell the lash server our client name */ #ifdef LASH_ENABLED { int enable_lash = 0; fluid_settings_getint (settings, "lash.enable", &enable_lash); if (enable_lash) fluid_lash_jack_client_name (fluid_lash_client, name); } #endif /* LASH_ENABLED */ client_ref->server = server; /* !! takes over allocation */ server = NULL; /* Set to NULL so it doesn't get freed below */ last_client = client_ref; if (isaudio) fluid_atomic_pointer_set (&client_ref->audio_driver, driver); else fluid_atomic_pointer_set (&client_ref->midi_driver, driver); fluid_mutex_unlock (last_client_mutex); /* -- unlock last_client */ if (server) FLUID_FREE (server); return client_ref; error_recovery: fluid_mutex_unlock (last_client_mutex); /* -- unlock clients list */ if (server) FLUID_FREE (server); /* -- free server name */ if (client_ref) { if (client_ref->client) jack_client_close (client_ref->client); FLUID_FREE (client_ref); } return NULL; } static int fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *client, fluid_settings_t *settings) { fluid_jack_audio_driver_t *dev; char name[64]; int multi; int i; int jack_srate; double sample_rate; if (!isaudio) { fluid_jack_midi_driver_t *dev = driver; dev->midi_port = jack_port_register (client, "midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput | JackPortIsTerminal, 0); if (!dev->midi_port) { FLUID_LOG (FLUID_ERR, "Failed to create Jack MIDI port"); return FLUID_FAILED; } return FLUID_OK; } dev = driver; fluid_settings_getint (settings, "audio.jack.multi", &multi); if (!multi) { /* create the two audio output ports */ dev->num_output_ports = 1; dev->num_fx_ports = 0; dev->output_ports = FLUID_ARRAY (jack_port_t*, 2 * dev->num_output_ports); if (dev->output_ports == NULL) { FLUID_LOG (FLUID_PANIC, "Jack server not running?"); return FLUID_FAILED; } dev->output_bufs = FLUID_ARRAY (float*, 2 * dev->num_output_ports); FLUID_MEMSET (dev->output_ports, 0, 2 * dev->num_output_ports * sizeof(jack_port_t*)); dev->output_ports[0] = jack_port_register (client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); dev->output_ports[1] = jack_port_register (client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } else { fluid_settings_getint (settings, "synth.audio-channels", &dev->num_output_ports); dev->output_ports = FLUID_ARRAY (jack_port_t *, 2 * dev->num_output_ports); if (dev->output_ports == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } dev->output_bufs = FLUID_ARRAY (float *, 2 * dev->num_output_ports); if (dev->output_bufs == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } FLUID_MEMSET (dev->output_ports, 0, 2 * dev->num_output_ports * sizeof (jack_port_t*)); for (i = 0; i < dev->num_output_ports; i++) { sprintf(name, "l_%02d", i); dev->output_ports[2 * i] = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); sprintf(name, "r_%02d", i); dev->output_ports[2 * i + 1] = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } fluid_settings_getint (settings, "synth.effects-channels", &dev->num_fx_ports); dev->fx_ports = FLUID_ARRAY(jack_port_t*, 2 * dev->num_fx_ports); if (dev->fx_ports == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } dev->fx_bufs = FLUID_ARRAY(float*, 2 * dev->num_fx_ports); if (dev->fx_bufs == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } FLUID_MEMSET(dev->fx_ports, 0, 2 * dev->num_fx_ports * sizeof(jack_port_t*)); for (i = 0; i < dev->num_fx_ports; i++) { sprintf(name, "fx_l_%02d", i); dev->fx_ports[2 * i] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); sprintf(name, "fx_r_%02d", i); dev->fx_ports[2 * i + 1] = jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } } /* Adjust sample rate to match JACK's */ jack_srate = jack_get_sample_rate (client); FLUID_LOG (FLUID_DBG, "Jack engine sample rate: %lu", jack_srate); fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate); if ((int)sample_rate != jack_srate) { FLUID_LOG(FLUID_INFO, "Jack sample rate mismatch, adjusting." " (synth.sample-rate=%lu, jackd=%lu)", (int)sample_rate, jack_srate); fluid_settings_setnum (settings, "synth.sample-rate", jack_srate); } /* Changing sample rate is non RT, so make sure we process it and/or other things now */ if (dev->callback == NULL) fluid_synth_process_event_queue(dev->data); return FLUID_OK; } static void fluid_jack_client_close (fluid_jack_client_t *client_ref, void *driver) { if (client_ref->audio_driver == driver) fluid_atomic_pointer_set (&client_ref->audio_driver, NULL); else if (client_ref->midi_driver == driver) fluid_atomic_pointer_set (&client_ref->midi_driver, NULL); if (client_ref->audio_driver || client_ref->midi_driver) { g_usleep (100000); /* FIXME - Hack to make sure that resources don't get freed while Jack callback is active */ return; } fluid_mutex_lock (last_client_mutex); if (client_ref == last_client) last_client = NULL; fluid_mutex_unlock (last_client_mutex); if (client_ref->client) jack_client_close (client_ref->client); if (client_ref->server) FLUID_FREE (client_ref->server); FLUID_FREE (client_ref); } fluid_audio_driver_t* new_fluid_jack_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { return new_fluid_jack_audio_driver2(settings, NULL, synth); } fluid_audio_driver_t* new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { fluid_jack_audio_driver_t* dev = NULL; jack_client_t *client; const char ** jack_ports; /* for looking up ports */ int autoconnect = 0; int i; dev = FLUID_NEW(fluid_jack_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_jack_audio_driver_t)); dev->callback = func; dev->data = data; dev->client_ref = new_fluid_jack_client (settings, TRUE, dev); if (!dev->client_ref) { FLUID_FREE (dev); return NULL; } client = dev->client_ref->client; /* connect the ports. */ /* FIXME: should be done by a patchbay application */ /* find some physical ports and connect to them */ fluid_settings_getint (settings, "audio.jack.autoconnect", &autoconnect); if (autoconnect) { jack_ports = jack_get_ports (client, NULL, NULL, JackPortIsInput|JackPortIsPhysical); if (jack_ports) { int err; int connected = 0; for (i = 0; jack_ports[i] && i<2 * dev->num_output_ports; ++i) { err = jack_connect (client, jack_port_name(dev->output_ports[i]), jack_ports[i]); if (err) { FLUID_LOG(FLUID_ERR, "Error connecting jack port"); } else { connected++; } } for (i = 0; jack_ports[i] && i<2 * dev->num_fx_ports; ++i) { err = jack_connect (client, jack_port_name(dev->fx_ports[i]), jack_ports[i]); if (err) { FLUID_LOG(FLUID_ERR, "Error connecting jack port"); } else { connected++; } } jack_free (jack_ports); /* free jack ports array (not the port values!) */ } else { FLUID_LOG(FLUID_WARN, "Could not connect to any physical jack ports; fluidsynth is unconnected"); } } return (fluid_audio_driver_t*) dev; } /* * delete_fluid_jack_audio_driver */ int delete_fluid_jack_audio_driver(fluid_audio_driver_t* p) { fluid_jack_audio_driver_t* dev = (fluid_jack_audio_driver_t*) p; if (dev == NULL) return 0; if (dev->client_ref != NULL) fluid_jack_client_close (dev->client_ref, dev); if (dev->output_bufs) FLUID_FREE (dev->output_bufs); if (dev->output_ports) FLUID_FREE (dev->output_ports); if (dev->fx_bufs) FLUID_FREE(dev->fx_bufs); if (dev->fx_ports) FLUID_FREE(dev->fx_ports); FLUID_FREE(dev); return 0; } /* Process function for audio and MIDI Jack drivers */ int fluid_jack_driver_process (jack_nframes_t nframes, void *arg) { fluid_jack_client_t *client = (fluid_jack_client_t *)arg; fluid_jack_audio_driver_t *audio_driver; fluid_jack_midi_driver_t *midi_driver; float *left, *right; int i, k; jack_midi_event_t midi_event; fluid_midi_event_t *evt; void *midi_buffer; jack_nframes_t event_count; jack_nframes_t event_index; unsigned int u; /* Process MIDI events first, so that they take effect before audio synthesis */ midi_driver = fluid_atomic_pointer_get (&client->midi_driver); if (midi_driver) { midi_buffer = jack_port_get_buffer (midi_driver->midi_port, 0); event_count = jack_midi_get_event_count (midi_buffer); for (event_index = 0; event_index < event_count; event_index++) { jack_midi_event_get (&midi_event, midi_buffer, event_index); /* let the parser convert the data into events */ for (u = 0; u < midi_event.size; u++) { evt = fluid_midi_parser_parse (midi_driver->parser, midi_event.buffer[u]); /* send the event to the next link in the chain */ if (evt != NULL) midi_driver->driver.handler (midi_driver->driver.data, evt); } } } audio_driver = client->audio_driver; if (!audio_driver) return 0; if (audio_driver->callback != NULL) { for (i = 0; i < audio_driver->num_output_ports * 2; i++) audio_driver->output_bufs[i] = (float *)jack_port_get_buffer (audio_driver->output_ports[i], nframes); return (*audio_driver->callback)(audio_driver->data, nframes, 0, NULL, 2 * audio_driver->num_output_ports, audio_driver->output_bufs); } else if (audio_driver->num_output_ports == 1 && audio_driver->num_fx_ports == 0) /* i.e. audio.jack.multi=no */ { left = (float*) jack_port_get_buffer (audio_driver->output_ports[0], nframes); right = (float*) jack_port_get_buffer (audio_driver->output_ports[1], nframes); fluid_synth_write_float (audio_driver->data, nframes, left, 0, 1, right, 0, 1); } else { for (i = 0, k = audio_driver->num_output_ports; i < audio_driver->num_output_ports; i++, k++) { audio_driver->output_bufs[i] = (float *)jack_port_get_buffer (audio_driver->output_ports[2*i], nframes); audio_driver->output_bufs[k] = (float *)jack_port_get_buffer (audio_driver->output_ports[2*i+1], nframes); } for (i = 0, k = audio_driver->num_fx_ports; i < audio_driver->num_fx_ports; i++, k++) { audio_driver->fx_bufs[i] = (float*) jack_port_get_buffer(audio_driver->fx_ports[2*i], nframes); audio_driver->fx_bufs[k] = (float*) jack_port_get_buffer(audio_driver->fx_ports[2*i+1], nframes); } fluid_synth_nwrite_float (audio_driver->data, nframes, audio_driver->output_bufs, audio_driver->output_bufs + audio_driver->num_output_ports, audio_driver->fx_bufs, audio_driver->fx_bufs + audio_driver->num_fx_ports); } return 0; } int fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg) { /* printf("the maximum buffer size is now %lu\n", nframes); */ return 0; } int fluid_jack_driver_srate(jack_nframes_t nframes, void *arg) { /* printf("the sample rate is now %lu/sec\n", nframes); */ /* FIXME: change the sample rate of the synthesizer! */ return 0; } void fluid_jack_driver_shutdown(void *arg) { // fluid_jack_audio_driver_t* dev = (fluid_jack_audio_driver_t*) arg; FLUID_LOG(FLUID_ERR, "Help! Lost the connection to the JACK server"); /* exit (1); */ } void fluid_jack_midi_driver_settings (fluid_settings_t *settings) { fluid_settings_register_str (settings, "midi.jack.id", "fluidsynth-midi", 0, NULL, NULL); fluid_settings_register_str (settings, "midi.jack.server", "", 0, NULL, NULL); } /* * new_fluid_jack_midi_driver */ fluid_midi_driver_t * new_fluid_jack_midi_driver (fluid_settings_t *settings, handle_midi_event_func_t handler, void *data) { fluid_jack_midi_driver_t* dev; fluid_return_val_if_fail (handler != NULL, NULL); /* allocate the device */ dev = FLUID_NEW (fluid_jack_midi_driver_t); if (dev == NULL) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET (dev, 0, sizeof (fluid_jack_midi_driver_t)); dev->driver.handler = handler; dev->driver.data = data; /* allocate one event to store the input data */ dev->parser = new_fluid_midi_parser (); if (dev->parser == NULL) { FLUID_LOG (FLUID_ERR, "Out of memory"); FLUID_FREE (dev); return NULL; } dev->client_ref = new_fluid_jack_client (settings, FALSE, dev); if (!dev->client_ref) { FLUID_FREE (dev); return NULL; } return (fluid_midi_driver_t *)dev; } int delete_fluid_jack_midi_driver(fluid_midi_driver_t *p) { fluid_jack_midi_driver_t *dev = (fluid_jack_midi_driver_t *)p; if (dev == NULL) return FLUID_OK; if (dev->client_ref != NULL) fluid_jack_client_close (dev->client_ref, dev); if (dev->parser != NULL) delete_fluid_midi_parser (dev->parser); FLUID_FREE (dev); return FLUID_OK; } fluidsynth-1.1.9/src/drivers/fluid_mdriver.c000066400000000000000000000202471322272076000211650ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_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 /* ALSA */ #if ALSA_SUPPORT fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p); void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings); fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_alsa_seq_driver(fluid_midi_driver_t* p); void fluid_alsa_seq_driver_settings(fluid_settings_t* settings); #endif /* JACK */ #if JACK_SUPPORT void fluid_jack_midi_driver_settings (fluid_settings_t *settings); fluid_midi_driver_t *new_fluid_jack_midi_driver (fluid_settings_t *settings, handle_midi_event_func_t handler, void *data); int delete_fluid_jack_midi_driver(fluid_midi_driver_t *p); #endif /* OSS */ #if OSS_SUPPORT fluid_midi_driver_t* new_fluid_oss_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_oss_midi_driver(fluid_midi_driver_t* p); void fluid_oss_midi_driver_settings(fluid_settings_t* settings); #endif /* Windows MIDI service */ #if WINMIDI_SUPPORT fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_winmidi_driver(fluid_midi_driver_t* p); void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings); #endif /* definitions for the MidiShare driver */ #if MIDISHARE_SUPPORT fluid_midi_driver_t* new_fluid_midishare_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_midishare_midi_driver(fluid_midi_driver_t* p); #endif /* definitions for the CoreMidi driver */ #if COREMIDI_SUPPORT fluid_midi_driver_t* new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data); int delete_fluid_coremidi_driver(fluid_midi_driver_t* p); void fluid_coremidi_driver_settings(fluid_settings_t* settings); #endif #ifdef FLUID_MIDI_SUPPORT /* * fluid_mdriver_definition */ struct fluid_mdriver_definition_t { char* name; fluid_midi_driver_t* (*new)(fluid_settings_t* settings, handle_midi_event_func_t event_handler, void* event_handler_data); int (*free)(fluid_midi_driver_t* p); void (*settings)(fluid_settings_t* settings); }; struct fluid_mdriver_definition_t fluid_midi_drivers[] = { #if JACK_SUPPORT { "jack", new_fluid_jack_midi_driver, delete_fluid_jack_midi_driver, fluid_jack_midi_driver_settings }, #endif #if OSS_SUPPORT { "oss", new_fluid_oss_midi_driver, delete_fluid_oss_midi_driver, 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", new_fluid_winmidi_driver, delete_fluid_winmidi_driver, fluid_winmidi_midi_driver_settings }, #endif #if MIDISHARE_SUPPORT { "midishare", new_fluid_midishare_midi_driver, delete_fluid_midishare_midi_driver, NULL }, #endif #if COREMIDI_SUPPORT { "coremidi", new_fluid_coremidi_driver, delete_fluid_coremidi_driver, fluid_coremidi_driver_settings }, #endif { NULL, NULL, NULL, NULL } }; #endif /* FLUID_MIDI_SUPPORT */ void fluid_midi_driver_settings(fluid_settings_t* settings) { int i; fluid_settings_register_int (settings, "midi.realtime-prio", FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0, NULL, NULL); /* Set the default driver */ #if ALSA_SUPPORT fluid_settings_register_str(settings, "midi.driver", "alsa_seq", 0, NULL, NULL); #elif JACK_SUPPORT fluid_settings_register_str(settings, "midi.driver", "jack", 0, NULL, NULL); #elif OSS_SUPPORT fluid_settings_register_str(settings, "midi.driver", "oss", 0, NULL, NULL); #elif WINMIDI_SUPPORT fluid_settings_register_str(settings, "midi.driver", "winmidi", 0, NULL, NULL); #elif MIDISHARE_SUPPORT fluid_settings_register_str(settings, "midi.driver", "midishare", 0, NULL, NULL); #elif COREMIDI_SUPPORT fluid_settings_register_str(settings, "midi.driver", "coremidi", 0, NULL, NULL); #else fluid_settings_register_str(settings, "midi.driver", "", 0, NULL, NULL); #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; fluid_midi_drivers[i].name != NULL; i++) { if (fluid_midi_drivers[i].settings != NULL) { fluid_midi_drivers[i].settings(settings); } } #endif } /** * Create a new MIDI driver instance. * @param settings Settings used to configure new MIDI driver. * @param handler MIDI handler callback (for example: fluid_midi_router_handle_midi_event() * for MIDI router) * @param event_handler_data Caller defined data to pass to 'handler' * @return New MIDI driver instance or NULL on error */ 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; int i; for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { if (fluid_settings_str_equal(settings, "midi.driver", fluid_midi_drivers[i].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); if (driver) { driver->name = fluid_midi_drivers[i].name; } return 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) FLUID_FREE (allnames); #endif return NULL; } /** * Delete a MIDI driver instance. * @param driver MIDI driver to delete */ void delete_fluid_midi_driver(fluid_midi_driver_t* driver) { #ifdef FLUID_MIDI_SUPPORT int i; for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { if (fluid_midi_drivers[i].name == driver->name) { fluid_midi_drivers[i].free(driver); return; } } #endif } fluidsynth-1.1.9/src/drivers/fluid_mdriver.h000066400000000000000000000021751322272076000211720ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_MDRIVER_H #define _FLUID_MDRIVER_H #include "fluidsynth_priv.h" void fluid_midi_driver_settings(fluid_settings_t* settings); /* * fluid_midi_driver_t */ struct _fluid_midi_driver_t { char* name; handle_midi_event_func_t handler; void* data; }; #endif /* _FLUID_AUDRIVER_H */ fluidsynth-1.1.9/src/drivers/fluid_midishare.c000066400000000000000000000305611322272076000214620ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_midishare.c * * Author: Stephane Letz (letz@grame.fr) Grame * * Interface to Grame's MidiShare drivers (www.grame.fr/MidiShare) * 21/12/01 : Add a compilation flag (MIDISHARE_DRIVER) for driver or application mode * 29/01/02 : Compilation on MacOSX, use a task for typeNote management * 03/06/03 : Adapdation for FluidSynth API * 18/03/04 : In appplication mode, connect MidiShare to the fluidsynth client (fluid_midishare_open_appl) */ #include "config.h" #if MIDISHARE_SUPPORT #include "fluid_midi.h" #include "fluid_mdriver.h" #include /* constants definitions */ #define MidiShareDrvRef 127 #if defined(MACINTOSH) && defined(MACOS9) #define MSHSlotName "\pfluidsynth" #define MSHDriverName "\pfluidsynth" #else #define MSHSlotName "fluidsynth" #define MSHDriverName "fluidsynth" #endif typedef struct { fluid_midi_driver_t driver; int status; short refnum; MidiFilterPtr filter; #if defined(MACINTOSH) && defined(MACOS9) UPPRcvAlarmPtr upp_alarm_ptr; UPPDriverPtr upp_wakeup_ptr; UPPDriverPtr upp_sleep_ptr; UPPTaskPtr upp_task_ptr; #endif SlotRefNum slotRef; unsigned char sysexbuf[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; } fluid_midishare_midi_driver_t; fluid_midi_driver_t* new_fluid_midishare_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data); int delete_fluid_midishare_midi_driver(fluid_midi_driver_t* p); int fluid_midishare_midi_driver_status(fluid_midi_driver_t* p); static void fluid_midishare_midi_driver_receive(short ref); #if defined(MIDISHARE_DRIVER) static int fluid_midishare_open_driver (fluid_midishare_midi_driver_t* dev); static void fluid_midishare_close_driver (fluid_midishare_midi_driver_t* dev); #else static int fluid_midishare_open_appl (fluid_midishare_midi_driver_t* dev); static void fluid_midishare_close_appl (fluid_midishare_midi_driver_t* dev); #endif /* * new_fluid_midishare_midi_driver */ fluid_midi_driver_t* new_fluid_midishare_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { fluid_midishare_midi_driver_t* dev; int i; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } /* allocate the device */ dev = FLUID_NEW(fluid_midishare_midi_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_midishare_midi_driver_t)); dev->driver.handler = handler; dev->driver.data = data; /* register to MidiShare as Application or Driver */ #if defined(MIDISHARE_DRIVER) if (!fluid_midishare_open_driver(dev)) goto error_recovery; #else if (!fluid_midishare_open_appl(dev)) goto error_recovery; #endif /*MidiSetInfo(dev->refnum, dev->router->synth); */ MidiSetInfo(dev->refnum, dev); dev->filter = MidiNewFilter(); if (dev->filter == 0) { FLUID_LOG(FLUID_ERR, "Can not allocate MidiShare filter"); goto error_recovery; } for (i = 0 ; i < 256; i++) { MidiAcceptPort(dev->filter, i, 1); /* accept all ports */ MidiAcceptType(dev->filter, i, 0); /* reject all types */ } for (i = 0 ; i < 16; i++) { MidiAcceptChan(dev->filter, i, 1); /* accept all chan */ } /* accept only the following types */ MidiAcceptType(dev->filter, typeNote, 1); MidiAcceptType(dev->filter, typeKeyOn, 1); MidiAcceptType(dev->filter, typeKeyOff, 1); MidiAcceptType(dev->filter, typeCtrlChange, 1); MidiAcceptType(dev->filter, typeProgChange, 1); MidiAcceptType(dev->filter, typePitchWheel, 1); MidiAcceptType(dev->filter, typeSysEx, 1); /* set the filter */ MidiSetFilter(dev->refnum, dev->filter); dev->status = FLUID_MIDI_READY; return (fluid_midi_driver_t*) dev; error_recovery: delete_fluid_midishare_midi_driver((fluid_midi_driver_t*) dev); return NULL; } /* * delete_fluid_midishare_midi_driver */ int delete_fluid_midishare_midi_driver(fluid_midi_driver_t* p) { fluid_midishare_midi_driver_t* dev; dev = (fluid_midishare_midi_driver_t*) p; if (dev == NULL) { return FLUID_OK; } if (dev->filter) MidiFreeFilter(dev->filter); #if defined(MIDISHARE_DRIVER) fluid_midishare_close_driver(dev); #else fluid_midishare_close_appl(dev); #endif #if defined(MACINTOSH) && defined(MACOS9) DisposeRoutineDescriptor(dev->upp_alarm_ptr); DisposeRoutineDescriptor(dev->upp_wakeup_ptr); DisposeRoutineDescriptor(dev->upp_sleep_ptr); DisposeRoutineDescriptor(dev->upp_task_ptr); #endif dev->status = FLUID_MIDI_DONE; FLUID_FREE(dev); return FLUID_OK; } /* * fluid_midishare_midi_driver_status */ int fluid_midishare_midi_driver_status(fluid_midi_driver_t* p) { fluid_midishare_midi_driver_t* dev = (fluid_midishare_midi_driver_t*) p; return dev->status; } /* * fluid_midishare_keyoff_task */ static void fluid_midishare_keyoff_task (long date, short ref, long a1, long a2, long a3) { fluid_midishare_midi_driver_t* dev = (fluid_midishare_midi_driver_t*)MidiGetInfo(ref); fluid_midi_event_t new_event; MidiEvPtr e =(MidiEvPtr)a1; fluid_midi_event_set_type(&new_event, NOTE_OFF); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_pitch(&new_event, Pitch(e)); fluid_midi_event_set_velocity(&new_event, Vel(e)); /* release vel */ /* and send it on its way to the router */ (*dev->driver.handler)(dev->driver.data, &new_event); MidiFreeEv(e); } /* * fluid_midishare_midi_driver_receive */ static void fluid_midishare_midi_driver_receive(short ref) { fluid_midishare_midi_driver_t* dev = (fluid_midishare_midi_driver_t*)MidiGetInfo(ref); fluid_midi_event_t new_event; MidiEvPtr e; int count, i; while ((e = MidiGetEv (ref))) { switch (EvType (e)) { case typeNote: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, NOTE_ON); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_pitch(&new_event, Pitch(e)); fluid_midi_event_set_velocity(&new_event, Vel(e)); /* and send it on its way to the router */ (*dev->driver.handler)(dev->driver.data, &new_event); #if defined(MACINTOSH) && defined(MACOS9) MidiTask(dev->upp_task_ptr, MidiGetTime()+Dur(e), ref, (long)e, 0, 0); #else MidiTask(fluid_midishare_keyoff_task, MidiGetTime()+Dur(e), ref, (long)e, 0, 0); #endif /* e gets freed in fluid_midishare_keyoff_task */ continue; case typeKeyOn: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, NOTE_ON); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_pitch(&new_event, Pitch(e)); fluid_midi_event_set_velocity(&new_event, Vel(e)); break; case typeKeyOff: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, NOTE_OFF); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_pitch(&new_event, Pitch(e)); fluid_midi_event_set_velocity(&new_event, Vel(e)); /* release vel */ break; case typeCtrlChange: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, CONTROL_CHANGE); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_control(&new_event, MidiGetField(e,0)); fluid_midi_event_set_value(&new_event, MidiGetField(e,1)); break; case typeProgChange: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, PROGRAM_CHANGE); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_program(&new_event, MidiGetField(e,0)); break; case typePitchWheel: /* Copy the data to fluid_midi_event_t */ fluid_midi_event_set_type(&new_event, PITCH_BEND); fluid_midi_event_set_channel(&new_event, Chan(e)); fluid_midi_event_set_value(&new_event, ((MidiGetField(e,0) + (MidiGetField(e,1) << 7)) - 8192)); break; case typeSysEx: count = MidiCountFields (e); /* Discard empty or too large SYSEX messages */ if (count == 0 || count > FLUID_MIDI_PARSER_MAX_DATA_SIZE) { MidiFreeEv (e); continue; } /* Copy SYSEX data, one byte at a time */ for (i = 0; i < count; i++) dev->sysexbuf[i] = MidiGetField (e, i); fluid_midi_event_set_sysex (&new_event, dev->sysexbuf, count, FALSE); break; default: MidiFreeEv (e); continue; } MidiFreeEv (e); /* Send the MIDI event */ (*dev->driver.handler)(dev->driver.data, &new_event); } } #if defined(MIDISHARE_DRIVER) /* * fluid_midishare_wakeup */ static void fluid_midishare_wakeup (short r) { MidiConnect (MidiShareDrvRef, r, true); MidiConnect (r, MidiShareDrvRef, true); } /* * fluid_midishare_sleep */ static void fluid_midishare_sleep (short r){} /* * fluid_midishare_open_driver */ static int fluid_midishare_open_driver (fluid_midishare_midi_driver_t* dev) { /* gcc wanted me to use {0,0} to initialize the reserved[2] fields */ TDriverInfos infos = { MSHDriverName, 100, 0, { 0, 0 } }; TDriverOperation op = { fluid_midishare_wakeup, fluid_midishare_sleep, { 0, 0, 0 } }; /* register to MidiShare */ #if defined(MACINTOSH) && defined(MACOS9) dev->upp_wakeup_ptr = NewDriverPtr(fluid_midishare_wakeup); dev->upp_sleep_ptr = NewDriverPtr(fluid_midishare_sleep); op.wakeup = (WakeupPtr)dev->upp_wakeup_ptr; op.sleep = (SleepPtr)dev->upp_sleep_ptr; dev->refnum = MidiRegisterDriver(&infos, &op); if (dev->refnum < 0) { FLUID_LOG(FLUID_ERR, "Can not open MidiShare Application client"); return 0; } dev->slotRef = MidiAddSlot (dev->refnum, MSHSlotName, MidiOutputSlot); dev->upp_alarm_ptr = NewRcvAlarmPtr(fluid_midishare_midi_driver_receive); dev->upp_task_ptr = NewTaskPtr(fluid_midishare_keyoff_task); MidiSetRcvAlarm(dev->refnum, dev->upp_alarm_ptr); #else dev->refnum = MidiRegisterDriver(&infos, &op); if (dev->refnum < 0) { FLUID_LOG(FLUID_ERR, "Can not open MidiShare Application client"); return 0; } dev->slotRef = MidiAddSlot (dev->refnum, MSHSlotName, MidiOutputSlot); MidiSetRcvAlarm(dev->refnum, fluid_midishare_midi_driver_receive); #endif return 1; } /* * fluid_midishare_close_driver */ static void fluid_midishare_close_driver (fluid_midishare_midi_driver_t* dev) { if (dev->refnum > 0) MidiUnregisterDriver(dev->refnum); } #else /* #if defined(MIDISHARE_DRIVER) */ /* * fluid_midishare_open_appl */ static int fluid_midishare_open_appl (fluid_midishare_midi_driver_t* dev) { /* register to MidiShare */ #if defined(MACINTOSH) && defined(MACOS9) dev->refnum = MidiOpen(MSHDriverName); if (dev->refnum < 0) { FLUID_LOG(FLUID_ERR, "Can not open MidiShare Driver client"); return 0; } dev->upp_alarm_ptr = NewRcvAlarmPtr(fluid_midishare_midi_driver_receive); dev->upp_task_ptr = NewTaskPtr(fluid_midishare_keyoff_task); MidiSetRcvAlarm(dev->refnum, dev->upp_alarm_ptr); #else dev->refnum = MidiOpen(MSHDriverName); if (dev->refnum < 0) { FLUID_LOG(FLUID_ERR, "Can not open MidiShare Driver client"); return 0; } MidiSetRcvAlarm(dev->refnum, fluid_midishare_midi_driver_receive); MidiConnect(0,dev->refnum,true); #endif return 1; } /* * fluid_midishare_close_appl */ static void fluid_midishare_close_appl (fluid_midishare_midi_driver_t* dev) { if (dev->refnum > 0) MidiClose(dev->refnum); } #endif /* #if defined(MIDISHARE_DRIVER) */ #endif /* MIDISHARE_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_oss.c000066400000000000000000000422251322272076000203210ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_oss.c * * Drivers for the Open (?) Sound System */ #include "fluid_synth.h" #include "fluid_midi.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #if OSS_SUPPORT #include #include #include #include #include #include #include #include #include #define BUFFER_LENGTH 512 // Build issue on some systems (OSS 4.0)? #if !defined(SOUND_PCM_WRITE_CHANNELS) && defined(SNDCTL_DSP_CHANNELS) #define SOUND_PCM_WRITE_CHANNELS SNDCTL_DSP_CHANNELS #endif /** fluid_oss_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; fluid_synth_t* synth; fluid_audio_callback_t read; void* buffer; fluid_thread_t *thread; int cont; int dspfd; int buffer_size; int buffer_byte_size; int bigendian; int formats; int format; int caps; fluid_audio_func_t callback; void* data; float* buffers[2]; } fluid_oss_audio_driver_t; int delete_fluid_oss_audio_driver(fluid_audio_driver_t* p); /* local utilities */ static int fluid_oss_set_queue_size(fluid_oss_audio_driver_t* dev, int ss, int ch, int qs, int bs); static void fluid_oss_audio_run(void* d); static void fluid_oss_audio_run2(void* d); typedef struct { fluid_midi_driver_t driver; int fd; fluid_thread_t *thread; int status; unsigned char buffer[BUFFER_LENGTH]; fluid_midi_parser_t* parser; } fluid_oss_midi_driver_t; fluid_midi_driver_t* new_fluid_oss_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data); int delete_fluid_oss_midi_driver(fluid_midi_driver_t* p); int fluid_oss_midi_driver_status(fluid_midi_driver_t* p); static void fluid_oss_midi_run(void* d); void fluid_oss_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.oss.device", "/dev/dsp", 0, NULL, NULL); } /* * new_fluid_oss_audio_driver */ fluid_audio_driver_t* new_fluid_oss_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { fluid_oss_audio_driver_t* dev = NULL; int channels, sr, sample_size = 0, oss_format; struct stat devstat; int queuesize; double sample_rate; int periods, period_size; int realtime_prio = 0; char* devname = NULL; int format; dev = FLUID_NEW(fluid_oss_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_oss_audio_driver_t)); fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getint (settings, "audio.realtime-prio", &realtime_prio); dev->dspfd = -1; dev->synth = synth; dev->callback = NULL; dev->data = NULL; dev->cont = 1; dev->buffer_size = (int) period_size; queuesize = (int) (periods * period_size); if (fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { sample_size = 16; oss_format = AFMT_S16_LE; dev->read = fluid_synth_write_s16; dev->buffer_byte_size = dev->buffer_size * 4; } else if (fluid_settings_str_equal(settings, "audio.sample-format", "float")) { sample_size = 32; oss_format = -1; dev->read = fluid_synth_write_float; dev->buffer_byte_size = dev->buffer_size * 8; } else { FLUID_LOG(FLUID_ERR, "Unknown sample format"); goto error_recovery; } dev->buffer = FLUID_MALLOC(dev->buffer_byte_size); if (dev->buffer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } if (!fluid_settings_dupstr(settings, "audio.oss.device", &devname) || !devname) { /* ++ alloc device name */ devname = FLUID_STRDUP ("/dev/dsp"); if (devname == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } if (stat(devname, &devstat) == -1) { FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname); goto error_recovery; } if ((devstat.st_mode & S_IFCHR) != S_IFCHR) { FLUID_LOG(FLUID_ERR, "Device <%s> is not a device file", devname); goto error_recovery; } dev->dspfd = open(devname, O_WRONLY, 0); if (dev->dspfd == -1) { FLUID_LOG(FLUID_ERR, "Device <%s> could not be opened for writing: %s", devname, strerror(errno)); goto error_recovery; } if (fluid_oss_set_queue_size(dev, sample_size, 2, queuesize, period_size) < 0) { FLUID_LOG(FLUID_ERR, "Can't set device buffer size"); goto error_recovery; } format = oss_format; if (ioctl(dev->dspfd, SNDCTL_DSP_SETFMT, &oss_format) < 0) { FLUID_LOG(FLUID_ERR, "Can't set the sample format"); goto error_recovery; } if (oss_format != format) { FLUID_LOG(FLUID_ERR, "Can't set the sample format"); goto error_recovery; } channels = 2; if (ioctl(dev->dspfd, SOUND_PCM_WRITE_CHANNELS, &channels) < 0){ FLUID_LOG(FLUID_ERR, "Can't set the number of channels"); goto error_recovery; } if (channels != 2) { FLUID_LOG(FLUID_ERR, "Can't set the number of channels"); goto error_recovery; } sr = sample_rate; if (ioctl(dev->dspfd, SNDCTL_DSP_SPEED, &sr) < 0){ FLUID_LOG(FLUID_ERR, "Can't set the sample rate"); goto error_recovery; } if ((sr < 0.95 * sample_rate) || (sr > 1.05 * sample_rate)) { FLUID_LOG(FLUID_ERR, "Can't set the sample rate"); goto error_recovery; } /* Create the audio thread */ dev->thread = new_fluid_thread ("oss-audio", fluid_oss_audio_run, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (devname) FLUID_FREE (devname); /* -- free device name */ return (fluid_audio_driver_t*) dev; error_recovery: if (devname) FLUID_FREE (devname); /* -- free device name */ delete_fluid_oss_audio_driver((fluid_audio_driver_t*) dev); return NULL; } fluid_audio_driver_t* new_fluid_oss_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { fluid_oss_audio_driver_t* dev = NULL; int channels, sr; struct stat devstat; int queuesize; double sample_rate; int periods, period_size; char* devname = NULL; int realtime_prio = 0; int format; dev = FLUID_NEW(fluid_oss_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_oss_audio_driver_t)); fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getint (settings, "audio.realtime-prio", &realtime_prio); dev->dspfd = -1; dev->synth = NULL; dev->read = NULL; dev->callback = func; dev->data = data; dev->cont = 1; dev->buffer_size = (int) period_size; queuesize = (int) (periods * period_size); dev->buffer_byte_size = dev->buffer_size * 2 * 2; /* 2 channels * 16 bits audio */ if (!fluid_settings_dupstr(settings, "audio.oss.device", &devname) || !devname) { devname = FLUID_STRDUP ("/dev/dsp"); if (!devname) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } if (stat(devname, &devstat) == -1) { FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname); goto error_recovery; } if ((devstat.st_mode & S_IFCHR) != S_IFCHR) { FLUID_LOG(FLUID_ERR, "Device <%s> is not a device file", devname); goto error_recovery; } dev->dspfd = open(devname, O_WRONLY, 0); if (dev->dspfd == -1) { FLUID_LOG(FLUID_ERR, "Device <%s> could not be opened for writing: %s", devname, strerror(errno)); goto error_recovery; } if (fluid_oss_set_queue_size(dev, 16, 2, queuesize, period_size) < 0) { FLUID_LOG(FLUID_ERR, "Can't set device buffer size"); goto error_recovery; } format = AFMT_S16_LE; if (ioctl(dev->dspfd, SNDCTL_DSP_SETFMT, &format) < 0) { FLUID_LOG(FLUID_ERR, "Can't set the sample format"); goto error_recovery; } if (format != AFMT_S16_LE) { FLUID_LOG(FLUID_ERR, "Can't set the sample format"); goto error_recovery; } channels = 2; if (ioctl(dev->dspfd, SOUND_PCM_WRITE_CHANNELS, &channels) < 0){ FLUID_LOG(FLUID_ERR, "Can't set the number of channels"); goto error_recovery; } if (channels != 2) { FLUID_LOG(FLUID_ERR, "Can't set the number of channels"); goto error_recovery; } sr = sample_rate; if (ioctl(dev->dspfd, SNDCTL_DSP_SPEED, &sr) < 0){ FLUID_LOG(FLUID_ERR, "Can't set the sample rate"); goto error_recovery; } if ((sr < 0.95 * sample_rate) || (sr > 1.05 * sample_rate)) { FLUID_LOG(FLUID_ERR, "Can't set the sample rate"); goto error_recovery; } /* allocate the buffers. FIXME!!! don't use interleaved samples */ dev->buffer = FLUID_MALLOC(dev->buffer_byte_size); dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size); dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size); if ((dev->buffer == NULL) || (dev->buffers[0] == NULL) || (dev->buffers[1] == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } /* Create the audio thread */ dev->thread = new_fluid_thread ("oss-audio", fluid_oss_audio_run2, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (devname) FLUID_FREE (devname); /* -- free device name */ return (fluid_audio_driver_t*) dev; error_recovery: if (devname) FLUID_FREE (devname); /* -- free device name */ delete_fluid_oss_audio_driver((fluid_audio_driver_t*) dev); return NULL; } /* * delete_fluid_oss_audio_driver */ int delete_fluid_oss_audio_driver(fluid_audio_driver_t* p) { fluid_oss_audio_driver_t* dev = (fluid_oss_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } dev->cont = 0; if (dev->thread) fluid_thread_join (dev->thread); if (dev->dspfd >= 0) { close(dev->dspfd); } if (dev->buffer != NULL) { FLUID_FREE(dev->buffer); } FLUID_FREE(dev); return FLUID_OK; } /** * fluid_oss_set_queue_size * * Set the internal buffersize of the output device. * * @param ss Sample size in bits * @param ch Number of channels * @param qs The queue size in frames * @param bs The synthesis buffer size in frames */ int fluid_oss_set_queue_size(fluid_oss_audio_driver_t* dev, int ss, int ch, int qs, int bs) { unsigned int fragmentSize; unsigned int fragSizePower; unsigned int fragments; unsigned int fragmentsPower; fragmentSize = (unsigned int) (bs * ch * ss / 8); fragSizePower = 0; while (0 < fragmentSize) { fragmentSize = (fragmentSize >> 1); fragSizePower++; } fragSizePower--; fragments = (unsigned int) (qs / bs); if (fragments < 2) { fragments = 2; } /* make sure fragments is a power of 2 */ fragmentsPower = 0; while (0 < fragments) { fragments = (fragments >> 1); fragmentsPower++; } fragmentsPower--; fragments = (1 << fragmentsPower); fragments = (fragments << 16) + fragSizePower; return ioctl(dev->dspfd, SNDCTL_DSP_SETFRAGMENT, &fragments); } /* * fluid_oss_audio_run */ void fluid_oss_audio_run(void* d) { fluid_oss_audio_driver_t* dev = (fluid_oss_audio_driver_t*) d; fluid_synth_t* synth = dev->synth; void* buffer = dev->buffer; int len = dev->buffer_size; /* it's as simple as that: */ while (dev->cont) { dev->read (synth, len, buffer, 0, 2, buffer, 1, 2); if (write (dev->dspfd, buffer, dev->buffer_byte_size) < 0) { FLUID_LOG(FLUID_ERR, "Error writing to OSS sound device: %s", g_strerror (errno)); break; } } FLUID_LOG(FLUID_DBG, "Audio thread finished"); } /* * fluid_oss_audio_run */ void fluid_oss_audio_run2(void* d) { fluid_oss_audio_driver_t* dev = (fluid_oss_audio_driver_t*) d; short* buffer = (short*) dev->buffer; float* left = dev->buffers[0]; float* right = dev->buffers[1]; int buffer_size = dev->buffer_size; int dither_index = 0; FLUID_LOG(FLUID_DBG, "Audio thread running"); /* it's as simple as that: */ while (dev->cont) { (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->buffers); fluid_synth_dither_s16 (&dither_index, buffer_size, left, right, buffer, 0, 2, buffer, 1, 2); if (write (dev->dspfd, buffer, dev->buffer_byte_size) < 0) { FLUID_LOG(FLUID_ERR, "Error writing to OSS sound device: %s", g_strerror (errno)); break; } } FLUID_LOG(FLUID_DBG, "Audio thread finished"); } void fluid_oss_midi_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "midi.oss.device", "/dev/midi", 0, NULL, NULL); } /* * new_fluid_oss_midi_driver */ fluid_midi_driver_t* new_fluid_oss_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { fluid_oss_midi_driver_t* dev; int realtime_prio = 0; char* device = NULL; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } /* allocate the device */ dev = FLUID_NEW(fluid_oss_midi_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_oss_midi_driver_t)); dev->fd = -1; dev->driver.handler = handler; dev->driver.data = data; /* allocate one event to store the input data */ dev->parser = new_fluid_midi_parser(); if (dev->parser == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } /* get the device name. if none is specified, use the default device. */ fluid_settings_dupstr(settings, "midi.oss.device", &device); /* ++ alloc device name */ if (device == NULL) { device = FLUID_STRDUP ("/dev/midi"); if (!device) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } } fluid_settings_getint (settings, "midi.realtime-prio", &realtime_prio); /* open the default hardware device. only use midi in. */ dev->fd = open(device, O_RDONLY, 0); if (dev->fd < 0) { perror(device); goto error_recovery; } if (fcntl (dev->fd, F_SETFL, O_NONBLOCK) == -1) { FLUID_LOG(FLUID_ERR, "Failed to set OSS MIDI device to non-blocking: %s", strerror (errno)); goto error_recovery; } dev->status = FLUID_MIDI_READY; /* create MIDI thread */ dev->thread = new_fluid_thread ("oss-midi", fluid_oss_midi_run, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (device) FLUID_FREE (device); /* ++ free device */ return (fluid_midi_driver_t*) dev; error_recovery: if (device) FLUID_FREE (device); /* ++ free device */ delete_fluid_oss_midi_driver((fluid_midi_driver_t*) dev); return NULL; } /* * delete_fluid_oss_midi_driver */ int delete_fluid_oss_midi_driver(fluid_midi_driver_t* p) { fluid_oss_midi_driver_t* dev; dev = (fluid_oss_midi_driver_t*) p; if (dev == NULL) { return FLUID_OK; } /* cancel the thread and wait for it before cleaning up */ dev->status = FLUID_MIDI_DONE; if (dev->thread) fluid_thread_join (dev->thread); if (dev->fd >= 0) { close(dev->fd); } if (dev->parser != NULL) { delete_fluid_midi_parser(dev->parser); } FLUID_FREE(dev); return FLUID_OK; } /* * fluid_oss_midi_run */ void fluid_oss_midi_run(void* d) { fluid_oss_midi_driver_t* dev = (fluid_oss_midi_driver_t*) d; fluid_midi_event_t* evt; struct pollfd fds; int n, i; /* go into a loop until someone tells us to stop */ dev->status = FLUID_MIDI_LISTENING; fds.fd = dev->fd; fds.events = POLLIN; fds.revents = 0; while (dev->status == FLUID_MIDI_LISTENING) { n = poll (&fds, 1, 100); if (n == 0) continue; if (n < 0) { FLUID_LOG(FLUID_ERR, "Error waiting for MIDI input: %s", strerror (errno)); break; } /* read new data */ n = read(dev->fd, dev->buffer, BUFFER_LENGTH); if (n == -EAGAIN) continue; if (n < 0) { perror("read"); FLUID_LOG(FLUID_ERR, "Failed to read the midi input"); break; } /* let the parser convert the data into events */ for (i = 0; i < n; i++) { evt = fluid_midi_parser_parse(dev->parser, dev->buffer[i]); if (evt != NULL) { /* send the event to the next link in the chain */ (*dev->driver.handler)(dev->driver.data, evt); } } } } int fluid_oss_midi_driver_status(fluid_midi_driver_t* p) { fluid_oss_midi_driver_t* dev = (fluid_oss_midi_driver_t*) p; return dev->status; } #endif /*#if OSS_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_portaudio.c000066400000000000000000000163171322272076000215260ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_portaudio.c * * Drivers for the PortAudio API : www.portaudio.com * Implementation files for PortAudio on each platform have to be added * * Stephane Letz (letz@grame.fr) Grame * 12/20/01 Adapdation for new audio drivers * * Josh Green * 2009-01-28 Overhauled for PortAudio 19 API and current FluidSynth API (was broken) */ #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_settings.h" #include "fluid_adriver.h" #if PORTAUDIO_SUPPORT #include /** fluid_portaudio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; fluid_synth_t *synth; fluid_audio_callback_t read; PaStream *stream; } fluid_portaudio_driver_t; static int fluid_portaudio_run (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData); int delete_fluid_portaudio_driver (fluid_audio_driver_t *p); #define PORTAUDIO_DEFAULT_DEVICE "PortAudio Default" void fluid_portaudio_driver_settings (fluid_settings_t *settings) { const PaDeviceInfo *deviceInfo; int numDevices; PaError err; int i; fluid_settings_register_str (settings, "audio.portaudio.device", PORTAUDIO_DEFAULT_DEVICE, 0, NULL, NULL); fluid_settings_add_option (settings, "audio.portaudio.device", PORTAUDIO_DEFAULT_DEVICE); err = Pa_Initialize(); if (err != paNoError) { FLUID_LOG (FLUID_ERR, "Error initializing PortAudio driver: %s", Pa_GetErrorText (err)); return; } numDevices = Pa_GetDeviceCount(); if (numDevices < 0) { FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices); return; } for (i = 0; i < numDevices; i++) { deviceInfo = Pa_GetDeviceInfo (i); if ( deviceInfo->maxOutputChannels >= 2 ) fluid_settings_add_option (settings, "audio.portaudio.device", deviceInfo->name); } /* done with PortAudio for now, may get reopened later */ err = Pa_Terminate(); if (err != paNoError) printf ("PortAudio termination error: %s\n", Pa_GetErrorText (err) ); } fluid_audio_driver_t * new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) { fluid_portaudio_driver_t *dev = NULL; PaStreamParameters outputParams; char *device = NULL; double sample_rate; int period_size; PaError err; dev = FLUID_NEW (fluid_portaudio_driver_t); if (dev == NULL) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } err = Pa_Initialize (); if (err != paNoError) { FLUID_LOG (FLUID_ERR, "Error initializing PortAudio driver: %s", Pa_GetErrorText (err)); FLUID_FREE (dev); return NULL; } FLUID_MEMSET (dev, 0, sizeof (fluid_portaudio_driver_t)); dev->synth = synth; fluid_settings_getint (settings, "audio.period-size", &period_size); fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate); fluid_settings_dupstr(settings, "audio.portaudio.device", &device); /* ++ alloc device name */ memset (&outputParams, 0, sizeof (outputParams)); outputParams.channelCount = 2; outputParams.suggestedLatency = (PaTime)period_size / sample_rate; /* Locate the device if specified */ if (strcmp (device, PORTAUDIO_DEFAULT_DEVICE) != 0) { const PaDeviceInfo *deviceInfo; int numDevices; int i; numDevices = Pa_GetDeviceCount (); if (numDevices < 0) { FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices); goto error_recovery; } for (i = 0; i < numDevices; i++) { deviceInfo = Pa_GetDeviceInfo (i); if (strcmp (device, deviceInfo->name) == 0) { outputParams.device = i; break; } } if (i == numDevices) { FLUID_LOG (FLUID_ERR, "PortAudio device '%s' was not found", device); goto error_recovery; } } else outputParams.device = Pa_GetDefaultOutputDevice(); if (fluid_settings_str_equal (settings, "audio.sample-format", "16bits")) { outputParams.sampleFormat = paInt16; dev->read = fluid_synth_write_s16; } else if (fluid_settings_str_equal (settings, "audio.sample-format", "float")) { outputParams.sampleFormat = paFloat32; dev->read = fluid_synth_write_float; } else { FLUID_LOG (FLUID_ERR, "Unknown sample format"); goto error_recovery; } /* PortAudio section */ /* Open an audio I/O stream. */ err = Pa_OpenStream (&dev->stream, NULL, /* Input parameters */ &outputParams, sample_rate, period_size, paNoFlag, fluid_portaudio_run, dev); if (err != paNoError) { FLUID_LOG (FLUID_ERR, "Error opening PortAudio stream: %s", Pa_GetErrorText (err)); goto error_recovery; } err = Pa_StartStream (dev->stream); if (err != paNoError) { FLUID_LOG (FLUID_ERR, "Error starting PortAudio stream: %s", Pa_GetErrorText (err)); goto error_recovery; } if (device) FLUID_FREE (device); /* -- free device name */ return (fluid_audio_driver_t *)dev; error_recovery: if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_portaudio_driver ((fluid_audio_driver_t *)dev); return NULL; } /* PortAudio callback * fluid_portaudio_run */ static int fluid_portaudio_run (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { fluid_portaudio_driver_t *dev = (fluid_portaudio_driver_t *)userData; /* it's as simple as that: */ dev->read (dev->synth, frameCount, output, 0, 2, output, 1, 2); return 0; } /* * delete_fluid_portaudio_driver */ int delete_fluid_portaudio_driver(fluid_audio_driver_t *p) { fluid_portaudio_driver_t* dev; PaError err; dev = (fluid_portaudio_driver_t*)p; if (dev == NULL) return FLUID_OK; /* PortAudio section */ if (dev->stream) Pa_CloseStream (dev->stream); err = Pa_Terminate(); if (err != paNoError) printf ("PortAudio termination error: %s\n", Pa_GetErrorText (err) ); FLUID_FREE (dev); return FLUID_OK; } #endif /*#if PORTAUDIO_SUPPORT */ fluidsynth-1.1.9/src/drivers/fluid_pulse.c000066400000000000000000000177011322272076000206460ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_pulse.c * * Audio driver for PulseAudio. * */ #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #include "config.h" #include #include /** fluid_pulse_audio_driver_t * * This structure should not be accessed directly. Use audio port * functions instead. */ typedef struct { fluid_audio_driver_t driver; pa_simple *pa_handle; fluid_audio_func_t callback; void* data; int buffer_size; fluid_thread_t *thread; int cont; } fluid_pulse_audio_driver_t; fluid_audio_driver_t* new_fluid_pulse_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_pulse_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_pulse_audio_driver(fluid_audio_driver_t* p); void fluid_pulse_audio_driver_settings(fluid_settings_t* settings); static void fluid_pulse_audio_run(void* d); static void fluid_pulse_audio_run2(void* d); void fluid_pulse_audio_driver_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "audio.pulseaudio.server", "default", 0, NULL, NULL); fluid_settings_register_str(settings, "audio.pulseaudio.device", "default", 0, NULL, NULL); fluid_settings_register_str(settings, "audio.pulseaudio.media-role", "music", 0, NULL, NULL); fluid_settings_register_int(settings, "audio.pulseaudio.adjust-latency", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); } fluid_audio_driver_t* new_fluid_pulse_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { return new_fluid_pulse_audio_driver2(settings, NULL, synth); } fluid_audio_driver_t* new_fluid_pulse_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { fluid_pulse_audio_driver_t* dev; pa_sample_spec samplespec; pa_buffer_attr bufattr; double sample_rate; int period_size, period_bytes, adjust_latency; char *server = NULL; char *device = NULL; char *media_role = NULL; int realtime_prio = 0; int err; dev = FLUID_NEW(fluid_pulse_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_pulse_audio_driver_t)); // fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_dupstr(settings, "audio.pulseaudio.server", &server); /* ++ alloc server string */ fluid_settings_dupstr(settings, "audio.pulseaudio.device", &device); /* ++ alloc device string */ fluid_settings_dupstr(settings, "audio.pulseaudio.media-role", &media_role); /* ++ alloc media-role string */ fluid_settings_getint(settings, "audio.realtime-prio", &realtime_prio); fluid_settings_getint(settings, "audio.pulseaudio.adjust-latency", &adjust_latency); if (media_role != NULL) { if (strcmp(media_role, "") != 0) { g_setenv("PULSE_PROP_media.role", media_role, TRUE); } FLUID_FREE (media_role); /* -- free media_role string */ } if (server && strcmp (server, "default") == 0) { FLUID_FREE (server); /* -- free server string */ server = NULL; } if (device && strcmp (device, "default") == 0) { FLUID_FREE (device); /* -- free device string */ device = NULL; } dev->data = data; dev->callback = func; dev->cont = 1; dev->buffer_size = period_size; samplespec.format = PA_SAMPLE_FLOAT32NE; samplespec.channels = 2; samplespec.rate = sample_rate; period_bytes = period_size * sizeof (float) * 2; bufattr.maxlength = adjust_latency ? -1 : period_bytes; bufattr.tlength = period_bytes; bufattr.minreq = -1; bufattr.prebuf = -1; /* Just initialize to same value as tlength */ bufattr.fragsize = -1; /* Not used */ dev->pa_handle = pa_simple_new (server, "FluidSynth", PA_STREAM_PLAYBACK, device, "FluidSynth output", &samplespec, NULL, /* pa_channel_map */ &bufattr, &err); if (!dev->pa_handle) { FLUID_LOG(FLUID_ERR, "Failed to create PulseAudio connection"); goto error_recovery; } FLUID_LOG(FLUID_INFO, "Using PulseAudio driver"); /* Create the audio thread */ dev->thread = new_fluid_thread ("pulse-audio", func ? fluid_pulse_audio_run2 : fluid_pulse_audio_run, dev, realtime_prio, FALSE); if (!dev->thread) goto error_recovery; if (server) FLUID_FREE (server); /* -- free server string */ if (device) 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 */ delete_fluid_pulse_audio_driver((fluid_audio_driver_t*) dev); return NULL; } int delete_fluid_pulse_audio_driver(fluid_audio_driver_t* p) { fluid_pulse_audio_driver_t* dev = (fluid_pulse_audio_driver_t*) p; if (dev == NULL) { return FLUID_OK; } dev->cont = 0; if (dev->thread) fluid_thread_join (dev->thread); if (dev->pa_handle) pa_simple_free(dev->pa_handle); FLUID_FREE(dev); return FLUID_OK; } /* Thread without audio callback, more efficient */ static void fluid_pulse_audio_run(void* d) { fluid_pulse_audio_driver_t* dev = (fluid_pulse_audio_driver_t*) d; float *buf; int buffer_size; int err; buffer_size = dev->buffer_size; /* FIXME - Probably shouldn't alloc in run() */ buf = FLUID_ARRAY(float, buffer_size * 2); if (buf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); return; } while (dev->cont) { fluid_synth_write_float(dev->data, buffer_size, buf, 0, 2, buf, 1, 2); if (pa_simple_write (dev->pa_handle, buf, buffer_size * sizeof (float) * 2, &err) < 0) { FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection."); break; } } /* while (dev->cont) */ FLUID_FREE(buf); } static void fluid_pulse_audio_run2(void* d) { fluid_pulse_audio_driver_t* dev = (fluid_pulse_audio_driver_t*) d; fluid_synth_t *synth = (fluid_synth_t *)(dev->data); float *left, *right, *buf; float* handle[2]; int buffer_size; int err; int i; buffer_size = dev->buffer_size; /* FIXME - Probably shouldn't alloc in run() */ left = FLUID_ARRAY(float, buffer_size); right = FLUID_ARRAY(float, buffer_size); buf = FLUID_ARRAY(float, buffer_size * 2); if (left == NULL || right == NULL || buf == NULL) { FLUID_FREE(left); FLUID_FREE(right); FLUID_FREE(buf); FLUID_LOG(FLUID_ERR, "Out of memory."); return; } handle[0] = left; handle[1] = right; while (dev->cont) { (*dev->callback)(synth, buffer_size, 0, NULL, 2, handle); /* Interleave the floating point data */ for (i = 0; i < buffer_size; i++) { buf[i * 2] = left[i]; buf[i * 2 + 1] = right[i]; } if (pa_simple_write (dev->pa_handle, buf, buffer_size * sizeof (float) * 2, &err) < 0) { FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection."); break; } } /* while (dev->cont) */ FLUID_FREE(left); FLUID_FREE(right); FLUID_FREE(buf); } fluidsynth-1.1.9/src/drivers/fluid_sndmgr.c000066400000000000000000000244161322272076000210110ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_sndmgr.c * * Driver for MacOS Classic */ #if SNDMAN_SUPPORT #include "fluid_synth.h" #include "fluid_adriver.h" #include "fluid_settings.h" #include typedef struct { fluid_audio_driver_t driver; SndDoubleBufferHeader2* doubleHeader; SndDoubleBackUPP doubleCallbackProc; SndChannelPtr channel; int callback_is_audio_func; void* data; fluid_audio_func_t callback; float* convbuffers[2]; int bufferByteSize; int bufferFrameSize; } fluid_sndmgr_audio_driver_t; fluid_audio_driver_t* new_fluid_sndmgr_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth); fluid_audio_driver_t* new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data); int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p); void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer); Fixed fluid_sndmgr_double_to_fix(long double theLD); /* * generic new : returns error */ int start_fluid_sndmgr_audio_driver(fluid_settings_t* settings, fluid_sndmgr_audio_driver_t* dev, int buffer_size) { int i; SndDoubleBufferHeader2* doubleHeader = NULL; SndDoubleBufferPtr doubleBuffer = NULL; OSErr err; SndChannelPtr channel = NULL; double sample_rate; fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); dev->doubleCallbackProc = NewSndDoubleBackProc(fluid_sndmgr_callback); /* the channel */ FLUID_LOG(FLUID_DBG, "FLUID-SndManager@2"); err = SndNewChannel(&channel, sampledSynth, initStereo, NULL); if ((err != noErr) || (channel == NULL)) { FLUID_LOG(FLUID_ERR, "Failed to allocate a sound channel (error %i)", err); return err; } /* the double buffer struct */ FLUID_LOG(FLUID_DBG, "FLUID-SndManager@3"); doubleHeader = FLUID_NEW(SndDoubleBufferHeader2); if (doubleHeader == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return -1; } doubleHeader->dbhBufferPtr[0] = NULL; doubleHeader->dbhBufferPtr[1] = NULL; doubleHeader->dbhNumChannels = 2; doubleHeader->dbhSampleSize = 16; doubleHeader->dbhCompressionID = 0; doubleHeader->dbhPacketSize = 0; doubleHeader->dbhSampleRate = fluid_sndmgr_double_to_fix((long double) sample_rate); doubleHeader->dbhDoubleBack = dev->doubleCallbackProc; doubleHeader->dbhFormat = 0; /* prepare dev */ FLUID_LOG(FLUID_DBG, "FLUID-SndManager@4"); dev->doubleHeader = doubleHeader; dev->channel = channel; dev->bufferFrameSize = buffer_size; dev->bufferByteSize = buffer_size * 2 * 2; /* the 2 doublebuffers */ FLUID_LOG(FLUID_DBG, "FLUID-SndManager@5"); for (i = 0; i < 2; i++) { doubleBuffer = (SndDoubleBufferPtr) FLUID_MALLOC(sizeof(SndDoubleBuffer) + dev->bufferByteSize); if (doubleBuffer == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return -1; } doubleBuffer->dbNumFrames = 0; doubleBuffer->dbFlags = 0; doubleBuffer->dbUserInfo[0] = (long) dev; doubleHeader->dbhBufferPtr[i] = doubleBuffer; CallSndDoubleBackProc(doubleHeader->dbhDoubleBack, channel, doubleBuffer); } /* start */ FLUID_LOG(FLUID_DBG, "FLUID-SndManager@6"); err = SndPlayDoubleBuffer(channel, (SndDoubleBufferHeader *)doubleHeader); if (err != noErr) { FLUID_LOG(FLUID_ERR, "Failed to start the sound driver (error %i)", err); return err; } FLUID_LOG(FLUID_DBG, "FLUID-SndManager@7"); return 0; } /* * new_fluid_sndmgr_audio_driver * This implementation used the 16bit format. */ fluid_audio_driver_t* new_fluid_sndmgr_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) { fluid_sndmgr_audio_driver_t* dev = NULL; int period_size, periods, buffer_size; /* check the format */ if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { FLUID_LOG(FLUID_ERR, "Unhandled sample format"); return NULL; } /* compute buffer size */ fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getint(settings, "audio.periods", &periods); buffer_size = period_size*periods; /* allocated dev */ dev = FLUID_NEW(fluid_sndmgr_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t)); dev->callback_is_audio_func = false; dev->data = (void *)synth; dev->callback = NULL; if (start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) { delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t*)dev); return NULL; } return (fluid_audio_driver_t*)dev; } /* * new_fluid_sndmgr_audio_driver2 * * This implementation used the audio_func float format, with * conversion from float to 16bits in the driver. */ fluid_audio_driver_t* new_fluid_sndmgr_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) { fluid_sndmgr_audio_driver_t* dev = NULL; int period_size, periods, buffer_size; /* compute buffer size */ fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getint(settings, "audio.periods", &periods); buffer_size = period_size*periods; /* allocated dev */ dev = FLUID_NEW(fluid_sndmgr_audio_driver_t); if (dev == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t)); /* allocate the conversion buffers */ dev->convbuffers[0] = FLUID_ARRAY(float, buffer_size); dev->convbuffers[1] = FLUID_ARRAY(float, buffer_size); if ((dev->convbuffers[0] == NULL) || (dev->convbuffers[1] == NULL)) { FLUID_LOG(FLUID_PANIC, "Out of memory"); goto error_recovery; } dev->callback_is_audio_func = true; dev->data = data; dev->callback = func; if (start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) { goto error_recovery; } return (fluid_audio_driver_t*)dev; error_recovery: delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t*)dev); return NULL; } /* * delete_fluid_sndmgr_audio_driver */ int delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t* p) { fluid_sndmgr_audio_driver_t* dev = (fluid_sndmgr_audio_driver_t*) p; if (dev != NULL) { if (dev->channel != NULL) { SndDisposeChannel(dev->channel, 1); } if (dev->doubleCallbackProc != NULL) { DisposeRoutineDescriptor(dev->doubleCallbackProc); } if (dev->doubleHeader != NULL) { if(dev->doubleHeader->dbhBufferPtr[0] != NULL) { FLUID_FREE(dev->doubleHeader->dbhBufferPtr[0]); } if (dev->doubleHeader->dbhBufferPtr[1] != NULL) { FLUID_FREE(dev->doubleHeader->dbhBufferPtr[1]); } FLUID_FREE(dev->doubleHeader); } if (dev->convbuffers[0] != NULL) { FLUID_FREE(dev->convbuffers[0]); } if (dev->convbuffers[1] != NULL) { FLUID_FREE(dev->convbuffers[1]); } FLUID_FREE(dev); } return 0; } /* * fluid_sndmgr_callback * */ void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer) { fluid_sndmgr_audio_driver_t* dev; signed short* buf; float* left; float* right; float v; int i, k, buffer_size; dev = (fluid_sndmgr_audio_driver_t*) doubleBuffer->dbUserInfo[0]; buf = (signed short*)doubleBuffer->dbSoundData; buffer_size = dev->bufferFrameSize; if (dev->callback_is_audio_func) { /* float API : conversion to signed short */ left = dev->convbuffers[0]; right = dev->convbuffers[1]; (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->convbuffers); for (i = 0, k = 0; i < buffer_size; i++) { v = 32767.0f * left[i]; fluid_clip(v, -32768.0f, 32767.0f); buf[k++] = (signed short) v; v = 32767.0f * right[i]; fluid_clip(v, -32768.0f, 32767.0f); buf[k++] = (signed short) v; } } else { /* let the synth do the convertion */ fluid_synth_write_s16((fluid_synth_t*)dev->data, buffer_size, buf, 0, 2, buf, 1, 2); } doubleBuffer->dbFlags = doubleBuffer->dbFlags | dbBufferReady; doubleBuffer->dbNumFrames = buffer_size; } /* * fluid_sndmgr_double_to_fix * * A Fixed number is of the type 12345.67890. It is 32 bits in size with the * high order bits representing the significant value (that before the point) * and the lower 16 bits representing the fractional part of the number. * The Sound Manager further complicates matters by using Fixed numbers, but * needing to represent numbers larger than what the Fixed is capable of. * To do this the Sound Manager treats the sign bit as having the value 32768 * which will cause any number greater or equal to 32768 to look like it is * negative. * This routine is designed to "do the right thing" and convert any long double * into the Fixed number it represents. * long double is the input type because AIFF files use extended80 numbers and * there are routines that will convert from an extended80 to a long double. * A long double has far greater precision than a Fixed, so any number whose * significant or fraction is larger than 65535 will not convert correctly. */ #define _MAX_VALUE 65535 #define _BITS_PER_BYTE 8 Fixed fluid_sndmgr_double_to_fix(long double theLD) { unsigned long theResult = 0; unsigned short theSignificant = 0, theFraction = 0; if (theLD < _MAX_VALUE) { theSignificant = theLD; theFraction = theLD - theSignificant; if (theFraction > _MAX_VALUE) { /* Won't be able to convert */ theSignificant = 0; theFraction = 0; } } theResult |= theSignificant; theResult = theResult << (sizeof (unsigned short) * _BITS_PER_BYTE); theResult |= theFraction; return theResult; } #endif fluidsynth-1.1.9/src/drivers/fluid_winmidi.c000066400000000000000000000256531322272076000211630ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* fluid_winmidi.c * * Driver for Windows MIDI * * NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called * from within the MIDI input callback, despite many examples contrary to that * on the Internet. Some MIDI devices will deadlock. Therefore we add MIDIHDR * pointers to a queue and re-add them in a separate thread, using a mutex and * conditional to wake up the thread. Lame-o API! :( */ #include "fluidsynth_priv.h" #if WINMIDI_SUPPORT #include "fluid_midi.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #define MIDI_SYSEX_MAX_SIZE 512 #define MIDI_SYSEX_BUF_COUNT 16 typedef struct { fluid_midi_driver_t driver; HMIDIIN hmidiin; int closing; /* Set to TRUE when closing driver, to prevent endless SYSEX lockup loop */ fluid_thread_t *sysExAddThread; /* Thread for SYSEX re-add thread */ fluid_cond_mutex_t *mutex; /* Lock for condition */ fluid_cond_t *cond; /* Condition to signal MIDI event thread of available events */ /* MIDI HDR for SYSEX buffer */ MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT]; /* TRUE for each MIDIHDR buffer which should be re-added to MIDI device */ int sysExHdrAdd[MIDI_SYSEX_BUF_COUNT]; /* Sysex data buffer */ unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE]; int sysExOffset; /* Current offset in sysex buffer (for message continuation) */ } 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) fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data); int delete_fluid_winmidi_driver(fluid_midi_driver_t* p); void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR msg, DWORD_PTR extra); static void fluid_winmidi_add_sysex_thread (void *data); static char* fluid_winmidi_input_error(int no); int fluid_winmidi_driver_status(fluid_midi_driver_t* p); void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings) { MMRESULT res; MIDIINCAPS in_caps; UINT i, num; fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0, NULL, NULL); num = midiInGetNumDevs(); if (num > 0) { fluid_settings_add_option(settings, "midi.winmidi.device", "default"); for (i = 0; i < num; i++) { res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS)); if (res == MMSYSERR_NOERROR) { fluid_settings_add_option(settings, "midi.winmidi.device", in_caps.szPname); } } } } /* * new_fluid_winmidi_driver */ fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* data) { fluid_winmidi_driver_t* dev; MIDIHDR *hdr; MMRESULT res; UINT i, err, num, midi_num = 0; MIDIINCAPS in_caps; char* devname = NULL; /* not much use doing anything */ if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Invalid argument"); return NULL; } dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t)); if (dev == NULL) { return NULL; } memset (dev, 0, sizeof (fluid_winmidi_driver_t)); dev->hmidiin = NULL; dev->driver.handler = handler; dev->driver.data = data; dev->closing = FALSE; /* get the device name. if none is specified, use the default device. */ if(!fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) || !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 (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 (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 */ err = midiInOpen(&dev->hmidiin, midi_num, (DWORD_PTR) fluid_winmidi_callback, (DWORD_PTR) dev, CALLBACK_FUNCTION); if (err != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)", fluid_winmidi_input_error(err), err); goto error_recovery; } /* Prepare and add SYSEX buffers */ for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++) { dev->sysExHdrAdd[i] = FALSE; hdr = &dev->sysExHdrs[i]; hdr->lpData = &dev->sysExBuf[i * MIDI_SYSEX_MAX_SIZE]; hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE; /* Prepare a buffer for SYSEX data and add it */ err = midiInPrepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR)); if (err == MMSYSERR_NOERROR) { err = midiInAddBuffer (dev->hmidiin, hdr, sizeof (MIDIHDR)); if (err != MMSYSERR_NOERROR) { FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", fluid_winmidi_input_error (err), err); midiInUnprepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR)); } } else FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", fluid_winmidi_input_error (err), err); } /* Start the MIDI input interface */ if (midiInStart(dev->hmidiin) != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Failed to start the MIDI input. MIDI input not available."); goto error_recovery; } /* Create mutex and condition */ dev->mutex = new_fluid_cond_mutex (); dev->cond = new_fluid_cond (); if (!dev->mutex || !dev->cond) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } /* Create thread which processes re-adding SYSEX buffers */ dev->sysExAddThread = new_fluid_thread ("winmidi-sysex", fluid_winmidi_add_sysex_thread, dev, 0, FALSE); if (!dev->sysExAddThread) { FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread"); 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; } /* * delete_fluid_winmidi_driver */ int delete_fluid_winmidi_driver(fluid_midi_driver_t* p) { fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t*) p; if (dev->hmidiin != NULL) { fluid_atomic_int_set (&dev->closing, TRUE); if (dev->sysExAddThread) { fluid_cond_mutex_lock (dev->mutex); /* ++ lock */ fluid_cond_signal (dev->cond); fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */ fluid_thread_join (dev->sysExAddThread); } midiInStop(dev->hmidiin); midiInReset(dev->hmidiin); midiInClose(dev->hmidiin); } if (dev->mutex) delete_fluid_cond_mutex (dev->mutex); if (dev->cond) delete_fluid_cond (dev->cond); FLUID_FREE(dev); return 0; } 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; int index; 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->closing) break; /* Prevent MIM_LONGDATA endless loop, don't re-add buffer if closing */ 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); } index = (pMidiHdr - dev->sysExHdrs) / sizeof (MIDIHDR); fluid_atomic_int_set (&dev->sysExHdrAdd[index], TRUE); fluid_cond_mutex_lock (dev->mutex); /* ++ lock */ fluid_cond_signal (dev->cond); fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */ break; case MIM_ERROR: break; case MIM_LONGERROR: break; case MIM_MOREDATA: break; } } /* Thread for re-adding SYSEX buffers */ static void fluid_winmidi_add_sysex_thread (void *data) { fluid_winmidi_driver_t *dev = data; int i; while (!fluid_atomic_int_get (&dev->closing)) { fluid_cond_mutex_lock (dev->mutex); /* ++ lock */ fluid_cond_wait (dev->cond, dev->mutex); fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */ for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++) { if (fluid_atomic_int_get (&dev->sysExHdrAdd[i])) { fluid_atomic_int_set (&dev->sysExHdrAdd[i], FALSE); midiInAddBuffer (dev->hmidiin, &dev->sysExHdrs[i], sizeof (MIDIHDR)); } } } } int fluid_winmidi_driver_status(fluid_midi_driver_t* p) { return 0; } static char* fluid_winmidi_input_error(int no) { midiInGetErrorText(no, fluid_winmidi_error_buffer, 256); return fluid_winmidi_error_buffer; } #endif /* WINMIDI_SUPPORT */ fluidsynth-1.1.9/src/fluid_dll.c000066400000000000000000000040101322272076000166000ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifdef WIN32 #include "fluidsynth_priv.h" #include "fluid_sys.h" static HINSTANCE fluid_hinstance = NULL; #ifndef FLUIDSYNTH_NOT_A_DLL BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { FLUID_LOG(FLUID_DBG, "DllMain"); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: fluid_set_hinstance((void*) hModule); break; } return TRUE; } #endif /** * Set the handle to the instance of the application on the Windows platform. * @param Application instance pointer * * The handle is needed to open DirectSound. * * @deprecated As of 1.1.9 DirectSound driver uses the desktop window handle, making this function redundant. */ void fluid_set_hinstance(void* hinstance) { if (fluid_hinstance == NULL) { fluid_hinstance = (HINSTANCE) hinstance; FLUID_LOG(FLUID_DBG, "DLL instance = %d", (int) fluid_hinstance); } } /** * Get the handle to the instance of the application on the Windows platform. * @return Application instance pointer or NULL if not set * * @deprecated As of 1.1.9 DirectSound driver uses the desktop window handle, making this function redundant. */ void* fluid_get_hinstance(void) { return (void*) fluid_hinstance; } fluidsynth-1.1.9/src/fluidsynth.c000066400000000000000000000620531322272076000170460ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "fluidsynth_priv.h" #if !defined(WIN32) && !defined(MACINTOSH) #define _GNU_SOURCE #endif #if defined(HAVE_GETOPT_H) #include #define GETOPT_SUPPORT 1 #endif #include "fluidsynth.h" #if defined(WIN32) && !defined(MINGW32) #include "config_win32.h" #endif #ifdef HAVE_SIGNAL_H #include "signal.h" #endif #include "fluid_lash.h" #ifndef WITH_MIDI #define WITH_MIDI 1 #endif void print_usage(void); void print_help(fluid_settings_t *settings); void print_welcome(void); #if !defined(MACINTOSH) static fluid_cmd_handler_t* newclient(void* data, char* addr); #endif /* * the globals */ fluid_cmd_handler_t* cmd_handler = NULL; int option_help = 0; /* set to 1 if "-o help" is specified */ /* Process a command line option -o setting=value, for example: -o synth.polyhony=16 */ void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg) { char* val; int hints; int ival; for (val = optarg; *val != '\0'; val++) { if (*val == '=') { *val++ = 0; break; } } /* did user request list of settings */ if (strcmp (optarg, "help") == 0) { option_help = 1; return; } if (strcmp (optarg, "") == 0) { fprintf (stderr, "Invalid -o option (name part is empty)\n"); return; } switch(fluid_settings_get_type(settings, optarg)){ case FLUID_NUM_TYPE: if (!fluid_settings_setnum (settings, optarg, atof (val))) { fprintf (stderr, "Failed to set floating point parameter '%s'\n", optarg); exit (1); } break; case FLUID_INT_TYPE: hints = fluid_settings_get_hints (settings, optarg); if (hints & FLUID_HINT_TOGGLED) { if (FLUID_STRCMP (val, "yes") == 0 || FLUID_STRCMP (val, "True") == 0 || FLUID_STRCMP (val, "TRUE") == 0 || FLUID_STRCMP (val, "true") == 0 || FLUID_STRCMP (val, "T") == 0) ival = 1; else ival = atoi (val); } else ival = atoi (val); if (!fluid_settings_setint (settings, optarg, ival)) { fprintf (stderr, "Failed to set integer parameter '%s'\n", optarg); exit (1); } break; case FLUID_STR_TYPE: if (!fluid_settings_setstr (settings, optarg, val)) { fprintf (stderr, "Failed to set string parameter '%s'\n", optarg); exit (1); } break; default: fprintf (stderr, "Setting parameter '%s' not found\n", optarg); exit (1); } } static void print_pretty_int (int i) { if (i == INT_MAX) printf ("MAXINT"); else if (i == INT_MIN) printf ("MININT"); else printf ("%d", i); } typedef struct { int count; /* Total count of options */ int curindex; /* Current index in options */ } OptionBag; /* Function to display each string option value */ static void settings_option_foreach_func (void *data, char *name, char *option) { OptionBag *bag = data; bag->curindex++; if (bag->curindex < bag->count) printf ("'%s',", option); else printf ("'%s'", option); } /* fluid_settings_foreach function for displaying option help "-o help" */ static void settings_foreach_func (void *data, char *name, int type) { fluid_settings_t *settings = (fluid_settings_t *)data; double dmin, dmax, ddef; int imin, imax, idef, hints; char *defstr; int count; OptionBag bag; switch (type) { case FLUID_NUM_TYPE: fluid_settings_getnum_range (settings, name, &dmin, &dmax); ddef = fluid_settings_getnum_default (settings, name); printf ("%-24s FLOAT [min=%0.3f, max=%0.3f, def=%0.3f]\n", name, dmin, dmax, ddef); break; case FLUID_INT_TYPE: fluid_settings_getint_range (settings, name, &imin, &imax); idef = fluid_settings_getint_default (settings, name); hints = fluid_settings_get_hints (settings, name); if (!(hints & FLUID_HINT_TOGGLED)) { printf ("%-24s INT [min=", name); print_pretty_int (imin); printf (", max="); print_pretty_int (imax); printf (", def="); print_pretty_int (idef); printf ("]\n"); } else printf ("%-24s BOOL [def=%s]\n", name, idef ? "True" : "False"); break; case FLUID_STR_TYPE: printf ("%-24s STR", name); defstr = fluid_settings_getstr_default (settings, name); count = fluid_settings_option_count (settings, name); if (defstr || count > 0) { if (defstr && count > 0) printf (" [def='%s' vals:", defstr); else if (defstr) printf (" [def='%s'", defstr); else printf (" [vals:"); if (count > 0) { bag.count = count; bag.curindex = 0; fluid_settings_foreach_option (settings, name, &bag, settings_option_foreach_func); } printf ("]\n"); } else printf ("\n"); break; case FLUID_SET_TYPE: printf ("%-24s SET\n", name); break; } } /* Output options for a setting string to stdout */ static void show_settings_str_options (fluid_settings_t *settings, char *name) { OptionBag bag; bag.count = fluid_settings_option_count (settings, name); bag.curindex = 0; fluid_settings_foreach_option (settings, name, &bag, settings_option_foreach_func); printf ("\n"); } static void fast_render_loop(fluid_settings_t* settings, fluid_synth_t* synth, fluid_player_t* player) { fluid_file_renderer_t* renderer; renderer = new_fluid_file_renderer (synth); if (!renderer) return; while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { if (fluid_file_renderer_process_block(renderer) != FLUID_OK) { break; } } delete_fluid_file_renderer(renderer); } #ifdef HAVE_SIGNAL_H /* * handle_signal */ void handle_signal(int sig_num) { } #endif /* * main */ int main(int argc, char** argv) { fluid_settings_t* settings; int arg1 = 1; char buf[512]; int c, i; int interactive = 1; int midi_in = 1; fluid_player_t* player = NULL; fluid_midi_router_t* router = NULL; //fluid_sequencer_t* sequencer = NULL; fluid_midi_driver_t* mdriver = NULL; fluid_audio_driver_t* adriver = NULL; fluid_synth_t* synth = NULL; #if !defined(MACINTOSH) fluid_server_t* server = NULL; #endif char* config_file = NULL; int audio_groups = 0; int audio_channels = 0; int with_server = 0; int dump = 0; int fast_render = 0; #ifdef LASH_ENABLED int connect_lash = 1; #endif char *optchars = "a:C:c:dE:f:F:G:g:hijK:L:lm:nO:o:p:R:r:sT:Vvz:"; #ifdef LASH_ENABLED int enabled_lash = 0; /* set to TRUE if lash gets enabled */ fluid_lash_args_t *lash_args; lash_args = fluid_lash_extract_args (&argc, &argv); #endif print_welcome (); settings = new_fluid_settings(); #ifdef GETOPT_SUPPORT /* pre section of GETOPT supported argument handling */ opterr = 0; while (1) { int option_index = 0; static struct option long_options[] = { {"audio-bufcount", 1, 0, 'c'}, {"audio-bufsize", 1, 0, 'z'}, {"audio-channels", 1, 0, 'L'}, {"audio-driver", 1, 0, 'a'}, {"audio-file-endian", 1, 0, 'E'}, {"audio-file-format", 1, 0, 'O'}, {"audio-file-type", 1, 0, 'T'}, {"audio-groups", 1, 0, 'G'}, {"chorus", 1, 0, 'C'}, {"connect-jack-outputs", 0, 0, 'j'}, {"disable-lash", 0, 0, 'l'}, {"dump", 0, 0, 'd'}, {"fast-render", 1, 0, 'F'}, {"gain", 1, 0, 'g'}, {"help", 0, 0, 'h'}, {"load-config", 1, 0, 'f'}, {"midi-channels", 1, 0, 'K'}, {"midi-driver", 1, 0, 'm'}, {"no-midi-in", 0, 0, 'n'}, {"no-shell", 0, 0, 'i'}, {"option", 1, 0, 'o'}, {"portname", 1, 0, 'p'}, {"reverb", 1, 0, 'R'}, {"sample-rate", 1, 0, 'r'}, {"server", 0, 0, 's'}, {"verbose", 0, 0, 'v'}, {"version", 0, 0, 'V'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, optchars, long_options, &option_index); if (c == -1) { break; } #else /* "pre" section to non getopt argument handling */ for (i = 1; i < argc; i++) { char *optarg; /* Skip non switch arguments (assume they are file names) */ if ((argv[i][0] != '-') || (argv[i][1] == '\0')) break; c = argv[i][1]; optarg = strchr (optchars, c); /* find the option character in optchars */ if (optarg && optarg[1] == ':') /* colon follows if switch argument expected */ { if (++i >= argc) { printf ("Option -%c requires an argument\n", c); print_usage(); exit(0); } else { optarg = argv[i]; if (optarg[0] == '-') { printf ("Expected argument to option -%c found switch instead\n", c); print_usage(); exit(0); } } } else optarg = ""; #endif switch (c) { #ifdef GETOPT_SUPPORT case 0: /* shouldn't normally happen, a long option's flag is set to NULL */ printf ("option %s", long_options[option_index].name); if (optarg) { printf (" with arg %s", optarg); } printf ("\n"); break; #endif case 'a': if (FLUID_STRCMP (optarg, "help") == 0) { printf ("-a options (audio driver):\n "); show_settings_str_options (settings, "audio.driver"); exit (0); } else fluid_settings_setstr(settings, "audio.driver", optarg); break; case 'C': if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.chorus.active", FALSE); } else { fluid_settings_setint(settings, "synth.chorus.active", TRUE); } break; case 'c': fluid_settings_setint(settings, "audio.periods", atoi(optarg)); break; case 'd': fluid_settings_setint(settings, "synth.dump", TRUE); dump = 1; break; case 'E': if (FLUID_STRCMP (optarg, "help") == 0) { printf ("-E options (audio file byte order):\n "); show_settings_str_options (settings, "audio.file.endian"); #if LIBSNDFILE_SUPPORT printf ("\nauto: Use audio file format's default endian byte order\n" "cpu: Use CPU native byte order\n"); #else printf ("\nNOTE: No libsndfile support!\n" "cpu: Use CPU native byte order\n"); #endif exit (0); } else fluid_settings_setstr(settings, "audio.file.endian", optarg); break; case 'f': config_file = optarg; break; case 'F': fluid_settings_setstr(settings, "audio.file.name", optarg); fast_render = 1; break; case 'G': audio_groups = atoi(optarg); break; case 'g': fluid_settings_setnum(settings, "synth.gain", atof(optarg)); break; case 'h': print_help(settings); break; case 'i': interactive = 0; break; case 'j': fluid_settings_setint(settings, "audio.jack.autoconnect", 1); break; case 'K': fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg)); break; case 'L': audio_channels = atoi(optarg); fluid_settings_setint(settings, "synth.audio-channels", audio_channels); break; case 'l': /* disable LASH */ #ifdef LASH_ENABLED connect_lash = 0; #endif break; case 'm': if (FLUID_STRCMP (optarg, "help") == 0) { printf ("-m options (MIDI driver):\n "); show_settings_str_options (settings, "midi.driver"); exit (0); } else fluid_settings_setstr(settings, "midi.driver", optarg); break; case 'n': midi_in = 0; break; case 'O': if (FLUID_STRCMP (optarg, "help") == 0) { printf ("-O options (audio file format):\n "); show_settings_str_options (settings, "audio.file.format"); #if LIBSNDFILE_SUPPORT printf ("\ns8, s16, s24, s32: Signed PCM audio of the given number of bits\n"); printf ("float, double: 32 bit and 64 bit floating point audio\n"); printf ("u8: Unsigned 8 bit audio\n"); #else printf ("\nNOTE: No libsndfile support!\n"); #endif exit (0); } else fluid_settings_setstr(settings, "audio.file.format", optarg); break; case 'o': process_o_cmd_line_option(settings, optarg); break; case 'p' : fluid_settings_setstr(settings, "midi.portname", optarg); break; case 'R': if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) { fluid_settings_setint(settings, "synth.reverb.active", FALSE); } else { fluid_settings_setint(settings, "synth.reverb.active", TRUE); } break; case 'r': fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg)); break; case 's': with_server = 1; break; case 'T': if (FLUID_STRCMP (optarg, "help") == 0) { printf ("-T options (audio file type):\n "); show_settings_str_options (settings, "audio.file.type"); #if LIBSNDFILE_SUPPORT printf ("\nauto: Determine type from file name extension, defaults to \"wav\"\n"); #else printf ("\nNOTE: No libsndfile support!\n"); #endif exit (0); } else fluid_settings_setstr(settings, "audio.file.type", optarg); break; case 'V': printf("FluidSynth %s\n", VERSION); exit (0); break; case 'v': fluid_settings_setint(settings, "synth.verbose", TRUE); break; case 'z': fluid_settings_setint(settings, "audio.period-size", atoi(optarg)); break; #ifdef GETOPT_SUPPORT case '?': printf ("Unknown option %c\n", optopt); print_usage(); exit(0); break; default: printf ("?? getopt returned character code 0%o ??\n", c); break; #else /* Non getopt default case */ default: printf ("Unknown switch '%c'\n", c); print_usage(); exit(0); break; #endif } /* end of switch statement */ } /* end of loop */ #ifdef GETOPT_SUPPORT arg1 = optind; #else arg1 = i; #endif /* option help requested? "-o help" */ if (option_help) { printf ("FluidSynth settings:\n"); fluid_settings_foreach (settings, settings, settings_foreach_func); exit (0); } #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); #endif #ifdef LASH_ENABLED /* connect to the lash server */ if (connect_lash) { enabled_lash = fluid_lash_connect (lash_args); fluid_settings_setint (settings, "lash.enable", enabled_lash ? 1 : 0); } #endif /* The 'groups' setting is relevant for LADSPA operation and channel mapping * in rvoice_mixer. * If not given, set number groups to number of audio channels, because * they are the same (there is nothing between synth output and 'sound card') */ if ((audio_groups == 0) && (audio_channels != 0)) { audio_groups = audio_channels; } if (audio_groups != 0) { fluid_settings_setint(settings, "synth.audio-groups", audio_groups); } if (fast_render) { midi_in = 0; interactive = 0; with_server = 0; fluid_settings_setstr(settings, "player.timing-source", "sample"); fluid_settings_setint(settings, "synth.lock-memory", 0); fluid_settings_setint(settings, "synth.parallel-render", 1); /* TODO: Fast_render should not need this, but currently do */ } /* create the synthesizer */ synth = new_fluid_synth(settings); if (synth == NULL) { fprintf(stderr, "Failed to create the synthesizer\n"); exit(-1); } cmd_handler = new_fluid_cmd_handler(synth); if (cmd_handler == NULL) { fprintf(stderr, "Failed to create the command handler\n"); goto cleanup; } /* load the soundfonts (check that all non options are SoundFont or MIDI files) */ for (i = arg1; i < argc; i++) { if (fluid_is_soundfont(argv[i])) { if (fluid_synth_sfload(synth, argv[i], 1) == -1) fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]); } else if (!fluid_is_midifile(argv[i])) fprintf (stderr, "Parameter '%s' not a SoundFont or MIDI file or error occurred identifying it.\n", argv[i]); } #ifdef HAVE_SIGNAL_H /* signal(SIGINT, handle_signal); */ #endif /* start the synthesis thread */ if (!fast_render) { adriver = new_fluid_audio_driver(settings, synth); if (adriver == NULL) { fprintf(stderr, "Failed to create the audio driver\n"); goto cleanup; } } /* start the midi router and link it to the synth */ #if WITH_MIDI if (midi_in) { /* In dump mode, text output is generated for events going into and out of the router. * The example dump functions are put into the chain before and after the router.. */ //sequencer = new_fluid_sequencer2(0); router = new_fluid_midi_router( settings, dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event, (void*)synth); if (router == NULL) { fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n" "will be available. You can access the synthesizer \n" "through the console.\n"); } else { fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */ // fluid_sequencer_register_fluidsynth(sequencer, synth); mdriver = new_fluid_midi_driver( settings, dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event, (void*) router); if (mdriver == NULL) { fprintf(stderr, "Failed to create the MIDI thread; no MIDI input\n" "will be available. You can access the synthesizer \n" "through the console.\n"); } } } #endif /* run commands specified in config file */ if (config_file != NULL) { fluid_source(cmd_handler, config_file); } else if (fluid_get_userconf(buf, 512) != NULL) { fluid_source(cmd_handler, buf); } else if (fluid_get_sysconf(buf, 512) != NULL) { fluid_source(cmd_handler, buf); } /* play the midi files, if any */ for (i = arg1; i < argc; i++) { if ((argv[i][0] != '-') && fluid_is_midifile(argv[i])) { if (player == NULL) { player = new_fluid_player(synth); if (player == NULL) { fprintf(stderr, "Failed to create the midifile player.\n" "Continuing without a player.\n"); break; } } fluid_player_add(player, argv[i]); } } if (player != NULL) { if (fluid_synth_get_sfont(synth, 0) == NULL) { /* Try to load the default soundfont if no soundfont specified */ char *s; if (fluid_settings_getstr(settings, "synth.default-soundfont", &s) <= 0) s = NULL; if ((s != NULL) && (s[0] != '\0')) fluid_synth_sfload(synth, s, 1); } fluid_player_play(player); } /* run the server, if requested */ #if !defined(MACINTOSH) if (with_server) { server = new_fluid_server(settings, newclient, synth); if (server == NULL) { fprintf(stderr, "Failed to create the server.\n" "Continuing without it.\n"); } } #endif #ifdef LASH_ENABLED if (enabled_lash) fluid_lash_create_thread (synth); #endif /* run the shell */ if (interactive) { printf ("Type 'help' for help topics.\n\n"); /* In dump mode we set the prompt to "". The UI cannot easily * handle lines, which don't end with CR. Changing the prompt * cannot be done through a command, because the current shell * does not handle empty arguments. The ordinary case is dump == * 0. */ fluid_settings_setstr(settings, "shell.prompt", dump ? "" : "> "); fluid_usershell(settings, cmd_handler); } if (fast_render) { char *filename; if (player == NULL) { fprintf(stderr, "No midi file specified!\n"); goto cleanup; } fluid_settings_dupstr (settings, "audio.file.name", &filename); printf ("Rendering audio to file '%s'..\n", filename); if (filename) FLUID_FREE (filename); fast_render_loop(settings, synth, player); } cleanup: #if !defined(MACINTOSH) if (server != NULL) { /* if the user typed 'quit' in the shell, kill the server */ if (!interactive) { fluid_server_join(server); } delete_fluid_server(server); } #endif if (cmd_handler != NULL) { delete_fluid_cmd_handler(cmd_handler); } if (player != NULL) { /* if the user typed 'quit' in the shell, stop the player */ if (interactive) { fluid_player_stop(player); } if (adriver != NULL || !fluid_settings_str_equal(settings, "player.timing-source", "sample")) { /* if no audio driver and sample timers are used, nothing makes the player advance */ fluid_player_join(player); } delete_fluid_player(player); } if (router) { #if WITH_MIDI if (mdriver) { delete_fluid_midi_driver(mdriver); } delete_fluid_midi_router(router); #endif } /*if (sequencer) { delete_fluid_sequencer(sequencer); }*/ if (adriver) { delete_fluid_audio_driver(adriver); } if (synth) { delete_fluid_synth(synth); } if (settings) { delete_fluid_settings(settings); } return 0; } #if !defined(MACINTOSH) static fluid_cmd_handler_t* newclient(void* data, char* addr) { fluid_synth_t* synth = (fluid_synth_t*) data; return new_fluid_cmd_handler(synth); } #endif /* * print_usage */ void print_usage() { fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n"); fprintf(stderr, "Try -h for help.\n"); exit(0); } void print_welcome() { printf("FluidSynth version %s\n" "Copyright (C) 2000-2018 Peter Hanappe and others.\n" "Distributed under the LGPL license.\n" "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n", FLUIDSYNTH_VERSION); } /* * print_help */ void print_help (fluid_settings_t *settings) { char *audio_options; char *midi_options; audio_options = fluid_settings_option_concat (settings, "audio.driver", NULL); midi_options = fluid_settings_option_concat (settings, "midi.driver", NULL); printf("Usage: \n"); printf(" fluidsynth [options] [soundfonts] [midifiles]\n"); printf("Possible options:\n"); printf(" -a, --audio-driver=[label]\n" " The name of the audio driver to use.\n" " Valid values: %s\n", audio_options ? audio_options : "ERROR"); printf(" -c, --audio-bufcount=[count]\n" " Number of audio buffers\n"); printf(" -C, --chorus\n" " Turn the chorus on or off [0|1|yes|no, default = on]\n"); printf(" -d, --dump\n" " Dump incoming and outgoing MIDI events to stdout\n"); printf(" -E, --audio-file-endian\n" " Audio file endian for fast rendering or aufile driver (\"help\" for list)\n"); printf(" -f, --load-config\n" " Load command configuration file (shell commands)\n"); printf(" -F, --fast-render=[file]\n" " Render MIDI file to raw audio data and store in [file]\n"); printf(" -g, --gain\n" " Set the master gain [0 < gain < 10, default = 0.2]\n"); printf(" -G, --audio-groups\n" " Defines the number of LADSPA audio nodes\n"); printf(" -h, --help\n" " Print out this help summary\n"); printf(" -i, --no-shell\n" " Don't read commands from the shell [default = yes]\n"); printf(" -j, --connect-jack-outputs\n" " Attempt to connect the jack outputs to the physical ports\n"); printf(" -K, --midi-channels=[num]\n" " The number of midi channels [default = 16]\n"); #ifdef LASH_ENABLED printf(" -l, --disable-lash\n" " Don't connect to LASH server\n"); #endif printf(" -L, --audio-channels=[num]\n" " The number of stereo audio channels [default = 1]\n"); printf(" -m, --midi-driver=[label]\n" " The name of the midi driver to use.\n" " Valid values: %s\n", midi_options ? midi_options : "ERROR"); printf(" -n, --no-midi-in\n" " Don't create a midi driver to read MIDI input events [default = yes]\n"); printf(" -o\n" " Define a setting, -o name=value (\"-o help\" to dump current values)\n"); printf(" -O, --audio-file-format\n" " Audio file format for fast rendering or aufile driver (\"help\" for list)\n"); printf(" -p, --portname=[label]\n" " Set MIDI port name (alsa_seq, coremidi drivers)\n"); printf(" -r, --sample-rate\n" " Set the sample rate\n"); printf(" -R, --reverb\n" " Turn the reverb on or off [0|1|yes|no, default = on]\n"); printf(" -s, --server\n" " Start FluidSynth as a server process\n"); printf(" -T, --audio-file-type\n" " Audio file type for fast rendering or aufile driver (\"help\" for list)\n"); printf(" -v, --verbose\n" " Print out verbose messages about midi events\n"); printf(" -V, --version\n" " Show version of program\n"); printf(" -z, --audio-bufsize=[size]\n" " Size of each audio buffer\n"); if (audio_options) FLUID_FREE (audio_options); if (midi_options) FLUID_FREE (midi_options); delete_fluid_settings (settings); exit(0); } fluidsynth-1.1.9/src/midi/000077500000000000000000000000001322272076000154255ustar00rootroot00000000000000fluidsynth-1.1.9/src/midi/fluid_midi.c000066400000000000000000001462161322272076000177100ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_midi.h" #include "fluid_sys.h" #include "fluid_synth.h" #include "fluid_settings.h" static int fluid_midi_event_length(unsigned char event); /* Read the entire contents of a file into memory, allocating enough memory * for the file, and returning the length and the buffer. * Note: This rewinds the file to the start before reading. * Returns NULL if there was an error reading or allocating memory. */ static char* fluid_file_read_full(fluid_file fp, size_t* length); #define READ_FULL_INITIAL_BUFLEN 1024 /*************************************************************** * * MIDIFILE */ /** * Return a new MIDI file handle for parsing an already-loaded MIDI file. * @internal * @param buffer Pointer to full contents of MIDI file (borrows the pointer). * The caller must not free buffer until after the fluid_midi_file is deleted. * @param length Size of the buffer in bytes. * @return New MIDI file handle or NULL on error. */ fluid_midi_file * new_fluid_midi_file(const char* buffer, size_t length) { fluid_midi_file *mf; mf = FLUID_NEW(fluid_midi_file); if (mf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); mf->c = -1; mf->running_status = -1; mf->buffer = buffer; mf->buf_len = length; mf->buf_pos = 0; mf->eof = FALSE; if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { FLUID_FREE(mf); return NULL; } return mf; } static char* fluid_file_read_full(fluid_file fp, size_t* length) { size_t buflen; char* buffer; size_t n; /* Work out the length of the file in advance */ if (FLUID_FSEEK(fp, 0, SEEK_END) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } buflen = ftell(fp); if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen); buffer = FLUID_MALLOC(buflen); if (buffer == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } n = FLUID_FREAD(buffer, 1, buflen, fp); if (n != buflen) { FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n, buflen); FLUID_FREE(buffer); return NULL; }; *length = n; return buffer; } /** * Delete a MIDI file handle. * @internal * @param mf MIDI file handle to close and free. */ void delete_fluid_midi_file (fluid_midi_file *mf) { if (mf == NULL) { return; } FLUID_FREE(mf); return; } /* * Gets the next byte in a MIDI file, taking into account previous running status. * * returns FLUID_FAILED if EOF or read error */ int fluid_midi_file_getc (fluid_midi_file *mf) { unsigned char c; if (mf->c >= 0) { c = mf->c; mf->c = -1; } else { if (mf->buf_pos >= mf->buf_len) { mf->eof = TRUE; return FLUID_FAILED; } c = mf->buffer[mf->buf_pos++]; mf->trackpos++; } return (int) c; } /* * Saves a byte to be returned the next time fluid_midi_file_getc() is called, * when it is necessary according to running status. */ int fluid_midi_file_push(fluid_midi_file *mf, int c) { mf->c = c; return FLUID_OK; } /* * fluid_midi_file_read */ int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) { int num = len < mf->buf_len - mf->buf_pos ? len : mf->buf_len - mf->buf_pos; if (num != len) { mf->eof = TRUE; } if (num < 0) { num = 0; } /* Note: Read bytes, even if there aren't enough, but only increment * trackpos if successful (emulates old behaviour of fluid_midi_file_read) */ FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num); mf->buf_pos += num; if (num == len) mf->trackpos += num; #if DEBUG else FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes"); #endif return (num != len) ? FLUID_FAILED : FLUID_OK; } /* * fluid_midi_file_skip */ int fluid_midi_file_skip (fluid_midi_file *mf, int skip) { int new_pos = mf->buf_pos + skip; /* Mimic the behaviour of fseek: Error to seek past the start of file, but * OK to seek past end (this just puts it into the EOF state). */ if (new_pos < 0) { FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); return FLUID_FAILED; } /* Clear the EOF flag, even if moved past the end of the file (this is * consistent with the behaviour of fseek). */ mf->eof = FALSE; mf->buf_pos = new_pos; return FLUID_OK; } /* * fluid_midi_file_eof */ int fluid_midi_file_eof(fluid_midi_file* mf) { /* Note: This does not simply test whether the file read pointer is past * the end of the file. It mimics the behaviour of feof by actually * testing the stateful EOF condition, which is set to TRUE if getc or * fread have attempted to read past the end (but not if they have * precisely reached the end), but reset to FALSE upon a successful seek. */ return mf->eof; } /* * fluid_midi_file_read_mthd */ int fluid_midi_file_read_mthd(fluid_midi_file *mf) { char mthd[14]; if (fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK) { return FLUID_FAILED; } if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) || (mthd[9] > 2)) { FLUID_LOG(FLUID_ERR, "Doesn't look like a MIDI file: invalid MThd header"); return FLUID_FAILED; } mf->type = mthd[9]; mf->ntracks = (unsigned) mthd[11]; mf->ntracks += (unsigned int) (mthd[10]) << 16; if ((signed char)mthd[12] < 0) { mf->uses_smpte = 1; mf->smpte_fps = -(signed char)mthd[12]; mf->smpte_res = (unsigned) mthd[13]; FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); return FLUID_FAILED; } else { mf->uses_smpte = 0; mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff); FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); } return FLUID_OK; } /* * fluid_midi_file_load_tracks */ int fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) { int i; for (i = 0; i < mf->ntracks; i++) { if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) { return FLUID_FAILED; } } return FLUID_OK; } /* * fluid_isasciistring */ int fluid_isasciistring(char *s) { int i; int len = (int) FLUID_STRLEN(s); for (i = 0; i < len; i++) { if (!fluid_isascii(s[i])) { return 0; } } return 1; } /* * fluid_getlength */ long fluid_getlength(unsigned char *s) { long i = 0; i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24); return i; } /* * fluid_midi_file_read_tracklen */ int fluid_midi_file_read_tracklen(fluid_midi_file *mf) { unsigned char length[5]; if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { return FLUID_FAILED; } mf->tracklen = fluid_getlength(length); mf->trackpos = 0; mf->eot = 0; return FLUID_OK; } /* * fluid_midi_file_eot */ int fluid_midi_file_eot(fluid_midi_file *mf) { #if DEBUG if (mf->trackpos > mf->tracklen) { printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); } #endif return mf->eot || (mf->trackpos >= mf->tracklen); } /* * fluid_midi_file_read_track */ int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) { fluid_track_t *track; unsigned char id[5], length[5]; int found_track = 0; int skip; if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) { return FLUID_FAILED; } id[4] = '\0'; mf->dtime = 0; while (!found_track) { if (fluid_isasciistring((char *) id) == 0) { FLUID_LOG(FLUID_ERR, "An non-ascii track header found, corrupt file"); return FLUID_FAILED; } else if (strcmp((char *) id, "MTrk") == 0) { found_track = 1; if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) { return FLUID_FAILED; } track = new_fluid_track(num); if (track == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } while (!fluid_midi_file_eot(mf)) { if (fluid_midi_file_read_event(mf, track) != FLUID_OK) { delete_fluid_track(track); return FLUID_FAILED; } } /* Skip remaining track data, if any */ if (mf->trackpos < mf->tracklen) { if (fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) { delete_fluid_track(track); return FLUID_FAILED; } } if (fluid_player_add_track(player, track) != FLUID_OK) { delete_fluid_track(track); return FLUID_FAILED; } } else { found_track = 0; if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { return FLUID_FAILED; } skip = fluid_getlength(length); /* fseek(mf->fp, skip, SEEK_CUR); */ if (fluid_midi_file_skip(mf, skip) != FLUID_OK) { return FLUID_FAILED; } } } if (fluid_midi_file_eof(mf)) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } return FLUID_OK; } /* * fluid_midi_file_read_varlen */ int fluid_midi_file_read_varlen(fluid_midi_file *mf) { int i; int c; mf->varlen = 0; for (i = 0;; i++) { if (i == 4) { FLUID_LOG(FLUID_ERR, "Invalid variable length number"); return FLUID_FAILED; } c = fluid_midi_file_getc(mf); if (c < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } if (c & 0x80) { mf->varlen |= (int) (c & 0x7F); mf->varlen <<= 7; } else { mf->varlen += c; break; } } return FLUID_OK; } /* * fluid_midi_file_read_event */ int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) { int status; int type; int tempo; unsigned char *metadata = NULL; unsigned char *dyn_buf = NULL; unsigned char static_buf[256]; int nominator, denominator, clocks, notes; fluid_midi_event_t *evt; int channel = 0; int param1 = 0; int param2 = 0; int size; /* read the delta-time of the event */ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { return FLUID_FAILED; } mf->dtime += mf->varlen; /* read the status byte */ status = fluid_midi_file_getc(mf); if (status < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } /* not a valid status byte: use the running status instead */ if ((status & 0x80) == 0) { if ((mf->running_status & 0x80) == 0) { FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); return FLUID_FAILED; } fluid_midi_file_push(mf, status); status = mf->running_status; } /* check what message we have */ mf->running_status = status; if (status == MIDI_SYSEX) { /* system exclusif */ /* read the length of the message */ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { return FLUID_FAILED; } if (mf->varlen) { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); metadata = FLUID_MALLOC(mf->varlen + 1); if (metadata == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } /* read the data of the message */ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { FLUID_FREE (metadata); return FLUID_FAILED; } evt = new_fluid_midi_event(); if (evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE (metadata); return FLUID_FAILED; } evt->dtime = mf->dtime; size = mf->varlen; if (metadata[mf->varlen - 1] == MIDI_EOX) size--; /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */ fluid_midi_event_set_sysex(evt, metadata, size, TRUE); fluid_track_add_event(track, evt); mf->dtime = 0; } return FLUID_OK; } else if (status == MIDI_META_EVENT) { /* meta events */ int result = FLUID_OK; /* get the type of the meta message */ type = fluid_midi_file_getc(mf); if (type < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } /* get the length of the data part */ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { return FLUID_FAILED; } if (mf->varlen < 255) { metadata = &static_buf[0]; } else { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); dyn_buf = FLUID_MALLOC(mf->varlen + 1); if (dyn_buf == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } metadata = dyn_buf; } /* read the data */ if (mf->varlen) { if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { if (dyn_buf) { FLUID_FREE(dyn_buf); } return FLUID_FAILED; } } /* handle meta data */ switch (type) { case MIDI_COPYRIGHT: metadata[mf->varlen] = 0; break; case MIDI_TRACK_NAME: metadata[mf->varlen] = 0; fluid_track_set_name(track, (char *) metadata); break; case MIDI_INST_NAME: metadata[mf->varlen] = 0; break; case MIDI_LYRIC: break; case MIDI_MARKER: break; case MIDI_CUE_POINT: break; /* don't care much for text events */ case MIDI_EOT: if (mf->varlen != 0) { FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); result = FLUID_FAILED; break; } mf->eot = 1; evt = new_fluid_midi_event(); if (evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); result = FLUID_FAILED; break; } evt->dtime = mf->dtime; evt->type = MIDI_EOT; fluid_track_add_event(track, evt); mf->dtime = 0; break; case MIDI_SET_TEMPO: if (mf->varlen != 3) { FLUID_LOG(FLUID_ERR, "Invalid length for SetTempo meta event"); result = FLUID_FAILED; break; } tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; evt = new_fluid_midi_event(); if (evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); result = FLUID_FAILED; break; } evt->dtime = mf->dtime; evt->type = MIDI_SET_TEMPO; evt->channel = 0; evt->param1 = tempo; evt->param2 = 0; fluid_track_add_event(track, evt); mf->dtime = 0; break; case MIDI_SMPTE_OFFSET: if (mf->varlen != 5) { FLUID_LOG(FLUID_ERR, "Invalid length for SMPTE Offset meta event"); result = FLUID_FAILED; break; } break; /* we don't use smtp */ case MIDI_TIME_SIGNATURE: if (mf->varlen != 4) { FLUID_LOG(FLUID_ERR, "Invalid length for TimeSignature meta event"); result = FLUID_FAILED; break; } nominator = metadata[0]; denominator = pow(2.0, (double) metadata[1]); clocks = metadata[2]; notes = metadata[3]; FLUID_LOG(FLUID_DBG, "signature=%d/%d, metronome=%d, 32nd-notes=%d", nominator, denominator, clocks, notes); break; case MIDI_KEY_SIGNATURE: if (mf->varlen != 2) { FLUID_LOG(FLUID_ERR, "Invalid length for KeySignature meta event"); result = FLUID_FAILED; break; } /* We don't care about key signatures anyway */ /* sf = metadata[0]; mi = metadata[1]; */ break; case MIDI_SEQUENCER_EVENT: break; default: break; } if (dyn_buf) { FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); FLUID_FREE(dyn_buf); } return result; } else { /* channel messages */ type = status & 0xf0; channel = status & 0x0f; /* all channel message have at least 1 byte of associated data */ if ((param1 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } switch (type) { case NOTE_ON: if ((param2 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } break; case NOTE_OFF: if ((param2 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } break; case KEY_PRESSURE: if ((param2 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } break; case CONTROL_CHANGE: if ((param2 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } break; case PROGRAM_CHANGE: break; case CHANNEL_PRESSURE: break; case PITCH_BEND: if ((param2 = fluid_midi_file_getc(mf)) < 0) { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); param2 = 0; break; default: /* Can't possibly happen !? */ FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); return FLUID_FAILED; } evt = new_fluid_midi_event(); if (evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } evt->dtime = mf->dtime; evt->type = type; evt->channel = channel; evt->param1 = param1; evt->param2 = param2; fluid_track_add_event(track, evt); mf->dtime = 0; } return FLUID_OK; } /* * fluid_midi_file_get_division */ int fluid_midi_file_get_division(fluid_midi_file *midifile) { return midifile->division; } /****************************************************** * * fluid_track_t */ /** * Create a MIDI event structure. * @return New MIDI event structure or NULL when out of memory. */ fluid_midi_event_t * new_fluid_midi_event () { fluid_midi_event_t* evt; evt = FLUID_NEW(fluid_midi_event_t); if (evt == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } evt->dtime = 0; evt->type = 0; evt->channel = 0; evt->param1 = 0; evt->param2 = 0; evt->next = NULL; evt->paramptr = NULL; return evt; } /** * Delete MIDI event structure. * @param evt MIDI event structure * @return Always returns #FLUID_OK */ int delete_fluid_midi_event(fluid_midi_event_t *evt) { fluid_midi_event_t *temp; while (evt) { temp = evt->next; /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2) FLUID_FREE (evt->paramptr); FLUID_FREE(evt); evt = temp; } return FLUID_OK; } /** * Get the event type field of a MIDI event structure. * @param evt MIDI event structure * @return Event type field (MIDI status byte without channel) */ int fluid_midi_event_get_type(fluid_midi_event_t *evt) { return evt->type; } /** * Set the event type field of a MIDI event structure. * @param evt MIDI event structure * @param type Event type field (MIDI status byte without channel) * @return Always returns #FLUID_OK */ int fluid_midi_event_set_type(fluid_midi_event_t *evt, int type) { evt->type = type; return FLUID_OK; } /** * Get the channel field of a MIDI event structure. * @param evt MIDI event structure * @return Channel field */ int fluid_midi_event_get_channel(fluid_midi_event_t *evt) { return evt->channel; } /** * Set the channel field of a MIDI event structure. * @param evt MIDI event structure * @param chan MIDI channel field * @return Always returns #FLUID_OK */ int fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan) { evt->channel = chan; return FLUID_OK; } /** * Get the key field of a MIDI event structure. * @param evt MIDI event structure * @return MIDI note number (0-127) */ int fluid_midi_event_get_key(fluid_midi_event_t *evt) { return evt->param1; } /** * Set the key field of a MIDI event structure. * @param evt MIDI event structure * @param v MIDI note number (0-127) * @return Always returns #FLUID_OK */ int fluid_midi_event_set_key(fluid_midi_event_t *evt, int v) { evt->param1 = v; return FLUID_OK; } /** * Get the velocity field of a MIDI event structure. * @param evt MIDI event structure * @return MIDI velocity number (0-127) */ int fluid_midi_event_get_velocity(fluid_midi_event_t *evt) { return evt->param2; } /** * Set the velocity field of a MIDI event structure. * @param evt MIDI event structure * @param v MIDI velocity value * @return Always returns #FLUID_OK */ int fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v) { evt->param2 = v; return FLUID_OK; } /** * Get the control number of a MIDI event structure. * @param evt MIDI event structure * @return MIDI control number */ int fluid_midi_event_get_control(fluid_midi_event_t *evt) { return evt->param1; } /** * Set the control field of a MIDI event structure. * @param evt MIDI event structure * @param v MIDI control number * @return Always returns #FLUID_OK */ int fluid_midi_event_set_control(fluid_midi_event_t *evt, int v) { evt->param1 = v; return FLUID_OK; } /** * Get the value field from a MIDI event structure. * @param evt MIDI event structure * @return Value field */ int fluid_midi_event_get_value(fluid_midi_event_t *evt) { return evt->param2; } /** * Set the value field of a MIDI event structure. * @param evt MIDI event structure * @param v Value to assign * @return Always returns #FLUID_OK */ int fluid_midi_event_set_value(fluid_midi_event_t *evt, int v) { evt->param2 = v; return FLUID_OK; } /** * Get the program field of a MIDI event structure. * @param evt MIDI event structure * @return MIDI program number (0-127) */ int fluid_midi_event_get_program(fluid_midi_event_t *evt) { return evt->param1; } /** * Set the program field of a MIDI event structure. * @param evt MIDI event structure * @param val MIDI program number (0-127) * @return Always returns #FLUID_OK */ int fluid_midi_event_set_program(fluid_midi_event_t *evt, int val) { evt->param1 = val; return FLUID_OK; } /** * Get the pitch field of a MIDI event structure. * @param evt MIDI event structure * @return Pitch value (14 bit value, 0-16383, 8192 is center) */ int fluid_midi_event_get_pitch(fluid_midi_event_t *evt) { return evt->param1; } /** * Set the pitch field of a MIDI event structure. * @param evt MIDI event structure * @param val Pitch value (14 bit value, 0-16383, 8192 is center) * @return Always returns FLUID_OK */ int fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val) { evt->param1 = val; return FLUID_OK; } /** * Assign sysex data to a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to SYSEX data * @param size Size of SYSEX data * @param dynamic TRUE if the SYSEX data has been dynamically allocated and * 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) { evt->type = MIDI_SYSEX; evt->paramptr = data; evt->param1 = size; evt->param2 = dynamic; return FLUID_OK; } /****************************************************** * * fluid_track_t */ /* * new_fluid_track */ fluid_track_t * new_fluid_track(int num) { fluid_track_t *track; track = FLUID_NEW(fluid_track_t); if (track == NULL) { return NULL; } track->name = NULL; track->num = num; track->first = NULL; track->cur = NULL; track->last = NULL; track->ticks = 0; return track; } /* * delete_fluid_track */ int delete_fluid_track(fluid_track_t *track) { if (track->name != NULL) { FLUID_FREE(track->name); } if (track->first != NULL) { delete_fluid_midi_event(track->first); } FLUID_FREE(track); return FLUID_OK; } /* * fluid_track_set_name */ int fluid_track_set_name(fluid_track_t *track, char *name) { int len; if (track->name != NULL) { FLUID_FREE(track->name); } if (name == NULL) { track->name = NULL; return FLUID_OK; } len = FLUID_STRLEN(name); track->name = FLUID_MALLOC(len + 1); if (track->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } FLUID_STRCPY(track->name, name); return FLUID_OK; } /* * fluid_track_get_name */ char * fluid_track_get_name(fluid_track_t *track) { return track->name; } /* * fluid_track_get_duration */ int fluid_track_get_duration(fluid_track_t *track) { int time = 0; fluid_midi_event_t *evt = track->first; while (evt != NULL) { time += evt->dtime; evt = evt->next; } return time; } /* * fluid_track_count_events */ int fluid_track_count_events(fluid_track_t *track, int *on, int *off) { fluid_midi_event_t *evt = track->first; while (evt != NULL) { if (evt->type == NOTE_ON) { (*on)++; } else if (evt->type == NOTE_OFF) { (*off)++; } evt = evt->next; } return FLUID_OK; } /* * fluid_track_add_event */ int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt) { evt->next = NULL; if (track->first == NULL) { track->first = evt; track->cur = evt; track->last = evt; } else { track->last->next = evt; track->last = evt; } return FLUID_OK; } /* * fluid_track_first_event */ fluid_midi_event_t * fluid_track_first_event(fluid_track_t *track) { track->cur = track->first; return track->cur; } /* * fluid_track_next_event */ fluid_midi_event_t * fluid_track_next_event(fluid_track_t *track) { if (track->cur != NULL) { track->cur = track->cur->next; } return track->cur; } /* * fluid_track_reset */ int fluid_track_reset(fluid_track_t *track) { track->ticks = 0; track->cur = track->first; return FLUID_OK; } /* * fluid_track_send_events */ int fluid_track_send_events(fluid_track_t *track, fluid_synth_t *synth, fluid_player_t *player, unsigned int ticks) { int status = FLUID_OK; fluid_midi_event_t *event; while (1) { event = track->cur; if (event == NULL) { return status; } /* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */ /* track->num, */ /* ticks, */ /* track->ticks, */ /* event->dtime, */ /* track->ticks + event->dtime); */ if (track->ticks + event->dtime > ticks) { return status; } track->ticks += event->dtime; if (!player || event->type == MIDI_EOT) { } else if (event->type == MIDI_SET_TEMPO) { fluid_player_set_midi_tempo(player, event->param1); } else { if (player->playback_callback) player->playback_callback(player->playback_userdata, event); } fluid_track_next_event(track); } return status; } /****************************************************** * * fluid_player */ /** * Create a new MIDI player. * @param synth Fluid synthesizer instance to create player for * @return New MIDI player instance or NULL on error (out of memory) */ fluid_player_t * new_fluid_player(fluid_synth_t *synth) { int i; fluid_player_t *player; player = FLUID_NEW(fluid_player_t); if (player == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } player->status = FLUID_PLAYER_READY; player->loop = 1; player->ntracks = 0; for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { player->track[i] = NULL; } player->synth = synth; player->system_timer = NULL; player->sample_timer = NULL; player->playlist = NULL; player->currentfile = NULL; player->division = 0; player->send_program_change = 1; player->miditempo = 480000; player->deltatime = 4.0; player->cur_msec = 0; player->cur_ticks = 0; fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); player->use_system_timer = fluid_settings_str_equal(synth->settings, "player.timing-source", "system"); fluid_settings_getint(synth->settings, "player.reset-synth", &i); player->reset_synth_between_songs = i; return player; } /** * Delete a MIDI player instance. * @param player MIDI player instance * @return Always returns #FLUID_OK */ int delete_fluid_player(fluid_player_t *player) { fluid_list_t *q; fluid_playlist_item* pi; if (player == NULL) { return FLUID_OK; } fluid_player_stop(player); fluid_player_reset(player); while (player->playlist != NULL) { q = player->playlist->next; pi = (fluid_playlist_item*) player->playlist->data; FLUID_FREE(pi->filename); FLUID_FREE(pi->buffer); FLUID_FREE(pi); delete1_fluid_list(player->playlist); player->playlist = q; } FLUID_FREE(player); return FLUID_OK; } /** * Registers settings related to the MIDI player */ void fluid_player_settings(fluid_settings_t *settings) { /* player.timing-source can be either "system" (use system timer) or "sample" (use timer based on number of written samples) */ fluid_settings_register_str(settings, "player.timing-source", "sample", 0, NULL, NULL); fluid_settings_add_option(settings, "player.timing-source", "sample"); fluid_settings_add_option(settings, "player.timing-source", "system"); /* Selects whether the player should reset the synth between songs, or not. */ fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); } int fluid_player_reset(fluid_player_t *player) { int i; for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { if (player->track[i] != NULL) { delete_fluid_track(player->track[i]); player->track[i] = NULL; } } /* player->current_file = NULL; */ /* player->status = FLUID_PLAYER_READY; */ /* player->loop = 1; */ player->ntracks = 0; player->division = 0; player->send_program_change = 1; player->miditempo = 480000; player->deltatime = 4.0; return 0; } /* * fluid_player_add_track */ int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) { if (player->ntracks < MAX_NUMBER_OF_TRACKS) { player->track[player->ntracks++] = track; return FLUID_OK; } else { return FLUID_FAILED; } } /* * fluid_player_count_tracks */ int fluid_player_count_tracks(fluid_player_t *player) { return player->ntracks; } /* * fluid_player_get_track */ fluid_track_t * fluid_player_get_track(fluid_player_t *player, int i) { if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) { return player->track[i]; } else { return NULL; } } /** * Change the MIDI callback function. This is usually set to * fluid_synth_handle_midi_event, but can optionally be changed * to a user-defined function instead, for intercepting all MIDI * messages sent to the synth. You can also use a midi router as * the callback function to modify the MIDI messages before sending * them to the synth. * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Parameter sent to the callback function * @returns FLUID_OK * @since 1.1.4 */ int fluid_player_set_playback_callback(fluid_player_t* player, handle_midi_event_func_t handler, void* handler_data) { player->playback_callback = handler; player->playback_userdata = handler_data; return FLUID_OK; } /** * Add a MIDI file to a player queue. * @param player MIDI player instance * @param midifile File name of the MIDI file to add * @return #FLUID_OK or #FLUID_FAILED */ int fluid_player_add(fluid_player_t *player, const char *midifile) { fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); char* f = FLUID_STRDUP(midifile); if (!pi || !f) { FLUID_FREE(pi); FLUID_FREE(f); FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } pi->filename = f; pi->buffer = NULL; pi->buffer_len = 0; player->playlist = fluid_list_append(player->playlist, pi); return FLUID_OK; } /** * Add a MIDI file to a player queue, from a buffer in memory. * @param player MIDI player instance * @param buffer Pointer to memory containing the bytes of a complete MIDI * file. The data is copied, so the caller may free or modify it immediately * without affecting the playlist. * @param len Length of the buffer, in bytes. * @return #FLUID_OK or #FLUID_FAILED */ int fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len) { /* Take a copy of the buffer, so the caller can free immediately. */ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); void *buf_copy = FLUID_MALLOC(len); if (!pi || !buf_copy) { FLUID_FREE(pi); FLUID_FREE(buf_copy); FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } FLUID_MEMCPY(buf_copy, buffer, len); pi->filename = NULL; pi->buffer = buf_copy; pi->buffer_len = len; player->playlist = fluid_list_append(player->playlist, pi); return FLUID_OK; } /* * fluid_player_load */ int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) { fluid_midi_file *midifile; char* buffer; size_t buffer_length; int buffer_owned; if (item->filename != NULL) { fluid_file fp; /* This file is specified by filename; load the file from disk */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, item->filename); /* Read the entire contents of the file into the buffer */ fp = FLUID_FOPEN(item->filename, "rb"); if (fp == NULL) { FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); return FLUID_FAILED; } buffer = fluid_file_read_full(fp, &buffer_length); if (buffer == NULL) { FLUID_FCLOSE(fp); return FLUID_FAILED; } buffer_owned = 1; FLUID_FCLOSE(fp); } else { /* This file is specified by a pre-loaded buffer; load from memory */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)", __FILE__, __LINE__, item->buffer); buffer = (char *) item->buffer; buffer_length = item->buffer_len; /* Do not free the buffer (it is owned by the playlist) */ buffer_owned = 0; } midifile = new_fluid_midi_file(buffer, buffer_length); if (midifile == NULL) { if (buffer_owned) { FLUID_FREE(buffer); } return FLUID_FAILED; } player->division = fluid_midi_file_get_division(midifile); fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) { if (buffer_owned) { FLUID_FREE(buffer); } delete_fluid_midi_file(midifile); return FLUID_FAILED; } delete_fluid_midi_file(midifile); if (buffer_owned) { FLUID_FREE(buffer); } return FLUID_OK; } void fluid_player_advancefile(fluid_player_t *player) { if (player->playlist == NULL) { return; /* No files to play */ } if (player->currentfile != NULL) { player->currentfile = fluid_list_next(player->currentfile); } if (player->currentfile == NULL) { if (player->loop == 0) { return; /* We're done playing */ } if (player->loop > 0) { player->loop--; } player->currentfile = player->playlist; } } void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) { fluid_playlist_item* current_playitem; int i; do { fluid_player_advancefile(player); if (player->currentfile == NULL) { /* Failed to find next song, probably since we're finished */ player->status = FLUID_PLAYER_DONE; return; } fluid_player_reset(player); current_playitem = (fluid_playlist_item *) player->currentfile->data; } while (fluid_player_load(player, current_playitem) != FLUID_OK); /* Successfully loaded midi file */ player->begin_msec = msec; player->start_msec = msec; player->start_ticks = 0; player->cur_ticks = 0; if (player->reset_synth_between_songs) { fluid_synth_system_reset(player->synth); } for (i = 0; i < player->ntracks; i++) { if (player->track[i] != NULL) { fluid_track_reset(player->track[i]); } } } /* * fluid_player_callback */ int fluid_player_callback(void *data, unsigned int msec) { int i; int loadnextfile; int status = FLUID_PLAYER_DONE; fluid_player_t *player; fluid_synth_t *synth; player = (fluid_player_t *) data; synth = player->synth; loadnextfile = player->currentfile == NULL ? 1 : 0; do { if (loadnextfile) { loadnextfile = 0; fluid_player_playlist_load(player, msec); if (player->currentfile == NULL) { return 0; } } player->cur_msec = msec; player->cur_ticks = (player->start_ticks + (int) ((double) (player->cur_msec - player->start_msec) / player->deltatime + 0.5)); /* 0.5 to average overall error when casting */ for (i = 0; i < player->ntracks; i++) { if (!fluid_track_eot(player->track[i])) { status = FLUID_PLAYER_PLAYING; if (fluid_track_send_events(player->track[i], synth, player, player->cur_ticks) != FLUID_OK) { /* */ } } } if (status == FLUID_PLAYER_DONE) { FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0); loadnextfile = 1; } } while (loadnextfile); player->status = status; return 1; } /** * Activates play mode for a MIDI player if not already playing. * @param player MIDI player instance * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_player_play(fluid_player_t *player) { if (player->status == FLUID_PLAYER_PLAYING) { return FLUID_OK; } if (player->playlist == NULL) { return FLUID_OK; } player->status = FLUID_PLAYER_PLAYING; if (player->use_system_timer) { player->system_timer = new_fluid_timer((int) player->deltatime, fluid_player_callback, (void *) player, TRUE, FALSE, TRUE); if (player->system_timer == NULL) { return FLUID_FAILED; } } else { player->sample_timer = new_fluid_sample_timer(player->synth, fluid_player_callback, (void *) player); if (player->sample_timer == NULL) { return FLUID_FAILED; } } return FLUID_OK; } /** * Stops a MIDI player. * @param player MIDI player instance * @return Always returns #FLUID_OK */ int fluid_player_stop(fluid_player_t *player) { if (player->system_timer != NULL) { delete_fluid_timer(player->system_timer); } if (player->sample_timer != NULL) { delete_fluid_sample_timer(player->synth, player->sample_timer); } player->status = FLUID_PLAYER_DONE; player->sample_timer = NULL; player->system_timer = NULL; return FLUID_OK; } /** * Get MIDI player status. * @param player MIDI player instance * @return Player status (#fluid_player_status) * @since 1.1.0 */ int fluid_player_get_status(fluid_player_t *player) { return player->status; } /** * Enable looping of a MIDI player * @param player MIDI player instance * @param loop Times left to loop the playlist. -1 means loop infinitely. * @return Always returns #FLUID_OK * @since 1.1.0 * * For example, if you want to loop the playlist twice, set loop to 2 * and call this function before you start the player. */ int fluid_player_set_loop(fluid_player_t *player, int loop) { player->loop = loop; return FLUID_OK; } /** * Set the tempo of a MIDI player. * @param player MIDI player instance * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec) * @return Always returns #FLUID_OK */ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) { player->miditempo = tempo; player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */ player->start_msec = player->cur_msec; player->start_ticks = player->cur_ticks; FLUID_LOG(FLUID_DBG, "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", tempo, player->deltatime, player->cur_msec, player->cur_ticks); return FLUID_OK; } /** * Set the tempo of a MIDI player in beats per minute. * @param player MIDI player instance * @param bpm Tempo in beats per minute * @return Always returns #FLUID_OK */ int fluid_player_set_bpm(fluid_player_t *player, int bpm) { return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm)); } /** * Wait for a MIDI player to terminate (when done playing). * @param player MIDI player instance * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_player_join(fluid_player_t *player) { if (player->system_timer) { return fluid_timer_join(player->system_timer); } else if (player->sample_timer) { /* Busy-wait loop, since there's no thread to wait for... */ while (player->status != FLUID_PLAYER_DONE) { #if defined(WIN32) Sleep(10); #else usleep(10000); #endif } } return FLUID_OK; } /** * Get the number of tempo ticks passed. * @param player MIDI player instance * @return The number of tempo ticks passed * @since 1.1.7 */ int fluid_player_get_current_tick(fluid_player_t * player) { return player->cur_ticks; } /** * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play. * @param player MIDI player instance * @return Total tick count of the sequence * @since 1.1.7 */ int fluid_player_get_total_ticks(fluid_player_t * player) { int i; int maxTicks = 0; for (i = 0; i < player->ntracks; i++) { if (player->track[i] != NULL) { int ticks = fluid_track_get_duration(player->track[i]); if( ticks > maxTicks ) maxTicks = ticks; } } return maxTicks; } /** * Get the tempo of a MIDI player in beats per minute. * @param player MIDI player instance * @return MIDI player tempo in BPM * @since 1.1.7 */ int fluid_player_get_bpm(fluid_player_t * player) { return (int)(60e6 / player->miditempo); } /** * Get the tempo of a MIDI player. * @param player MIDI player instance * @return Tempo of the MIDI player (in microseconds per quarter note, as per MIDI file spec) * @since 1.1.7 */ int fluid_player_get_midi_tempo(fluid_player_t * player) { return player->miditempo; } /************************************************************************ * MIDI PARSER * */ /* * new_fluid_midi_parser */ fluid_midi_parser_t * new_fluid_midi_parser () { fluid_midi_parser_t *parser; parser = FLUID_NEW(fluid_midi_parser_t); if (parser == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ return parser; } /* * delete_fluid_midi_parser */ int delete_fluid_midi_parser(fluid_midi_parser_t *parser) { FLUID_FREE(parser); return FLUID_OK; } /** * Parse a MIDI stream one character at a time. * @param parser Parser instance * @param c Next character in MIDI stream * @return A parsed MIDI event or NULL if none. Event is internal and should * not be modified or freed and is only valid until next call to this function. */ fluid_midi_event_t * fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) { fluid_midi_event_t *event; /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle * of another message. */ if (c >= 0xF8) { if (c == MIDI_SYSTEM_RESET) { parser->event.type = c; parser->status = 0; /* clear the status */ return &parser->event; } return NULL; } /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ if (c & 0x80) { /* Any status byte terminates SYSEX messages (not just 0xF7) */ if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) { event = &parser->event; fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes, FALSE); } else event = NULL; if (c < 0xF0) /* Voice category message? */ { parser->channel = c & 0x0F; parser->status = c & 0xF0; /* The event consumes x bytes of data... (subtract 1 for the status byte) */ parser->nr_bytes_total = fluid_midi_event_length(parser->status) - 1; parser->nr_bytes = 0; /* 0 bytes read so far */ } else if (c == MIDI_SYSEX) { parser->status = MIDI_SYSEX; parser->nr_bytes = 0; } else parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ return event; /* Return SYSEX event or NULL */ } /* Data/parameter byte */ /* Discard data bytes for events we don't care about */ if (parser->status == 0) return NULL; /* Max data size exceeded? (SYSEX messages only really) */ if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) { parser->status = 0; /* Discard the rest of the message */ return NULL; } /* Store next byte */ parser->data[parser->nr_bytes++] = c; /* Do we still need more data to get this event complete? */ if (parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total) return NULL; /* Event is complete, return it. * Running status byte MIDI feature is also handled here. */ parser->event.type = parser->status; parser->event.channel = parser->channel; parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */ switch (parser->status) { case NOTE_OFF: case NOTE_ON: case KEY_PRESSURE: case CONTROL_CHANGE: case PROGRAM_CHANGE: case CHANNEL_PRESSURE: parser->event.param1 = parser->data[0]; /* For example key number */ parser->event.param2 = parser->data[1]; /* For example velocity */ break; case PITCH_BEND: /* Pitch-bend is transmitted with 14-bit precision. */ parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; break; default: /* Unlikely */ return NULL; } return &parser->event; } /* Purpose: * Returns the length of a MIDI message. */ static int fluid_midi_event_length(unsigned char event) { switch (event & 0xF0) { case NOTE_OFF: case NOTE_ON: case KEY_PRESSURE: case CONTROL_CHANGE: case PITCH_BEND: return 3; case PROGRAM_CHANGE: case CHANNEL_PRESSURE: return 2; } switch (event) { case MIDI_TIME_CODE: case MIDI_SONG_SELECT: case 0xF4: case 0xF5: return 2; case MIDI_TUNE_REQUEST: return 1; case MIDI_SONG_POSITION: return 3; } return 1; } fluidsynth-1.1.9/src/midi/fluid_midi.h000066400000000000000000000314371322272076000177130ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_MIDI_H #define _FLUID_MIDI_H #include "fluidsynth_priv.h" #include "fluid_sys.h" #include "fluid_list.h" typedef struct _fluid_midi_parser_t fluid_midi_parser_t; fluid_midi_parser_t* new_fluid_midi_parser(void); int delete_fluid_midi_parser(fluid_midi_parser_t* parser); fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c); /*************************************************************** * * CONSTANTS & ENUM */ #define MAX_NUMBER_OF_TRACKS 128 enum fluid_midi_event_type { /* channel messages */ NOTE_OFF = 0x80, NOTE_ON = 0x90, KEY_PRESSURE = 0xa0, CONTROL_CHANGE = 0xb0, PROGRAM_CHANGE = 0xc0, CHANNEL_PRESSURE = 0xd0, PITCH_BEND = 0xe0, /* system exclusive */ MIDI_SYSEX = 0xf0, /* system common - never in midi files */ MIDI_TIME_CODE = 0xf1, MIDI_SONG_POSITION = 0xf2, MIDI_SONG_SELECT = 0xf3, MIDI_TUNE_REQUEST = 0xf6, MIDI_EOX = 0xf7, /* system real-time - never in midi files */ MIDI_SYNC = 0xf8, MIDI_TICK = 0xf9, MIDI_START = 0xfa, MIDI_CONTINUE = 0xfb, MIDI_STOP = 0xfc, MIDI_ACTIVE_SENSING = 0xfe, MIDI_SYSTEM_RESET = 0xff, /* meta event - for midi files only */ MIDI_META_EVENT = 0xff }; enum fluid_midi_control_change { BANK_SELECT_MSB = 0x00, MODULATION_MSB = 0x01, BREATH_MSB = 0x02, FOOT_MSB = 0x04, PORTAMENTO_TIME_MSB = 0x05, DATA_ENTRY_MSB = 0x06, VOLUME_MSB = 0x07, BALANCE_MSB = 0x08, PAN_MSB = 0x0A, EXPRESSION_MSB = 0x0B, EFFECTS1_MSB = 0x0C, EFFECTS2_MSB = 0x0D, GPC1_MSB = 0x10, /* general purpose controller */ GPC2_MSB = 0x11, GPC3_MSB = 0x12, GPC4_MSB = 0x13, BANK_SELECT_LSB = 0x20, MODULATION_WHEEL_LSB = 0x21, BREATH_LSB = 0x22, FOOT_LSB = 0x24, PORTAMENTO_TIME_LSB = 0x25, DATA_ENTRY_LSB = 0x26, VOLUME_LSB = 0x27, BALANCE_LSB = 0x28, PAN_LSB = 0x2A, EXPRESSION_LSB = 0x2B, EFFECTS1_LSB = 0x2C, EFFECTS2_LSB = 0x2D, GPC1_LSB = 0x30, GPC2_LSB = 0x31, GPC3_LSB = 0x32, GPC4_LSB = 0x33, SUSTAIN_SWITCH = 0x40, PORTAMENTO_SWITCH = 0x41, SOSTENUTO_SWITCH = 0x42, SOFT_PEDAL_SWITCH = 0x43, LEGATO_SWITCH = 0x45, HOLD2_SWITCH = 0x45, SOUND_CTRL1 = 0x46, SOUND_CTRL2 = 0x47, SOUND_CTRL3 = 0x48, SOUND_CTRL4 = 0x49, SOUND_CTRL5 = 0x4A, SOUND_CTRL6 = 0x4B, SOUND_CTRL7 = 0x4C, SOUND_CTRL8 = 0x4D, SOUND_CTRL9 = 0x4E, SOUND_CTRL10 = 0x4F, GPC5 = 0x50, GPC6 = 0x51, GPC7 = 0x52, GPC8 = 0x53, PORTAMENTO_CTRL = 0x54, EFFECTS_DEPTH1 = 0x5B, EFFECTS_DEPTH2 = 0x5C, EFFECTS_DEPTH3 = 0x5D, EFFECTS_DEPTH4 = 0x5E, EFFECTS_DEPTH5 = 0x5F, DATA_ENTRY_INCR = 0x60, DATA_ENTRY_DECR = 0x61, NRPN_LSB = 0x62, NRPN_MSB = 0x63, RPN_LSB = 0x64, RPN_MSB = 0x65, ALL_SOUND_OFF = 0x78, ALL_CTRL_OFF = 0x79, LOCAL_CONTROL = 0x7A, ALL_NOTES_OFF = 0x7B, OMNI_OFF = 0x7C, OMNI_ON = 0x7D, POLY_OFF = 0x7E, POLY_ON = 0x7F }; /* General MIDI RPN event numbers (LSB, MSB = 0) */ enum midi_rpn_event { RPN_PITCH_BEND_RANGE = 0x00, RPN_CHANNEL_FINE_TUNE = 0x01, RPN_CHANNEL_COARSE_TUNE = 0x02, RPN_TUNING_PROGRAM_CHANGE = 0x03, RPN_TUNING_BANK_SELECT = 0x04, RPN_MODULATION_DEPTH_RANGE = 0x05 }; enum midi_meta_event { MIDI_COPYRIGHT = 0x02, MIDI_TRACK_NAME = 0x03, MIDI_INST_NAME = 0x04, MIDI_LYRIC = 0x05, MIDI_MARKER = 0x06, MIDI_CUE_POINT = 0x07, MIDI_EOT = 0x2f, MIDI_SET_TEMPO = 0x51, MIDI_SMPTE_OFFSET = 0x54, MIDI_TIME_SIGNATURE = 0x58, MIDI_KEY_SIGNATURE = 0x59, MIDI_SEQUENCER_EVENT = 0x7f }; /* MIDI SYSEX useful manufacturer values */ enum midi_sysex_manuf { MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ }; #define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ /* SYSEX sub-ID #1 which follows device ID */ #define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ #define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ /** * SYSEX tuning message IDs. */ enum midi_sysex_tuning_msg_id { MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */ MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ }; /* General MIDI sub-ID #2 */ #define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ #define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ enum fluid_driver_status { FLUID_MIDI_READY, FLUID_MIDI_LISTENING, FLUID_MIDI_DONE }; /*************************************************************** * * TYPE DEFINITIONS & FUNCTION DECLARATIONS */ /* From ctype.h */ #define fluid_isascii(c) (((c) & ~0x7f) == 0) /* * fluid_midi_event_t */ struct _fluid_midi_event_t { fluid_midi_event_t* next; /* Link to next event */ void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ unsigned int param1; /* First parameter */ unsigned int param2; /* Second parameter */ unsigned char type; /* MIDI event type */ unsigned char channel; /* MIDI channel */ }; /* * fluid_track_t */ struct _fluid_track_t { char* name; int num; fluid_midi_event_t *first; fluid_midi_event_t *cur; fluid_midi_event_t *last; unsigned int ticks; }; typedef struct _fluid_track_t fluid_track_t; fluid_track_t* new_fluid_track(int num); int delete_fluid_track(fluid_track_t* track); int fluid_track_set_name(fluid_track_t* track, char* name); char* fluid_track_get_name(fluid_track_t* track); int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt); fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track); fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track); int fluid_track_get_duration(fluid_track_t* track); int fluid_track_reset(fluid_track_t* track); int fluid_track_send_events(fluid_track_t* track, fluid_synth_t* synth, fluid_player_t* player, unsigned int ticks); #define fluid_track_eot(track) ((track)->cur == NULL) /* * fluid_playlist_item * Used as the `data' elements of the fluid_player.playlist. * Represents either a filename or a pre-loaded memory buffer. * Exactly one of `filename' and `buffer' is non-NULL. */ typedef struct { char* filename; /** Name of file (owned); NULL if data pre-loaded */ void* buffer; /** The MIDI file data (owned); NULL if filename */ size_t buffer_len; /** Number of bytes in buffer; 0 if filename */ } fluid_playlist_item; /* * fluid_player */ struct _fluid_player_t { int status; int ntracks; fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; fluid_synth_t* synth; fluid_timer_t* system_timer; fluid_sample_timer_t* sample_timer; int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */ fluid_list_t* playlist; /* List of fluid_playlist_item* objects */ fluid_list_t* currentfile; /* points to an item in files, or NULL if not playing */ char send_program_change; /* should we ignore the program changes? */ char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ int start_ticks; /* the number of tempo ticks passed at the last tempo change */ int cur_ticks; /* the number of tempo ticks passed */ int begin_msec; /* the time (msec) of the beginning of the file */ int start_msec; /* the start time of the last tempo change */ int cur_msec; /* the current time */ int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ double deltatime; /* milliseconds per midi tick. depends on set-tempo */ unsigned int division; handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ void* playback_userdata; /* pointer to user-defined data passed to playback_callback function */ }; int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track); int fluid_player_callback(void* data, unsigned int msec); int fluid_player_count_tracks(fluid_player_t* player); fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i); int fluid_player_reset(fluid_player_t* player); int fluid_player_load(fluid_player_t* player, fluid_playlist_item *item); void fluid_player_settings(fluid_settings_t* settings); /* * fluid_midi_file */ typedef struct { const char* buffer; /* Entire contents of MIDI file (borrowed) */ int buf_len; /* Length of buffer, in bytes */ int buf_pos; /* Current read position in contents buffer */ int eof; /* The "end of file" condition */ int running_status; int c; int type; int ntracks; int uses_smpte; unsigned int smpte_fps; unsigned int smpte_res; unsigned int division; /* If uses_SMPTE == 0 then division is ticks per beat (quarter-note) */ double tempo; /* Beats per second (SI rules =) */ int tracklen; int trackpos; int eot; int varlen; int dtime; } fluid_midi_file; fluid_midi_file* new_fluid_midi_file(const char* buffer, size_t length); void delete_fluid_midi_file(fluid_midi_file* mf); int fluid_midi_file_read_mthd(fluid_midi_file* midifile); int fluid_midi_file_load_tracks(fluid_midi_file* midifile, fluid_player_t* player); int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num); int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track); int fluid_midi_file_read_varlen(fluid_midi_file* mf); int fluid_midi_file_getc(fluid_midi_file* mf); int fluid_midi_file_push(fluid_midi_file* mf, int c); int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len); int fluid_midi_file_skip(fluid_midi_file* mf, int len); int fluid_midi_file_eof(fluid_midi_file* mf); int fluid_midi_file_read_tracklen(fluid_midi_file* mf); int fluid_midi_file_eot(fluid_midi_file* mf); int fluid_midi_file_get_division(fluid_midi_file* midifile); #define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */ /* * fluid_midi_parser_t */ struct _fluid_midi_parser_t { unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */ unsigned char channel; /* The channel of the event that is received (in case of a channel event) */ unsigned int nr_bytes; /* How many bytes have been read for the current event? */ unsigned int nr_bytes_total; /* How many bytes does the current event type include? */ unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */ fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */ }; int fluid_isasciistring(char* s); long fluid_getlength(unsigned char *s); #endif /* _FLUID_MIDI_H */ fluidsynth-1.1.9/src/midi/fluid_midi_router.c000066400000000000000000001006361322272076000213040ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* Original author: Markus Nentwig, nentwig@users.sourceforge.net * * Josh Green made it general purpose with a complete usable public API and * cleaned it up a bit. */ #include "fluid_midi_router.h" #include "fluid_midi.h" #include "fluid_synth.h" /* * fluid_midi_router */ struct _fluid_midi_router_t { fluid_synth_t* synth; fluid_mutex_t rules_mutex; fluid_midi_router_rule_t *rules[FLUID_MIDI_ROUTER_RULE_COUNT]; /* List of rules for each rule type */ fluid_midi_router_rule_t *free_rules; /* List of rules to free (was waiting for final events which were received) */ handle_midi_event_func_t event_handler; /* Callback function for generated events */ void* event_handler_data; /* One arg for the callback */ int nr_midi_channels; /* For clipping the midi channel */ /* FIXME - If there are multiple command handlers, they will conflict! */ fluid_midi_router_rule_t *cmd_rule; /* Rule currently being processed by shell command handler */ int cmd_rule_type; /* Type of the rule (fluid_midi_router_rule_type) */ }; struct _fluid_midi_router_rule_t { int chan_min; /* Channel window, for which this rule is valid */ int chan_max; fluid_real_t chan_mul; /* Channel multiplier (usually 0 or 1) */ int chan_add; /* Channel offset */ int par1_min; /* Parameter 1 window, for which this rule is valid */ int par1_max; fluid_real_t par1_mul; int par1_add; int par2_min; /* Parameter 2 window, for which this rule is valid */ int par2_max; fluid_real_t par2_mul; int par2_add; int pending_events; /* In case of noteon: How many keys are still down? */ char keys_cc[128]; /* Flags, whether a key is down / controller is set (sustain) */ fluid_midi_router_rule_t* next; /* next entry */ int waiting; /* Set to TRUE when rule has been deactivated but there are still pending_events */ }; /** * Create a new midi router. The default rules will pass all events unmodified. * @param settings Settings used to configure MIDI router * @param handler MIDI event callback. * @param event_handler_data Caller defined data pointer which gets passed to 'handler' * @return New MIDI router instance or NULL on error * * The MIDI handler callback should process the possibly filtered/modified MIDI * events from the MIDI router and forward them on to a synthesizer for example. * The function fluid_synth_handle_midi_event() can be used for \a handle and * a #fluid_synth_t passed as the \a event_handler_data parameter for this purpose. */ fluid_midi_router_t * new_fluid_midi_router(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data) { fluid_midi_router_t *router = NULL; int i; router = FLUID_NEW (fluid_midi_router_t); if (router == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET (router, 0, sizeof (fluid_midi_router_t)); /* Retrieve the number of MIDI channels for range limiting */ fluid_settings_getint(settings, "synth.midi-channels", &router->nr_midi_channels); fluid_mutex_init (router->rules_mutex); router->synth = (fluid_synth_t *)event_handler_data; router->event_handler = handler; router->event_handler_data = event_handler_data; /* Create default routing rules which pass all events unmodified */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { router->rules[i] = new_fluid_midi_router_rule (); if (!router->rules[i]) goto error_recovery; } return router; error_recovery: delete_fluid_midi_router (router); return NULL; } /** * Delete a MIDI router instance. * @param router MIDI router to delete * @return Returns #FLUID_OK on success, #FLUID_FAILED otherwise (only if NULL * \a router passed really) */ int delete_fluid_midi_router (fluid_midi_router_t *router) { fluid_midi_router_rule_t *rule; fluid_midi_router_rule_t *next_rule; int i; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { for (rule = router->rules[i]; rule; rule = next_rule) { next_rule = rule->next; FLUID_FREE (rule); } } fluid_mutex_destroy (router->rules_mutex); FLUID_FREE (router); return FLUID_OK; } /** * Set a MIDI router to use default "unity" rules. Such a router will pass all * events unmodified. * @param router Router to set to default rules. * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_midi_router_set_default_rules (fluid_midi_router_t *router) { fluid_midi_router_rule_t *new_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; int i, i2; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); /* Allocate new default rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { new_rules[i] = new_fluid_midi_router_rule (); if (!new_rules[i]) { /* Free already allocated rules */ for (i2 = 0; i2 < i; i2++) delete_fluid_midi_router_rule (new_rules[i]); return FLUID_FAILED; } } fluid_mutex_lock (router->rules_mutex); /* ++ lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { del_rules[i] = NULL; prev_rule = NULL; /* Process existing rules */ for (rule = router->rules[i]; rule; rule = next_rule) { next_rule = rule->next; if (rule->pending_events == 0) /* Rule has no pending events? */ { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else if (rule == router->rules[i]) router->rules[i] = next_rule; /* Prepend to delete list */ rule->next = del_rules[i]; del_rules[i] = rule; } else { rule->waiting = TRUE; /* Pending events, mark as waiting */ prev_rule = rule; } } /* Prepend new default rule */ new_rules[i]->next = router->rules[i]; router->rules[i] = new_rules[i]; } fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free old rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { for (rule = del_rules[i]; rule; rule = next_rule) { next_rule = rule->next; FLUID_FREE (rule); } } return FLUID_OK; } /** * Clear all rules in a MIDI router. Such a router will drop all events until * rules are added. * @param router Router to clear all rules from * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_midi_router_clear_rules (fluid_midi_router_t *router) { fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; int i; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); fluid_mutex_lock (router->rules_mutex); /* ++ lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { del_rules[i] = NULL; prev_rule = NULL; /* Process existing rules */ for (rule = router->rules[i]; rule; rule = next_rule) { next_rule = rule->next; if (rule->pending_events == 0) /* Rule has no pending events? */ { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else if (rule == router->rules[i]) router->rules[i] = next_rule; /* Prepend to delete list */ rule->next = del_rules[i]; del_rules[i] = rule; } else { rule->waiting = TRUE; /* Pending events, mark as waiting */ prev_rule = rule; } } } fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free old rules outside of lock */ for (i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) { for (rule = del_rules[i]; rule; rule = next_rule) { next_rule = rule->next; FLUID_FREE (rule); } } return FLUID_OK; } /** * Add a rule to a MIDI router. * @param router MIDI router * @param rule Rule to add (used directly and should not be accessed again following a * successful call to this function). * @param type The type of rule to add (#fluid_midi_router_rule_type) * @return #FLUID_OK on success, #FLUID_FAILED otherwise (invalid rule for example) * @since 1.1.0 */ int fluid_midi_router_add_rule (fluid_midi_router_t *router, fluid_midi_router_rule_t *rule, int type) { fluid_midi_router_rule_t *free_rules, *next_rule; fluid_return_val_if_fail (router != NULL, FLUID_FAILED); fluid_return_val_if_fail (rule != NULL, FLUID_FAILED); fluid_return_val_if_fail (type >= 0 && type < FLUID_MIDI_ROUTER_RULE_COUNT, FLUID_FAILED); fluid_mutex_lock (router->rules_mutex); /* ++ lock */ /* Take over free rules list, if any (to free outside of lock) */ free_rules = router->free_rules; router->free_rules = NULL; rule->next = router->rules[type]; router->rules[type] = rule; fluid_mutex_unlock (router->rules_mutex); /* -- unlock */ /* Free any deactivated rules which were waiting for events and are now done */ for (; free_rules; free_rules = next_rule) { next_rule = free_rules->next; FLUID_FREE (free_rules); } return FLUID_OK; } /** * Create a new MIDI router rule. * @return Newly allocated router rule or NULL if out of memory. * @since 1.1.0 * * The new rule is a "unity" rule which will accept any values and wont modify * them. */ fluid_midi_router_rule_t * new_fluid_midi_router_rule (void) { fluid_midi_router_rule_t *rule; rule = FLUID_NEW (fluid_midi_router_rule_t); if (rule == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET (rule, 0, sizeof (fluid_midi_router_rule_t)); rule->chan_min = 0; rule->chan_max = 999999; rule->chan_mul = 1.0; rule->chan_add = 0; rule->par1_min = 0; rule->par1_max = 999999; rule->par1_mul = 1.0; rule->par1_add = 0; rule->par2_min = 0; rule->par2_max = 999999; rule->par2_mul = 1.0; rule->par2_add = 0; return rule; }; /** * Free a MIDI router rule. * @param rule Router rule to free * @since 1.1.0 * * Note that rules which have been added to a router are managed by the router, * so this function should seldom be needed. */ void delete_fluid_midi_router_rule (fluid_midi_router_rule_t *rule) { fluid_return_if_fail (rule != NULL); FLUID_FREE (rule); } /** * Set the channel portion of a rule. * @param rule MIDI router rule * @param min Minimum value for rule match * @param max Maximum value for rule match * @param mul Value which is multiplied by matching event's channel value (1.0 to not modify) * @param add Value which is added to matching event's channel value (0 to not modify) * @since 1.1.0 * * The \a min and \a max parameters define a channel range window to match * incoming events to. If \a min is less than or equal to \a max then an event * is matched if its channel is within the defined range (including \a min * and \a max). If \a min is greater than \a max then rule is inverted and matches * everything except in *between* the defined range (so \a min and \a max would match). * * The \a mul and \a add values are used to modify event channel values prior to * sending the event, if the rule matches. */ void fluid_midi_router_rule_set_chan (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add) { fluid_return_if_fail (rule != NULL); rule->chan_min = min; rule->chan_max = max; rule->chan_mul = mul; rule->chan_add = add; } /** * Set the first parameter portion of a rule. * @param rule MIDI router rule * @param min Minimum value for rule match * @param max Maximum value for rule match * @param mul Value which is multiplied by matching event's 1st parameter value (1.0 to not modify) * @param add Value which is added to matching event's 1st parameter value (0 to not modify) * @since 1.1.0 * * The 1st parameter of an event depends on the type of event. For note events * its the MIDI note #, for CC events its the MIDI control number, for program * change events its the MIDI program #, for pitch bend events its the bend value, * for channel pressure its the channel pressure value and for key pressure * its the MIDI note number. * * Pitch bend values have a maximum value of 16383 (8192 is pitch bend center) and all * other events have a max of 127. All events have a minimum value of 0. * * The \a min and \a max parameters define a parameter range window to match * incoming events to. If \a min is less than or equal to \a max then an event * is matched if its 1st parameter is within the defined range (including \a min * and \a max). If \a min is greater than \a max then rule is inverted and matches * everything except in *between* the defined range (so \a min and \a max would match). * * The \a mul and \a add values are used to modify event 1st parameter values prior to * sending the event, if the rule matches. */ void fluid_midi_router_rule_set_param1 (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add) { fluid_return_if_fail (rule != NULL); rule->par1_min = min; rule->par1_max = max; rule->par1_mul = mul; rule->par1_add = add; } /** * Set the second parameter portion of a rule. * @param rule MIDI router rule * @param min Minimum value for rule match * @param max Maximum value for rule match * @param mul Value which is multiplied by matching event's 2nd parameter value (1.0 to not modify) * @param add Value which is added to matching event's 2nd parameter value (0 to not modify) * @since 1.1.0 * * The 2nd parameter of an event depends on the type of event. For note events * its the MIDI velocity, for CC events its the control value and for key pressure * events its the key pressure value. All other types lack a 2nd parameter. * * All applicable 2nd parameters have the range 0-127. * * The \a min and \a max parameters define a parameter range window to match * incoming events to. If \a min is less than or equal to \a max then an event * is matched if its 2nd parameter is within the defined range (including \a min * and \a max). If \a min is greater than \a max then rule is inverted and matches * everything except in *between* the defined range (so \a min and \a max would match). * * The \a mul and \a add values are used to modify event 2nd parameter values prior to * sending the event, if the rule matches. */ void fluid_midi_router_rule_set_param2 (fluid_midi_router_rule_t *rule, int min, int max, float mul, int add) { fluid_return_if_fail (rule != NULL); rule->par2_min = min; rule->par2_max = max; rule->par2_mul = mul; rule->par2_add = add; } /** * Handle a MIDI event through a MIDI router instance. * @param data MIDI router instance #fluid_midi_router_t, its a void * so that * this function can be used as a callback for other subsystems * (new_fluid_midi_driver() for example). * @param event MIDI event to handle * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * Purpose: The midi router is called for each event, that is received * via the 'physical' midi input. Each event can trigger an arbitrary number * of generated events (one for each rule that matches). * * In default mode, a noteon event is just forwarded to the synth's 'noteon' function, * a 'CC' event to the synth's 'CC' function and so on. * * The router can be used to: * - filter messages (for example: Pass sustain pedal CCs only to selected channels) * - split the keyboard (noteon with notenr < x: to ch 1, >x to ch 2) * - layer sounds (for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...) * - velocity scaling (for each noteon event, scale the velocity by 1.27 to give DX7 users * a chance) * - velocity switching ("v <=100: Angel Choir; V > 100: Hell's Bells") * - get rid of aftertouch * - ... */ int fluid_midi_router_handle_midi_event (void* data, fluid_midi_event_t* event) { fluid_midi_router_t* router = (fluid_midi_router_t *)data; fluid_midi_router_rule_t **rulep, *rule, *next_rule, *prev_rule = NULL; int event_has_par2 = 0; /* Flag, indicates that current event needs two parameters */ int par1_max = 127; /* Range limit for par1 */ int par2_max = 127; /* Range limit for par2 */ int ret_val = FLUID_OK; int chan; /* Channel of the generated event */ int par1; /* par1 of the generated event */ int par2; int event_par1; int event_par2; fluid_midi_event_t new_event; /* Some keyboards report noteoff through a noteon event with vel=0. * Convert those to noteoff to ease processing. */ if (event->type == NOTE_ON && event->param2 == 0) { event->type = NOTE_OFF; event->param2 = 127; /* Release velocity */ } fluid_mutex_lock (router->rules_mutex); /* ++ lock rules */ /* Depending on the event type, choose the correct list of rules. */ switch (event->type) { case NOTE_ON: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; case NOTE_OFF: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; event_has_par2 = 1; break; case CONTROL_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CC]; event_has_par2 = 1; break; case PROGRAM_CHANGE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PROG_CHANGE]; break; case PITCH_BEND: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PITCH_BEND]; par1_max = 16383; break; case CHANNEL_PRESSURE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE]; break; case KEY_PRESSURE: rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE]; event_has_par2 = 1; break; case MIDI_SYSTEM_RESET: case MIDI_SYSEX: ret_val = router->event_handler (router->event_handler_data,event); fluid_mutex_unlock (router->rules_mutex); /* -- unlock rules */ return ret_val; default: rulep = NULL; /* Event will not be passed on */ break; } /* Loop over rules in the list, looking for matches for this event. */ for (rule = rulep ? *rulep : NULL; rule; prev_rule = rule, rule = next_rule) { event_par1 = (int)event->param1; event_par2 = (int)event->param2; next_rule = rule->next; /* Rule may get removed from list, so get next here */ /* Channel window */ if (rule->chan_min > rule->chan_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event->channel > rule->chan_max && event->channel < rule->chan_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max) */ { if (event->channel > rule->chan_max || event->channel < rule->chan_min) continue; } /* Par 1 window */ if (rule->par1_min > rule->par1_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event_par1 > rule->par1_max && event_par1 < rule->par1_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ { if (event_par1 > rule->par1_max || event_par1 < rule->par1_min) continue; } /* Par 2 window (only applies to event types, which have 2 pars) * For noteoff events, velocity switching doesn't make any sense. * Velocity scaling might be useful, though. */ if (event_has_par2 && event->type != NOTE_OFF) { if (rule->par2_min > rule->par2_max) { /* Inverted rule: Exclude everything between max and min (but not min/max) */ if (event_par2 > rule->par2_max && event_par2 < rule->par2_min) continue; } else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ { if (event_par2 > rule->par2_max || event_par2 < rule->par2_min) continue; } } /* Channel scaling / offset * 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); /* 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); /* 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); else par2 = 0; /* Channel range limiting */ if (chan < 0) chan = 0; else if (chan >= router->nr_midi_channels) chan = router->nr_midi_channels - 1; /* Par1 range limiting */ if (par1 < 0) par1 = 0; else if (par1 > par1_max) par1 = par1_max; /* Par2 range limiting */ if (event_has_par2) { if (par2 < 0) par2 = 0; else if (par2 > par2_max) par2 = par2_max; } /* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2). * We keep track on the state of noteon and sustain pedal events. If the application tries * to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have * arrived. In the meantime while waiting, it will only let through 'negative' events * (noteoff or pedal up). */ if (event->type == NOTE_ON || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 >= 64)) { /* Noteon or sustain pedal down event generated */ if (rule->keys_cc[par1] == 0) { rule->keys_cc[par1] = 1; rule->pending_events++; } } else if (event->type == NOTE_OFF || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 < 64)) { /* Noteoff or sustain pedal up event generated */ if (rule->keys_cc[par1] > 0) { rule->keys_cc[par1] = 0; rule->pending_events--; /* Rule is waiting for negative event to be destroyed? */ if (rule->waiting) { if (rule->pending_events == 0) { /* Remove rule from rule list */ if (prev_rule) prev_rule->next = next_rule; else *rulep = next_rule; /* Add to free list */ rule->next = router->free_rules; router->free_rules = rule; rule = prev_rule; /* Set rule to previous rule, which gets assigned to the next prev_rule value (in for() statement) */ } goto send_event; /* Pass the event to complete the cycle */ } } } /* Rule is still waiting for negative event? (note off or pedal up) */ if (rule->waiting) continue; /* Skip (rule is inactive except for matching negative event) */ send_event: /* At this point it is decided, what is sent to the synth. * Create a new event and make the appropriate call */ fluid_midi_event_set_type (&new_event, event->type); fluid_midi_event_set_channel (&new_event, chan); new_event.param1 = par1; new_event.param2 = par2; /* FIXME - What should be done on failure? For now continue to process events, but return failure to caller. */ if (router->event_handler (router->event_handler_data, &new_event) != FLUID_OK) ret_val = FLUID_FAILED; } fluid_mutex_unlock (router->rules_mutex); /* -- unlock rules */ return ret_val; } #define CHECK_VALID_ROUTER(_router, _out) \ if (router == NULL) { \ fluid_ostream_printf(out, "cannot execute router command without a midi router.\n"); \ return FLUID_FAILED; \ } /* Command handler for "router_clear" command */ int fluid_midi_router_handle_clear (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t *router = synth->midi_router; if (ac != 0) { fluid_ostream_printf (out, "router_clear needs no arguments.\n"); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); fluid_midi_router_clear_rules (router); return FLUID_OK; } /* Command handler for "router_default" command */ int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t *router = synth->midi_router; if (ac != 0) { fluid_ostream_printf(out, "router_default needs no arguments.\n"); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); fluid_midi_router_set_default_rules (router); return FLUID_OK; } /* Command handler for "router_begin" command */ int fluid_midi_router_handle_begin (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t* router = synth->midi_router; if (ac != 1) { fluid_ostream_printf (out, "router_begin requires [note|cc|prog|pbend|cpress|kpress]\n"); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); if (FLUID_STRCMP (av[0], "note") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_NOTE; else if (FLUID_STRCMP (av[0], "cc") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CC; else if (FLUID_STRCMP (av[0], "prog") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PROG_CHANGE; else if (FLUID_STRCMP (av[0], "pbend") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PITCH_BEND; else if (FLUID_STRCMP (av[0], "cpress") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE; else if (FLUID_STRCMP (av[0], "kpress") == 0) router->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE; else { fluid_ostream_printf (out, "router_begin requires [note|cc|prog|pbend|cpress|kpress]\n"); return FLUID_FAILED; } if (router->cmd_rule) delete_fluid_midi_router_rule (router->cmd_rule); router->cmd_rule = new_fluid_midi_router_rule (); if (!router->cmd_rule) return FLUID_FAILED; return FLUID_OK; } /* Command handler for "router_end" command */ int fluid_midi_router_handle_end (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t* router = synth->midi_router; if (ac != 0) { fluid_ostream_printf (out, "router_end needs no arguments.\n"); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); if (!router->cmd_rule) { fluid_ostream_printf (out, "No active router_begin command.\n"); return FLUID_FAILED; } /* Add the rule */ if (fluid_midi_router_add_rule (router, router->cmd_rule, router->cmd_rule_type) != FLUID_OK) delete_fluid_midi_router_rule (router->cmd_rule); /* Free on failure */ router->cmd_rule = NULL; return FLUID_OK; } /* Command handler for "router_chan" command */ int fluid_midi_router_handle_chan (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t* router = synth->midi_router; if (ac != 4) { fluid_ostream_printf(out, "router_chan needs four args: min, max, mul, add."); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); if (!router->cmd_rule) { fluid_ostream_printf (out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_chan (router->cmd_rule, atoi (av[0]), atoi (av[1]), atof (av[2]), atoi (av[3])); return FLUID_OK; } /* Command handler for "router_par1" command */ int fluid_midi_router_handle_par1 (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t* router = synth->midi_router; if (ac != 4) { fluid_ostream_printf(out, "router_par1 needs four args: min, max, mul, add."); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); if (!router->cmd_rule) { fluid_ostream_printf (out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_param1 (router->cmd_rule, atoi (av[0]), atoi (av[1]), atof (av[2]), atoi (av[3])); return FLUID_OK; } /* Command handler for "router_par2" command */ int fluid_midi_router_handle_par2 (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_midi_router_t* router = synth->midi_router; if (ac != 4) { fluid_ostream_printf(out, "router_par2 needs four args: min, max, mul, add."); return FLUID_FAILED; } CHECK_VALID_ROUTER (router, out); if (!router->cmd_rule) { fluid_ostream_printf (out, "No active router_begin command.\n"); return FLUID_FAILED; } fluid_midi_router_rule_set_param2 (router->cmd_rule, atoi (av[0]), atoi (av[1]), atof (av[2]), atoi (av[3])); return FLUID_OK; } /** * MIDI event callback function to display event information to stdout * @param data MIDI router instance * @param event MIDI event data * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * An implementation of the #handle_midi_event_func_t function type, used for * displaying MIDI event information between the MIDI driver and router to * stdout. Useful for adding into a MIDI router chain for debugging MIDI events. */ int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event) { switch (event->type) { case NOTE_ON: fprintf(stdout, "event_pre_noteon %i %i %i\n", event->channel, event->param1, event->param2); break; case NOTE_OFF: fprintf(stdout, "event_pre_noteoff %i %i %i\n", event->channel, event->param1, event->param2); break; case CONTROL_CHANGE: fprintf(stdout, "event_pre_cc %i %i %i\n", event->channel, event->param1, event->param2); break; case PROGRAM_CHANGE: fprintf(stdout, "event_pre_prog %i %i\n", event->channel, event->param1); break; case PITCH_BEND: fprintf(stdout, "event_pre_pitch %i %i\n", event->channel, event->param1); break; case CHANNEL_PRESSURE: fprintf(stdout, "event_pre_cpress %i %i\n", event->channel, event->param1); break; case KEY_PRESSURE: fprintf(stdout, "event_pre_kpress %i %i %i\n", event->channel, event->param1, event->param2); break; default: break; } return fluid_midi_router_handle_midi_event((fluid_midi_router_t*) data, event); } /** * MIDI event callback function to display event information to stdout * @param data MIDI router instance * @param event MIDI event data * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * An implementation of the #handle_midi_event_func_t function type, used for * displaying MIDI event information between the MIDI driver and router to * stdout. Useful for adding into a MIDI router chain for debugging MIDI events. */ int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event) { switch (event->type) { case NOTE_ON: fprintf(stdout, "event_post_noteon %i %i %i\n", event->channel, event->param1, event->param2); break; case NOTE_OFF: fprintf(stdout, "event_post_noteoff %i %i %i\n", event->channel, event->param1, event->param2); break; case CONTROL_CHANGE: fprintf(stdout, "event_post_cc %i %i %i\n", event->channel, event->param1, event->param2); break; case PROGRAM_CHANGE: fprintf(stdout, "event_post_prog %i %i\n", event->channel, event->param1); break; case PITCH_BEND: fprintf(stdout, "event_post_pitch %i %i\n", event->channel, event->param1); break; case CHANNEL_PRESSURE: fprintf(stdout, "event_post_cpress %i %i\n", event->channel, event->param1); break; case KEY_PRESSURE: fprintf(stdout, "event_post_kpress %i %i %i\n", event->channel, event->param1, event->param2); break; default: break; } return fluid_synth_handle_midi_event((fluid_synth_t*) data, event); } fluidsynth-1.1.9/src/midi/fluid_midi_router.h000066400000000000000000000033011322272076000213000ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* Author: Markus Nentwig, nentwig@users.sourceforge.net */ #ifndef _FLUID_MIDIROUTER_H #define _FLUID_MIDIROUTER_H #include "fluidsynth_priv.h" #include "fluid_midi.h" #include "fluid_sys.h" int fluid_midi_router_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_begin(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_chan(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_par1(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_par2(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_midi_router_handle_end(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); #endif fluidsynth-1.1.9/src/midi/fluid_seq.c000066400000000000000000000736401322272076000175560ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* 2002 : API design by Peter Hanappe and Antoine Schmitt August 2002 : Implementation by Antoine Schmitt as@gratin.org as part of the infiniteCD author project http://www.infiniteCD.org/ */ #include "fluid_event_priv.h" #include "fluidsynth_priv.h" // FLUID_NEW, etc #include "fluid_sys.h" // timer, threads, etc... #include "fluid_list.h" /*************************************************************** * * SEQUENCER */ #define FLUID_SEQUENCER_EVENTS_MAX 1000 /* Private data for SEQUENCER */ struct _fluid_sequencer_t { unsigned int startMs; gint currentMs; gboolean useSystemTimer; double scale; // ticks per second fluid_list_t* clients; fluid_seq_id_t clientsID; /* for queue + heap */ fluid_evt_entry* preQueue; fluid_evt_entry* preQueueLast; fluid_timer_t* timer; int queue0StartTime; short prevCellNb; fluid_evt_entry* queue0[256][2]; fluid_evt_entry* queue1[255][2]; fluid_evt_entry* queueLater; fluid_evt_heap_t* heap; fluid_mutex_t mutex; #if FLUID_SEQ_WITH_TRACE char *tracebuf; char *traceptr; int tracelen; #endif }; /* Private data for clients */ typedef struct _fluid_sequencer_client_t { fluid_seq_id_t id; char* name; fluid_event_callback_t callback; void* data; } fluid_sequencer_client_t; /* prototypes */ static short _fluid_seq_queue_init(fluid_sequencer_t* seq, int nbEvents); static void _fluid_seq_queue_end(fluid_sequencer_t* seq); static short _fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt); static void _fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, fluid_seq_id_t src, fluid_seq_id_t dest, int type); static int _fluid_seq_queue_process(void* data, unsigned int msec); // callback from timer static void _fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry); static void _fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* temp); static void _fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq); static void _fluid_free_evt_queue(fluid_evt_entry** first, fluid_evt_entry** last); /* API implementation */ /** * Create a new sequencer object which uses the system timer. Use * new_fluid_sequencer2() to specify whether the system timer or * fluid_sequencer_process() is used to advance the sequencer. * @return New sequencer instance */ fluid_sequencer_t* new_fluid_sequencer (void) { return new_fluid_sequencer2 (TRUE); } /** * Create a new sequencer object. * @param use_system_timer If TRUE, sequencer will advance at the rate of the * system clock. If FALSE, call fluid_sequencer_process() to advance * the sequencer. * @return New sequencer instance * @since 1.1.0 */ fluid_sequencer_t* new_fluid_sequencer2 (int use_system_timer) { fluid_sequencer_t* seq; seq = FLUID_NEW(fluid_sequencer_t); if (seq == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t)); seq->scale = 1000; // default value seq->useSystemTimer = use_system_timer ? TRUE : FALSE; seq->startMs = seq->useSystemTimer ? fluid_curtime() : 0; seq->clients = NULL; seq->clientsID = 0; if (-1 == _fluid_seq_queue_init(seq, FLUID_SEQUENCER_EVENTS_MAX)) { FLUID_FREE(seq); fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } #if FLUID_SEQ_WITH_TRACE seq->tracelen = 1024*100; seq->tracebuf = (char *)FLUID_MALLOC(seq->tracelen); if (seq->tracebuf == NULL) { _fluid_seq_queue_end(seq); FLUID_FREE(seq); fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } seq->traceptr = seq->tracebuf; #endif return(seq); } /** * Free a sequencer object. * @param seq Sequencer to delete */ void delete_fluid_sequencer (fluid_sequencer_t* seq) { if (seq == NULL) { return; } /* cleanup clients */ while (seq->clients) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)seq->clients->data; fluid_sequencer_unregister_client(seq, client->id); } _fluid_seq_queue_end(seq); /* if (seq->clients) { fluid_list_t *tmp = seq->clients; while (tmp != NULL) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; if (client->name) FLUID_FREE(client->name); tmp = tmp->next; } delete_fluid_list(seq->clients); seq->clients = NULL; }*/ #if FLUID_SEQ_WITH_TRACE if (seq->tracebuf != NULL) FLUID_FREE(seq->tracebuf); seq->tracebuf = NULL; #endif FLUID_FREE(seq); } /** * Check if a sequencer is using the system timer or not. * @param seq Sequencer object * @return TRUE if system timer is being used, FALSE otherwise. * @since 1.1.0 */ int fluid_sequencer_get_use_system_timer (fluid_sequencer_t* seq) { return seq->useSystemTimer ? 1 : 0; } #if FLUID_SEQ_WITH_TRACE /* trace */ void fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...) { va_list args; int len, remain = seq->tracelen - (seq->traceptr - seq->tracebuf); if (remain <= 0) return; va_start (args, fmt); len = vsnprintf(seq->traceptr, remain, fmt, args); va_end (args); if (len > 0) { if (len <= remain) { // all written, with 0 at end seq->traceptr += len; } else { // not enough room, set to end seq->traceptr = seq->tracebuf + seq->tracelen; } } return; } /** * Clear sequencer trace buffer. * @param seq Sequencer object */ void fluid_seq_cleartrace(fluid_sequencer_t* seq) { seq->traceptr = seq->tracebuf; } /** * Get sequencer trace buffer. * @param seq Sequencer object */ char * fluid_seq_gettrace(fluid_sequencer_t* seq) { return seq->tracebuf; } #else void fluid_seq_dotrace(fluid_sequencer_t* seq, char *fmt, ...) {} #endif // FLUID_SEQ_WITH_TRACE /* clients */ /** * Register a sequencer client. * @param seq Sequencer object * @param name Name of sequencer client * @param callback Sequencer client callback or NULL for a source client. * @param data User data to pass to the \a callback * @return Unique sequencer ID or #FLUID_FAILED on error * * Clients can be sources or destinations of events. Sources don't need to * register a callback. */ fluid_seq_id_t fluid_sequencer_register_client (fluid_sequencer_t* seq, const char *name, fluid_event_callback_t callback, void* data) { fluid_sequencer_client_t * client; char * nameCopy; client = FLUID_NEW(fluid_sequencer_client_t); if (client == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } nameCopy = FLUID_STRDUP(name); if (nameCopy == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); FLUID_FREE(client); return FLUID_FAILED; } seq->clientsID++; client->name = nameCopy; client->id = seq->clientsID; client->callback = callback; client->data = data; seq->clients = fluid_list_append(seq->clients, (void *)client); return (client->id); } /** * Unregister a previously registered client. * @param seq Sequencer object * @param id Client ID as returned by fluid_sequencer_register_client(). */ void fluid_sequencer_unregister_client (fluid_sequencer_t* seq, fluid_seq_id_t id) { fluid_list_t *tmp; fluid_event_t* evt; if (seq->clients == NULL) return; evt = new_fluid_event(); if (evt != NULL) { fluid_event_unregistering(evt); fluid_event_set_dest(evt, id); } tmp = seq->clients; while (tmp) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; if (client->id == id) { /* What should we really do if evt is null due to out-of-memory? */ if (client->callback != NULL && evt != NULL) (client->callback)(fluid_sequencer_get_tick(seq), evt, seq, client->data); if (client->name) FLUID_FREE(client->name); seq->clients = fluid_list_remove_link(seq->clients, tmp); delete1_fluid_list(tmp); FLUID_FREE(client); delete_fluid_event(evt); return; } tmp = tmp->next; } delete_fluid_event(evt); return; } /** * Count a sequencers registered clients. * @param seq Sequencer object * @return Count of sequencer clients. */ int fluid_sequencer_count_clients(fluid_sequencer_t* seq) { if (seq->clients == NULL) return 0; return fluid_list_size(seq->clients); } /** * Get a client ID from its index (order in which it was registered). * @param seq Sequencer object * @param index Index of register client * @return Client ID or #FLUID_FAILED if not found */ fluid_seq_id_t fluid_sequencer_get_client_id (fluid_sequencer_t* seq, int index) { fluid_list_t *tmp = fluid_list_nth(seq->clients, index); if (tmp == NULL) { return FLUID_FAILED; } else { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; return client->id; } } /** * Get the name of a registered client. * @param seq Sequencer object * @param id Client ID * @return Client name or NULL if not found. String is internal and should not * be modified or freed. */ char * fluid_sequencer_get_client_name(fluid_sequencer_t* seq, int id) { fluid_list_t *tmp; if (seq->clients == NULL) return NULL; tmp = seq->clients; while (tmp) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; if (client->id == id) return client->name; tmp = tmp->next; } return NULL; } /** * Check if a client is a destination client. * @param seq Sequencer object * @param id Client ID * @return TRUE if client is a destination client, FALSE otherwise or if not found */ int fluid_sequencer_client_is_dest(fluid_sequencer_t* seq, int id) { fluid_list_t *tmp; if (seq->clients == NULL) return FALSE; tmp = seq->clients; while (tmp) { fluid_sequencer_client_t *client = (fluid_sequencer_client_t*)tmp->data; if (client->id == id) return (client->callback != NULL); tmp = tmp->next; } return FALSE; } /** * Send an event immediately. * @param seq Sequencer object * @param evt Event to send (copied) */ /* Event not actually copied, but since its used immediately it virtually is. */ void fluid_sequencer_send_now(fluid_sequencer_t* seq, fluid_event_t* evt) { fluid_seq_id_t destID = fluid_event_get_dest(evt); /* find callback */ fluid_list_t *tmp = seq->clients; while (tmp) { fluid_sequencer_client_t *dest = (fluid_sequencer_client_t*)tmp->data; if (dest->id == destID) { if (dest->callback) (dest->callback)(fluid_sequencer_get_tick(seq), evt, seq, dest->data); return; } tmp = tmp->next; } } /** * Schedule an event for sending at a later time. * @param seq Sequencer object * @param evt Event to send * @param time Time value in ticks (in milliseconds with the default time scale of 1000). * @param absolute TRUE if \a time is absolute sequencer time (time since sequencer * creation), FALSE if relative to current time. * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_sequencer_send_at (fluid_sequencer_t* seq, fluid_event_t* evt, unsigned int time, int absolute) { unsigned int now = fluid_sequencer_get_tick(seq); /* set absolute */ if (!absolute) time = now + time; /* time stamp event */ fluid_event_set_time(evt, time); /* queue for processing later */ return _fluid_seq_queue_pre_insert(seq, evt); } /** * Remove events from the event queue. * @param seq Sequencer object * @param source Source client ID to match or -1 for wildcard * @param dest Destination client ID to match or -1 for wildcard * @param type Event type to match or -1 for wildcard (#fluid_seq_event_type) */ void fluid_sequencer_remove_events (fluid_sequencer_t* seq, fluid_seq_id_t source, fluid_seq_id_t dest, int type) { _fluid_seq_queue_pre_remove(seq, source, dest, type); } /************************************* time **************************************/ /** * Get the current tick of a sequencer. * @param seq Sequencer object * @return Current tick value */ unsigned int fluid_sequencer_get_tick (fluid_sequencer_t* seq) { unsigned int absMs = seq->useSystemTimer ? (int) fluid_curtime() : g_atomic_int_get(&seq->currentMs); double nowFloat; unsigned int now; nowFloat = ((double)(absMs - seq->startMs))*seq->scale/1000.0f; now = nowFloat; return now; } /** * Set the time scale of a sequencer. * @param seq Sequencer object * @param scale Sequencer scale value in ticks per second * (default is 1000 for 1 tick per millisecond, max is 1000.0) * * If there are already scheduled events in the sequencer and the scale is changed * the events are adjusted accordingly. */ void fluid_sequencer_set_time_scale (fluid_sequencer_t* seq, double scale) { if (scale <= 0) { fluid_log(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); return; } if (scale > 1000.0) // Otherwise : problems with the timer = 0ms... scale = 1000.0; if (seq->scale != scale) { double oldScale = seq->scale; // stop timer if (seq->timer) { delete_fluid_timer(seq->timer); seq->timer = NULL; } seq->scale = scale; // change start0 so that cellNb is preserved seq->queue0StartTime = (seq->queue0StartTime + seq->prevCellNb)*(seq->scale/oldScale) - seq->prevCellNb; // change all preQueue events for new scale { fluid_evt_entry* tmp; tmp = seq->preQueue; while (tmp) { if (tmp->entryType == FLUID_EVT_ENTRY_INSERT) tmp->evt.time = tmp->evt.time*seq->scale/oldScale; tmp = tmp->next; } } /* re-start timer */ if (seq->useSystemTimer) { seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, TRUE, FALSE, TRUE); } } } /** * Get a sequencer's time scale. * @param seq Sequencer object. * @return Time scale value in ticks per second. */ double fluid_sequencer_get_time_scale(fluid_sequencer_t* seq) { return seq->scale; } /********************** the queue **********************/ /* The queue stores all future events to be processed. Data structures There is a heap, allocated at init time, for managing a pool of event entries, that is description of an event, its time, and whether it is a normal event or a removal command. The queue is separated in two arrays, and a list. The first array 'queue0' corresponds to the events to be sent in the next 256 ticks (0 to 255), the second array 'queue1' contains the events to be send from now+256 to now+65535. The list called 'queueLater' contains the events to be sent later than that. In each array, one cell contains a list of events having the same time (in the queue0 array), or the same time/256 (in the queue1 array), and a pointer to the last event in the list of the cell so as to be able to insert fast at the end of the list (i.e. a cell = 2 pointers). The 'queueLater' list is ordered by time and by post time. This way, inserting 'soon' events is fast (below 65535 ticks, that is about 1 minute if 1 tick=1ms). Inserting later events is more slow, but this is a realtime engine, isn't it ? The queue0 starts at queue0StartTime. When 256 ticks have elapsed, the queue0 array is emptied, and the first cell of the queue1 array is expanded in the queue0 array, according to the time of each event. The queue1 array is shifted to the left, and the first events of the queueLater list are inserte in the last cell of the queue1 array. We remember the previously managed cell in queue0 in the prevCellNb variable. When processing the current cell, we process the events in between (late events). Functions The main thread functions first get an event entry from the heap, and copy the given event into it, then merely enqueue it in a preQueue. This is in order to protect the data structure: everything is managed in the callback (thread or interrupt, depending on the architecture). All queue data structure management is done in a timer callback: '_fluid_seq_queue_process'. The _fluid_seq_queue_process function first process the preQueue, inserting or removing event entrys from the queue, then processes the queue, by sending events ready to be sent at the current time. Critical sections between the main thread (or app) and the sequencer thread (or interrupt) are: - the heap management (if two threads get a free event at the same time) - the preQueue access. These are really small and fast sections (merely a pointer or two changing value). They are not protected by a mutex for now (August 2002). Waiting for crossplatform mutex solutions. When changing this code, beware that the _fluid_seq_queue_pre_insert function may be called by the callback of the queue thread (ex : a note event inserts a noteoff event). */ /********************/ /* API */ /********************/ static short _fluid_seq_queue_init(fluid_sequencer_t* seq, int maxEvents) { seq->heap = _fluid_evt_heap_init(maxEvents); if (seq->heap == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return -1; } seq->preQueue = NULL; seq->preQueueLast = NULL; FLUID_MEMSET(seq->queue0, 0, 2*256*sizeof(fluid_evt_entry *)); FLUID_MEMSET(seq->queue1, 0, 2*255*sizeof(fluid_evt_entry *)); seq->queueLater = NULL; seq->queue0StartTime = fluid_sequencer_get_tick(seq); seq->prevCellNb = -1; fluid_mutex_init(seq->mutex); /* start timer */ if (seq->useSystemTimer) { seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, TRUE, FALSE, TRUE); } return (0); } static void _fluid_seq_queue_end(fluid_sequencer_t* seq) { int i; /* free all remaining events */ _fluid_free_evt_queue(&seq->preQueue, &seq->preQueueLast); for (i = 0; i < 256; i++) _fluid_free_evt_queue(&(seq->queue0[i][0]), &(seq->queue0[i][1])); for (i = 0; i < 255; i++) _fluid_free_evt_queue(&(seq->queue1[i][0]), &(seq->queue1[i][1])); _fluid_free_evt_queue(&seq->queueLater, NULL); if (seq->timer) { delete_fluid_timer(seq->timer); seq->timer = NULL; } if (seq->heap) { _fluid_evt_heap_free(seq->heap); seq->heap = NULL; } fluid_mutex_destroy(seq->mutex); } /********************/ /* queue management */ /********************/ /* Create event_entry and append to the preQueue. * May be called from the main thread (usually) but also recursively * from the queue thread, when a callback itself does an insert... */ static short _fluid_seq_queue_pre_insert(fluid_sequencer_t* seq, fluid_event_t * evt) { fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap); if (evtentry == NULL) { /* should not happen */ fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); return -1; } evtentry->next = NULL; evtentry->entryType = FLUID_EVT_ENTRY_INSERT; FLUID_MEMCPY(&(evtentry->evt), evt, sizeof(fluid_event_t)); fluid_mutex_lock(seq->mutex); /* append to preQueue */ if (seq->preQueueLast) { seq->preQueueLast->next = evtentry; } else { seq->preQueue = evtentry; } seq->preQueueLast = evtentry; fluid_mutex_unlock(seq->mutex); return (0); } /* Create event_entry and append to the preQueue. * May be called from the main thread (usually) but also recursively * from the queue thread, when a callback itself does an insert... */ static void _fluid_seq_queue_pre_remove(fluid_sequencer_t* seq, fluid_seq_id_t src, fluid_seq_id_t dest, int type) { fluid_evt_entry * evtentry = _fluid_seq_heap_get_free(seq->heap); if (evtentry == NULL) { /* should not happen */ fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); return; } evtentry->next = NULL; evtentry->entryType = FLUID_EVT_ENTRY_REMOVE; { fluid_event_t* evt = &(evtentry->evt); fluid_event_set_source(evt, src); fluid_event_set_source(evt, src); fluid_event_set_dest(evt, dest); evt->type = type; } fluid_mutex_lock(seq->mutex); /* append to preQueue */ if (seq->preQueueLast) { seq->preQueueLast->next = evtentry; } else { seq->preQueue = evtentry; } seq->preQueueLast = evtentry; fluid_mutex_unlock(seq->mutex); return; } static void _fluid_free_evt_queue(fluid_evt_entry** first, fluid_evt_entry** last) { fluid_evt_entry* tmp2; fluid_evt_entry* tmp = *first; while (tmp != NULL) { tmp2 = tmp->next; FLUID_FREE(tmp); tmp = tmp2; } *first = NULL; if (last != NULL) { *last = NULL; } } /* Callback from timer (may be in a different thread, or in an interrupt) */ static int _fluid_seq_queue_process(void* data, unsigned int msec) { fluid_sequencer_t* seq = (fluid_sequencer_t *)data; fluid_sequencer_process(seq, msec); /* continue timer */ return 1; } /** * Advance a sequencer that isn't using the system timer. * @param seq Sequencer object * @param msec Time to advance sequencer to (absolute time since sequencer start). * @since 1.1.0 */ void fluid_sequencer_process(fluid_sequencer_t* seq, unsigned int msec) { /* process prequeue */ fluid_evt_entry* tmp; fluid_evt_entry* next; fluid_mutex_lock(seq->mutex); /* get the preQueue */ tmp = seq->preQueue; seq->preQueue = NULL; seq->preQueueLast = NULL; fluid_mutex_unlock(seq->mutex); /* walk all the preQueue and process them in order : inserts and removes */ while (tmp) { next = tmp->next; if (tmp->entryType == FLUID_EVT_ENTRY_REMOVE) { _fluid_seq_queue_remove_entries_matching(seq, tmp); } else { _fluid_seq_queue_insert_entry(seq, tmp); } tmp = next; } /* send queued events */ g_atomic_int_set(&seq->currentMs, msec); _fluid_seq_queue_send_queued_events(seq); } #if 0 static void _fluid_seq_queue_print_later(fluid_sequencer_t* seq) { int count = 0; fluid_evt_entry* tmp = seq->queueLater; printf("queueLater:\n"); while (tmp) { unsigned int delay = tmp->evt.time - seq->queue0StartTime; printf("queueLater: Delay = %i\n", delay); tmp = tmp->next; count++; } printf("queueLater: Total of %i events\n", count); } #endif static void _fluid_seq_queue_insert_queue0(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell) { if (seq->queue0[cell][1] == NULL) { seq->queue0[cell][1] = seq->queue0[cell][0] = tmp; } else { seq->queue0[cell][1]->next = tmp; seq->queue0[cell][1] = tmp; } tmp->next = NULL; } static void _fluid_seq_queue_insert_queue1(fluid_sequencer_t* seq, fluid_evt_entry* tmp, int cell) { if (seq->queue1[cell][1] == NULL) { seq->queue1[cell][1] = seq->queue1[cell][0] = tmp; } else { seq->queue1[cell][1]->next = tmp; seq->queue1[cell][1] = tmp; } tmp->next = NULL; } static void _fluid_seq_queue_insert_queue_later(fluid_sequencer_t* seq, fluid_evt_entry* evtentry) { fluid_evt_entry* prev; fluid_evt_entry* tmp; unsigned int time = evtentry->evt.time; /* insert in 'queueLater', after the ones that have the same * time */ /* first? */ if ((seq->queueLater == NULL) || (seq->queueLater->evt.time > time)) { evtentry->next = seq->queueLater; seq->queueLater = evtentry; return; } /* walk queueLater */ /* this is the only slow thing : if the event is more than 65535 ticks after the current time */ prev = seq->queueLater; tmp = prev->next; while (tmp) { if (tmp->evt.time > time) { /* insert before tmp */ evtentry->next = tmp; prev->next = evtentry; return; } prev = tmp; tmp = prev->next; } /* last */ evtentry->next = NULL; prev->next = evtentry; } static void _fluid_seq_queue_insert_entry(fluid_sequencer_t* seq, fluid_evt_entry * evtentry) { /* time is relative to seq origin, in ticks */ fluid_event_t * evt = &(evtentry->evt); unsigned int time = evt->time; unsigned int delay; if (seq->queue0StartTime > 0) { /* queue0StartTime could be < 0 if the scale changed a lot early, breaking the following comparison */ if (time < (unsigned int)seq->queue0StartTime) { /* we are late, send now */ fluid_sequencer_send_now(seq, evt); _fluid_seq_heap_set_free(seq->heap, evtentry); return; } } if (seq->prevCellNb >= 0) { /* prevCellNb could be -1 is seq was just started - unlikely */ /* prevCellNb can also be -1 if cellNb was reset to 0 in _fluid_seq_queue_send_queued_events() */ if (time <= (unsigned int)(seq->queue0StartTime + seq->prevCellNb)) { /* we are late, send now */ fluid_sequencer_send_now(seq, evt); _fluid_seq_heap_set_free(seq->heap, evtentry); return; } } delay = time - seq->queue0StartTime; if (delay > 65535) { _fluid_seq_queue_insert_queue_later(seq, evtentry); } else if (delay > 255) { _fluid_seq_queue_insert_queue1(seq, evtentry, delay/256 - 1); } else { _fluid_seq_queue_insert_queue0(seq, evtentry, delay); } } static int _fluid_seq_queue_matchevent(fluid_event_t* evt, int templType, fluid_seq_id_t templSrc, fluid_seq_id_t templDest) { int eventType; if (templSrc != -1 && templSrc != fluid_event_get_source(evt)) return 0; if (templDest != -1 && templDest != fluid_event_get_dest(evt)) return 0; if (templType == -1) return 1; eventType = fluid_event_get_type(evt); if (templType == eventType) return 1; if (templType == FLUID_SEQ_ANYCONTROLCHANGE) if (eventType == FLUID_SEQ_PITCHBEND || eventType == FLUID_SEQ_MODULATION || eventType == FLUID_SEQ_SUSTAIN || eventType == FLUID_SEQ_PAN || eventType == FLUID_SEQ_VOLUME || eventType == FLUID_SEQ_REVERBSEND || eventType == FLUID_SEQ_CONTROLCHANGE || eventType == FLUID_SEQ_CHORUSSEND) return 1; return 0; } static void _fluid_seq_queue_remove_entries_matching(fluid_sequencer_t* seq, fluid_evt_entry* templ) { /* we walk everything : this is slow, but that is life */ int i, type; fluid_seq_id_t src, dest; src = templ->evt.src; dest = templ->evt.dest; type = templ->evt.type; /* we can set it free now */ _fluid_seq_heap_set_free(seq->heap, templ); /* queue0 */ for (i = 0 ; i < 256 ; i++) { fluid_evt_entry* tmp = seq->queue0[i][0]; fluid_evt_entry* prev = NULL; while (tmp) { /* remove and/or walk */ if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { /* remove */ if (prev) { prev->next = tmp->next; if (tmp == seq->queue0[i][1]) // last one in list seq->queue0[i][1] = prev; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = prev->next; } else { /* first one in list */ seq->queue0[i][0] = tmp->next; if (tmp == seq->queue0[i][1]) // last one in list seq->queue0[i][1] = NULL; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = seq->queue0[i][0]; } } else { prev = tmp; tmp = prev->next; } } } /* queue1 */ for (i = 0 ; i < 255 ; i++) { fluid_evt_entry* tmp = seq->queue1[i][0]; fluid_evt_entry* prev = NULL; while (tmp) { if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { /* remove */ if (prev) { prev->next = tmp->next; if (tmp == seq->queue1[i][1]) // last one in list seq->queue1[i][1] = prev; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = prev->next; } else { /* first one in list */ seq->queue1[i][0] = tmp->next; if (tmp == seq->queue1[i][1]) // last one in list seq->queue1[i][1] = NULL; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = seq->queue1[i][0]; } } else { prev = tmp; tmp = prev->next; } } } /* queueLater */ { fluid_evt_entry* tmp = seq->queueLater; fluid_evt_entry* prev = NULL; while (tmp) { if (_fluid_seq_queue_matchevent((&tmp->evt), type, src, dest)) { /* remove */ if (prev) { prev->next = tmp->next; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = prev->next; } else { seq->queueLater = tmp->next; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = seq->queueLater; } } else { prev = tmp; tmp = prev->next; } } } } static void _fluid_seq_queue_send_cell_events(fluid_sequencer_t* seq, int cellNb) { fluid_evt_entry* next; fluid_evt_entry* tmp; tmp = seq->queue0[cellNb][0]; while (tmp) { fluid_sequencer_send_now(seq, &(tmp->evt)); next = tmp->next; _fluid_seq_heap_set_free(seq->heap, tmp); tmp = next; } seq->queue0[cellNb][0] = NULL; seq->queue0[cellNb][1] = NULL; } static void _fluid_seq_queue_slide(fluid_sequencer_t* seq) { short i; fluid_evt_entry* next; fluid_evt_entry* tmp; int count = 0; /* do the slide */ seq->queue0StartTime += 256; /* sort all queue1[0] into queue0 according to new queue0StartTime */ tmp = seq->queue1[0][0]; while (tmp) { unsigned int delay = tmp->evt.time - seq->queue0StartTime; next = tmp->next; if (delay > 255) { /* should not happen !! */ /* append it to queue1[1] */ _fluid_seq_queue_insert_queue1(seq, tmp, 1); } else { _fluid_seq_queue_insert_queue0(seq, tmp, delay); } tmp = next; count++; } /* slide all queue1[i] into queue1[i-1] */ for (i = 1 ; i < 255 ; i++) { seq->queue1[i-1][0] = seq->queue1[i][0]; seq->queue1[i-1][1] = seq->queue1[i][1]; } seq->queue1[254][0] = NULL; seq->queue1[254][1] = NULL; /* append queueLater to queue1[254] */ count = 0; tmp = seq->queueLater; while (tmp) { unsigned int delay = tmp->evt.time - seq->queue0StartTime; if (delay > 65535) { break; } next = tmp->next; /* append it */ _fluid_seq_queue_insert_queue1(seq, tmp, 254); tmp = next; count++; } seq->queueLater = tmp; } static void _fluid_seq_queue_send_queued_events(fluid_sequencer_t* seq) { unsigned int nowTicks = fluid_sequencer_get_tick(seq); short cellNb; cellNb = seq->prevCellNb + 1; while (cellNb <= (int)(nowTicks - seq->queue0StartTime)) { if (cellNb == 256) { cellNb = 0; _fluid_seq_queue_slide(seq); } /* slide */ /* process queue0[cellNb] */ _fluid_seq_queue_send_cell_events(seq, cellNb); /* the current scale may have changed through a callback event */ nowTicks = fluid_sequencer_get_tick(seq); /* next cell */ cellNb++; } seq->prevCellNb = cellNb - 1; } fluidsynth-1.1.9/src/midi/fluid_seqbind.c000066400000000000000000000225361322272076000204110ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* 2002 : API design by Peter Hanappe and Antoine Schmitt August 2002 : Implementation by Antoine Schmitt as@gratin.org as part of the infiniteCD author project http://www.infiniteCD.org/ */ #include "fluidsynth_priv.h" #include "fluid_synth.h" #include "fluid_midi.h" #include "fluid_event_priv.h" /*************************************************************** * * SEQUENCER BINDING */ struct _fluid_seqbind_t { fluid_synth_t* synth; fluid_sequencer_t* seq; fluid_sample_timer_t* sample_timer; fluid_seq_id_t client_id; }; typedef struct _fluid_seqbind_t fluid_seqbind_t; int fluid_seqbind_timer_callback(void* data, unsigned int msec); void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data); /* Proper cleanup of the seqbind struct. */ void delete_fluid_seqbind(fluid_seqbind_t* seqbind) { if (seqbind == NULL) { return; } if ((seqbind->client_id != -1) && (seqbind->seq != NULL)) { fluid_sequencer_unregister_client(seqbind->seq, seqbind->client_id); seqbind->client_id = -1; } if ((seqbind->sample_timer != NULL) && (seqbind->synth != NULL)) { delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); seqbind->sample_timer = NULL; } FLUID_FREE(seqbind); } /** * Registers a synthesizer as a destination client of the given sequencer. * The \a synth is registered with the name "fluidsynth". * @param seq Sequencer instance * @param synth Synthesizer instance * @returns Sequencer client ID, or #FLUID_FAILED on error. */ fluid_seq_id_t fluid_sequencer_register_fluidsynth (fluid_sequencer_t* seq, fluid_synth_t* synth) { fluid_seqbind_t* seqbind; seqbind = FLUID_NEW(fluid_seqbind_t); if (seqbind == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } seqbind->synth = synth; seqbind->seq = seq; seqbind->sample_timer = NULL; seqbind->client_id = -1; /* set up the sample timer */ if (!fluid_sequencer_get_use_system_timer(seq)) { seqbind->sample_timer = new_fluid_sample_timer(synth, fluid_seqbind_timer_callback, (void *) seqbind); if (seqbind->sample_timer == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); delete_fluid_seqbind(seqbind); return FLUID_FAILED; } } /* register fluidsynth itself */ seqbind->client_id = fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)seqbind); if (seqbind->client_id == -1) { delete_fluid_seqbind(seqbind); return FLUID_FAILED; } return seqbind->client_id; } /* Callback for sample timer */ int fluid_seqbind_timer_callback(void* data, unsigned int msec) { fluid_seqbind_t* seqbind = (fluid_seqbind_t *) data; fluid_sequencer_process(seqbind->seq, msec); return 1; } /* Callback for midi events */ void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_sequencer_t* seq, void* data) { fluid_synth_t* synth; fluid_seqbind_t* seqbind = (fluid_seqbind_t *) data; synth = seqbind->synth; switch (fluid_event_get_type(evt)) { case FLUID_SEQ_NOTEON: fluid_synth_noteon(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_velocity(evt)); break; case FLUID_SEQ_NOTEOFF: fluid_synth_noteoff(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt)); break; case FLUID_SEQ_NOTE: { unsigned int dur; fluid_synth_noteon(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_velocity(evt)); dur = fluid_event_get_duration(evt); fluid_event_noteoff(evt, fluid_event_get_channel(evt), fluid_event_get_key(evt)); fluid_sequencer_send_at(seq, evt, dur, 0); } break; case FLUID_SEQ_ALLSOUNDSOFF: fluid_synth_all_sounds_off(synth, fluid_event_get_channel(evt)); break; case FLUID_SEQ_ALLNOTESOFF: fluid_synth_cc(synth, fluid_event_get_channel(evt), 0x7B, 0); break; case FLUID_SEQ_BANKSELECT: fluid_synth_bank_select(synth, fluid_event_get_channel(evt), fluid_event_get_bank(evt)); break; case FLUID_SEQ_PROGRAMCHANGE: fluid_synth_program_change(synth, fluid_event_get_channel(evt), fluid_event_get_program(evt)); break; case FLUID_SEQ_PROGRAMSELECT: fluid_synth_program_select(synth, fluid_event_get_channel(evt), fluid_event_get_sfont_id(evt), fluid_event_get_bank(evt), fluid_event_get_program(evt)); break; case FLUID_SEQ_ANYCONTROLCHANGE: /* nothing = only used by remove_events */ break; case FLUID_SEQ_PITCHBEND: fluid_synth_pitch_bend(synth, fluid_event_get_channel(evt), fluid_event_get_pitch(evt)); break; case FLUID_SEQ_PITCHWHHELSENS: fluid_synth_pitch_wheel_sens(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt)); break; case FLUID_SEQ_CONTROLCHANGE: fluid_synth_cc(synth, fluid_event_get_channel(evt), fluid_event_get_control(evt), fluid_event_get_value(evt)); break; case FLUID_SEQ_MODULATION: { short ctrl = 0x01; // MODULATION_MSB fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_SUSTAIN: { short ctrl = 0x40; // SUSTAIN_SWITCH fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_PAN: { short ctrl = 0x0A; // PAN_MSB fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_VOLUME: { short ctrl = 0x07; // VOLUME_MSB fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_REVERBSEND: { short ctrl = 0x5B; // EFFECTS_DEPTH1 fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_CHORUSSEND: { short ctrl = 0x5D; // EFFECTS_DEPTH3 fluid_synth_cc(synth, fluid_event_get_channel(evt), ctrl, fluid_event_get_value(evt)); } break; case FLUID_SEQ_CHANNELPRESSURE: { fluid_synth_channel_pressure(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt)); } break; case FLUID_SEQ_SYSTEMRESET: { fluid_synth_system_reset(synth); } break; case FLUID_SEQ_UNREGISTERING: /* free ourselves */ { seqbind->client_id = -1; /* avoid recursive call to fluid_sequencer_unregister_client */ delete_fluid_seqbind(seqbind); } break; case FLUID_SEQ_TIMER: /* nothing in fluidsynth */ break; default: break; } } static fluid_seq_id_t get_fluidsynth_dest(fluid_sequencer_t* seq) { int i; fluid_seq_id_t id; char* name; int j = fluid_sequencer_count_clients(seq); for (i = 0; i < j; i++) { id = fluid_sequencer_get_client_id(seq, i); name = fluid_sequencer_get_client_name(seq, id); if (name && (strcmp(name, "fluidsynth") == 0)) { return id; } } return -1; } /** * Transforms an incoming midi event (from a midi driver or midi router) to a * sequencer event and adds it to the sequencer queue for sending as soon as possible. * @param data The sequencer, must be a valid #fluid_sequencer_t * @param event MIDI event * @return #FLUID_OK or #FLUID_FAILED * @since 1.1.0 */ int fluid_sequencer_add_midi_event_to_buffer(void* data, fluid_midi_event_t* event) { fluid_event_t evt; fluid_sequencer_t* seq = (fluid_sequencer_t*) data; int chan = fluid_midi_event_get_channel(event); fluid_event_clear(&evt); fluid_event_set_time(&evt, fluid_sequencer_get_tick(seq)); fluid_event_set_dest(&evt, get_fluidsynth_dest(seq)); switch (fluid_midi_event_get_type(event)) { case NOTE_OFF: fluid_event_noteoff(&evt, chan, (short)fluid_midi_event_get_key(event)); break; case NOTE_ON: fluid_event_noteon(&evt, fluid_midi_event_get_channel(event), (short)fluid_midi_event_get_key(event), (short)fluid_midi_event_get_velocity(event)); break; case CONTROL_CHANGE: fluid_event_control_change(&evt, chan, (short)fluid_midi_event_get_control(event), (short)fluid_midi_event_get_value(event)); break; case PROGRAM_CHANGE: fluid_event_program_change(&evt, chan, (short)fluid_midi_event_get_program(event)); break; case PITCH_BEND: fluid_event_pitch_bend(&evt, chan, fluid_midi_event_get_pitch(event)); break; case CHANNEL_PRESSURE: fluid_event_channel_pressure(&evt, chan, (short)fluid_midi_event_get_program(event)); break; case MIDI_SYSTEM_RESET: fluid_event_system_reset(&evt); break; default: /* Not yet implemented */ return FLUID_FAILED; } /* Schedule for sending at next call to fluid_sequencer_process */ return fluid_sequencer_send_at(seq, &evt, 0, 0); } fluidsynth-1.1.9/src/rvoice/000077500000000000000000000000001322272076000157725ustar00rootroot00000000000000fluidsynth-1.1.9/src/rvoice/fluid_adsr_env.c000066400000000000000000000025531322272076000211270ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_adsr_env.h" void fluid_adsr_env_set_data(fluid_adsr_env_t* env, fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max) { env->data[section].count = count; env->data[section].coeff = coeff; env->data[section].increment = increment; env->data[section].min = min; env->data[section].max = max; } fluidsynth-1.1.9/src/rvoice/fluid_adsr_env.h000066400000000000000000000100511322272076000211240ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_ADSR_ENVELOPE_H #define _FLUID_ADSR_ENVELOPE_H #include "fluidsynth_priv.h" #include "fluid_sys.h" /* * envelope data */ struct _fluid_env_data_t { unsigned int count; fluid_real_t coeff; fluid_real_t increment; fluid_real_t min; fluid_real_t max; }; /* Indices for envelope tables */ enum fluid_voice_envelope_index_t{ FLUID_VOICE_ENVDELAY, FLUID_VOICE_ENVATTACK, FLUID_VOICE_ENVHOLD, FLUID_VOICE_ENVDECAY, FLUID_VOICE_ENVSUSTAIN, FLUID_VOICE_ENVRELEASE, FLUID_VOICE_ENVFINISHED, FLUID_VOICE_ENVLAST }; typedef enum fluid_voice_envelope_index_t fluid_adsr_env_section_t; typedef struct _fluid_adsr_env_t fluid_adsr_env_t; struct _fluid_adsr_env_t { fluid_env_data_t data[FLUID_VOICE_ENVLAST]; unsigned int count; int section; fluid_real_t val; /* the current value of the envelope */ }; /* For performance, all functions are inlined */ static FLUID_INLINE void fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv) { fluid_env_data_t* env_data; fluid_real_t x; env_data = &env->data[env->section]; /* skip to the next section of the envelope if necessary */ while (env->count >= env_data->count) { // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH] if (env->section == FLUID_VOICE_ENVDECAY && is_volenv) env->val = env_data->min * env_data->coeff; env_data = &env->data[++env->section]; env->count = 0; } /* calculate the envelope value and check for valid range */ x = env_data->coeff * env->val + env_data->increment; if (x < env_data->min) { x = env_data->min; env->section++; env->count = 0; } else if (x > env_data->max) { x = env_data->max; env->section++; env->count = 0; } env->val = x; env->count++; } /* This one cannot be inlined since it is referenced in the event queue */ void fluid_adsr_env_set_data(fluid_adsr_env_t* env, fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max); static inline void fluid_adsr_env_reset(fluid_adsr_env_t* env) { env->count = 0; env->section = 0; env->val = 0.0f; } static inline fluid_real_t fluid_adsr_env_get_val(fluid_adsr_env_t* env) { return env->val; } static inline void fluid_adsr_env_set_val(fluid_adsr_env_t* env, fluid_real_t val) { env->val = val; } static inline fluid_adsr_env_section_t fluid_adsr_env_get_section(fluid_adsr_env_t* env) { return env->section; } static inline void fluid_adsr_env_set_section(fluid_adsr_env_t* env, fluid_adsr_env_section_t section) { env->section = section; env->count = 0; } /* Used for determining which voice to kill. Returns max amplitude from now, and forward in time. */ static inline fluid_real_t fluid_adsr_env_get_max_val(fluid_adsr_env_t* env) { if (env->section > FLUID_VOICE_ENVATTACK){ return env->val * 1000; } else { return env->data[FLUID_VOICE_ENVATTACK].max; } } #endif fluidsynth-1.1.9/src/rvoice/fluid_chorus.c000066400000000000000000000406001322272076000206240ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe, Markus Nentwig and others. * * 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 */ /* based on a chrous implementation made by Juergen Mueller And Sundry Contributors in 1998 CHANGES - Adapted for fluidsynth, Peter Hanappe, March 2002 - Variable delay line implementation using bandlimited interpolation, code reorganization: Markus Nentwig May 2002 */ /* * Chorus effect. * * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): * * * gain-in ___ * ibuff -----+--------------------------------------------->| | * | _________ | | * | | | * level 1 | | * +---->| delay 1 |----------------------------->| | * | |_________| | | * | /|\ | | * : | | | * : +-----------------+ +--------------+ | + | * : | Delay control 1 |<--| mod. speed 1 | | | * : +-----------------+ +--------------+ | | * | _________ | | * | | | * level n | | * +---->| delay n |----------------------------->| | * |_________| | | * /|\ |___| * | | * +-----------------+ +--------------+ | * gain-out * | Delay control n |<--| mod. speed n | | * +-----------------+ +--------------+ +----->obuff * * * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). * * The delay of each block is modulated between 0..depth ms * */ /* Variable delay line implementation * ================================== * * The modulated delay needs the value of the delayed signal between * samples. A lowpass filter is used to obtain intermediate values * between samples (bandlimited interpolation). The sample pulse * train is convoluted with the impulse response of the low pass * filter (sinc function). To make it work with a small number of * samples, the sinc function is windowed (Hamming window). * */ #include "fluid_chorus.h" #include "fluid_sys.h" #define MAX_CHORUS 99 #define MAX_DELAY 100 #define MAX_DEPTH 10 #define MIN_SPEED_HZ 0.29 #define MAX_SPEED_HZ 5 /* Length of one delay line in samples: * Set through MAX_SAMPLES_LN2. * For example: * MAX_SAMPLES_LN2=12 * => MAX_SAMPLES=pow(2,12)=4096 * => MAX_SAMPLES_ANDMASK=4095 */ #define MAX_SAMPLES_LN2 12 #define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1)) #define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1) /* Interpolate how many steps between samples? Must be power of two For example: 8 => use a resolution of 256 steps between any two samples */ #define INTERPOLATION_SUBSAMPLES_LN2 8 #define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1)) #define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1) /* Use how many samples for interpolation? Must be odd. '7' sounds relatively clean, when listening to the modulated delay signal alone. For a demo on aliasing try '1' With '3', the aliasing is still quite pronounced for some input frequencies */ #define INTERPOLATION_SAMPLES 5 /* Private data for SKEL file */ struct _fluid_chorus_t { int type; fluid_real_t depth_ms; fluid_real_t level; fluid_real_t speed_Hz; int number_blocks; fluid_real_t *chorusbuf; int counter; long phase[MAX_CHORUS]; long modulation_period_samples; int *lookup_tab; fluid_real_t sample_rate; /* sinc lookup table */ fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES]; }; static void fluid_chorus_triangle(int *buf, int len, int depth); static void fluid_chorus_sine(int *buf, int len, int depth); fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate) { int i; int ii; fluid_chorus_t* chorus; chorus = FLUID_NEW(fluid_chorus_t); if (chorus == NULL) { fluid_log(FLUID_PANIC, "chorus: Out of memory"); return NULL; } FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); chorus->sample_rate = sample_rate; /* Lookup table for the SI function (impulse response of an ideal low pass) */ /* i: Offset in terms of whole samples */ for (i = 0; i < INTERPOLATION_SAMPLES; i++){ /* ii: Offset in terms of fractional samples ('subsamples') */ for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){ /* Move the origin into the center of the table */ double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2. + (double) ii / (double) INTERPOLATION_SUBSAMPLES); if (fabs(i_shifted) < 0.000001) { /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ chorus->sinc_table[i][ii] = (fluid_real_t)1.; } else { chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted); /* Hamming window */ chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES)); }; }; }; /* allocate lookup tables */ chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ)); if (chorus->lookup_tab == NULL) { fluid_log(FLUID_PANIC, "chorus: Out of memory"); goto error_recovery; } /* allocate sample buffer */ chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES); if (chorus->chorusbuf == NULL) { fluid_log(FLUID_PANIC, "chorus: Out of memory"); goto error_recovery; } if (fluid_chorus_init(chorus) != FLUID_OK){ goto error_recovery; }; return chorus; error_recovery: delete_fluid_chorus(chorus); return NULL; } void delete_fluid_chorus(fluid_chorus_t* chorus) { if (chorus == NULL) { return; } if (chorus->chorusbuf != NULL) { FLUID_FREE(chorus->chorusbuf); } if (chorus->lookup_tab != NULL) { FLUID_FREE(chorus->lookup_tab); } FLUID_FREE(chorus); } int fluid_chorus_init(fluid_chorus_t* chorus) { int i; for (i = 0; i < MAX_SAMPLES; i++) { chorus->chorusbuf[i] = 0.0; } /* initialize the chorus with the default settings */ fluid_chorus_set (chorus, FLUID_CHORUS_SET_ALL, FLUID_CHORUS_DEFAULT_N, FLUID_CHORUS_DEFAULT_LEVEL, FLUID_CHORUS_DEFAULT_SPEED, FLUID_CHORUS_DEFAULT_DEPTH, FLUID_CHORUS_MOD_SINE); return FLUID_OK; } void fluid_chorus_reset(fluid_chorus_t* chorus) { fluid_chorus_init(chorus); } /** * Set one or more chorus parameters. * @param chorus Chorus instance * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t) * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) * @param level Chorus level (0.0-10.0) * @param speed Chorus speed in Hz (0.29-5.0) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) */ void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, float speed, float depth_ms, int type) { int modulation_depth_samples; int i; if (set & FLUID_CHORUS_SET_NR) chorus->number_blocks = nr; if (set & FLUID_CHORUS_SET_LEVEL) chorus->level = level; if (set & FLUID_CHORUS_SET_SPEED) chorus->speed_Hz = speed; if (set & FLUID_CHORUS_SET_DEPTH) chorus->depth_ms = depth_ms; if (set & FLUID_CHORUS_SET_TYPE) chorus->type = type; if (chorus->number_blocks < 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.", 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.", (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.", (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."); chorus->depth_ms = 0.0; } /* Depth: Check for too high value through modulation_depth_samples. */ if (chorus->level < 0.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! " "Setting it to 0.1."); chorus->level = 0.1; } /* The modulating LFO goes through a full period every x samples: */ chorus->modulation_period_samples = chorus->sample_rate / chorus->speed_Hz; /* The variation in delay time is x: */ modulation_depth_samples = (int) (chorus->depth_ms / 1000.0 /* convert modulation depth in ms to s*/ * chorus->sample_rate); if (modulation_depth_samples > MAX_SAMPLES) { fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); modulation_depth_samples = MAX_SAMPLES; } /* initialize LFO table */ if (chorus->type == FLUID_CHORUS_MOD_SINE) { fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, modulation_depth_samples); } else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) { fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples, modulation_depth_samples); } else { fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); chorus->type = FLUID_CHORUS_MOD_SINE; fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples, modulation_depth_samples); } for (i = 0; i < chorus->number_blocks; i++) { /* Set the phase of the chorus blocks equally spaced */ chorus->phase[i] = (int) ((double) chorus->modulation_period_samples * (double) i / (double) chorus->number_blocks); } /* Start of the circular buffer */ chorus->counter = 0; } void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; int i; fluid_real_t d_in, d_out; for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { d_in = in[sample_index]; d_out = 0.0f; # if 0 /* Debug: Listen to the chorus signal only */ left_out[sample_index]=0; right_out[sample_index]=0; #endif /* Write the current sample into the circular buffer */ chorus->chorusbuf[chorus->counter] = d_in; for (i = 0; i < chorus->number_blocks; i++) { int ii; /* Calculate the delay in subsamples for the delay line of chorus block nr. */ /* The value in the lookup table is so, that this expression * will always be positive. It will always include a number of * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to * remain positive at all times. */ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter - chorus->lookup_tab[chorus->phase[i]]); int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES; /* modulo divide by INTERPOLATION_SUBSAMPLES */ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ /* Add the delayed signal to the chorus sum d_out Note: The * delay in the delay line moves backwards for increasing * delay!*/ /* The & in chorusbuf[...] is equivalent to a division modulo MAX_SAMPLES, only faster. */ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] * chorus->sinc_table[ii][pos_subsamples]; pos_samples--; }; /* Cycle the phase of the modulating LFO */ chorus->phase[i]++; chorus->phase[i] %= (chorus->modulation_period_samples); } /* foreach chorus block */ d_out *= chorus->level; /* Add the chorus sum d_out to output */ left_out[sample_index] += d_out; right_out[sample_index] += d_out; /* Move forward in circular buffer */ chorus->counter++; chorus->counter %= MAX_SAMPLES; } /* foreach sample */ } /* Duplication of code ... (replaces sample data instead of mixing) */ void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; int i; fluid_real_t d_in, d_out; for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) { d_in = in[sample_index]; d_out = 0.0f; # if 0 /* Debug: Listen to the chorus signal only */ left_out[sample_index]=0; right_out[sample_index]=0; #endif /* Write the current sample into the circular buffer */ chorus->chorusbuf[chorus->counter] = d_in; for (i = 0; i < chorus->number_blocks; i++) { int ii; /* Calculate the delay in subsamples for the delay line of chorus block nr. */ /* The value in the lookup table is so, that this expression * will always be positive. It will always include a number of * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to * remain positive at all times. */ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter - chorus->lookup_tab[chorus->phase[i]]); int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES; /* modulo divide by INTERPOLATION_SUBSAMPLES */ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK; for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){ /* Add the delayed signal to the chorus sum d_out Note: The * delay in the delay line moves backwards for increasing * delay!*/ /* The & in chorusbuf[...] is equivalent to a division modulo MAX_SAMPLES, only faster. */ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK] * chorus->sinc_table[ii][pos_subsamples]; pos_samples--; }; /* Cycle the phase of the modulating LFO */ chorus->phase[i]++; chorus->phase[i] %= (chorus->modulation_period_samples); } /* foreach chorus block */ d_out *= chorus->level; /* Store the chorus sum d_out to output */ left_out[sample_index] = d_out; right_out[sample_index] = d_out; /* Move forward in circular buffer */ chorus->counter++; chorus->counter %= MAX_SAMPLES; } /* foreach sample */ } /* Purpose: * * Calculates a modulation waveform (sine) Its value ( modulo * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES. * Its period length is len. The waveform data will be used modulo * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform * a couple of times here, the resulting (current position in * buffer)-(waveform sample) will always be positive. */ static void fluid_chorus_sine(int *buf, int len, int depth) { int i; double val; for (i = 0; i < len; i++) { val = sin((double) i / (double)len * 2.0 * M_PI); buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES); buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; // printf("%i %i\n",i,buf[i]); } } /* Purpose: * Calculates a modulation waveform (triangle) * See fluid_chorus_sine for comments. */ static void fluid_chorus_triangle(int *buf, int len, int depth) { int i=0; int ii=len-1; double val; double val2; while (i <= ii){ val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES; val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES; buf[i++] = (int) val2; buf[ii--] = (int) val2; } } fluidsynth-1.1.9/src/rvoice/fluid_chorus.h000066400000000000000000000036741322272076000206430ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_CHORUS_H #define _FLUID_CHORUS_H #include "fluidsynth_priv.h" typedef struct _fluid_chorus_t fluid_chorus_t; /** Flags for fluid_chorus_set() */ typedef enum { FLUID_CHORUS_SET_NR = 1 << 0, FLUID_CHORUS_SET_LEVEL = 1 << 1, FLUID_CHORUS_SET_SPEED = 1 << 2, FLUID_CHORUS_SET_DEPTH = 1 << 3, FLUID_CHORUS_SET_TYPE = 1 << 4, } fluid_chorus_set_t; /** Value for fluid_chorus_set() which sets all chorus parameters. */ #define FLUID_CHORUS_SET_ALL 0x1F /* * chorus */ fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate); void delete_fluid_chorus(fluid_chorus_t* chorus); int fluid_chorus_init(fluid_chorus_t* chorus); void fluid_chorus_reset(fluid_chorus_t* chorus); void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, float speed, float depth_ms, int type); void fluid_chorus_processmix(fluid_chorus_t* chorus, 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, fluid_real_t *left_out, fluid_real_t *right_out); #endif /* _FLUID_CHORUS_H */ fluidsynth-1.1.9/src/rvoice/fluid_iir_filter.c000066400000000000000000000257061322272076000214630ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_iir_filter.h" #include "fluid_sys.h" #include "fluid_conv.h" /** * Applies a lowpass filter with variable cutoff frequency and quality factor. * Also modifies filter state accordingly. * @param iir_filter Filter parameter * @param dsp_buf Pointer to the synthesized audio data * @param count Count of samples in dsp_buf */ /* * Variable description: * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - dsp_phase_fractional: The fractional part of dsp_phase * - dsp_coeff: A table of four coefficients, depending on the fractional phase. * Used to interpolate between samples. * - dsp_process_buffer: Holds the processed signal between stages * - dsp_centernode: delay line for the IIR filter * - dsp_hist1: same * - dsp_hist2: same */ void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter, fluid_real_t *dsp_buf, int count) { /* IIR filter sample history */ fluid_real_t dsp_hist1 = iir_filter->hist1; fluid_real_t dsp_hist2 = iir_filter->hist2; /* IIR filter coefficients */ fluid_real_t dsp_a1 = iir_filter->a1; fluid_real_t dsp_a2 = iir_filter->a2; fluid_real_t dsp_b02 = iir_filter->b02; fluid_real_t dsp_b1 = iir_filter->b1; int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; fluid_real_t dsp_centernode; int dsp_i; /* filter (implement the voice filter according to SoundFont standard) */ /* Check for denormal number (too close to zero). */ if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ /* Two versions of the filter loop. One, while the filter is * changing towards its new setting. The other, if the filter * doesn't change. */ if (dsp_filter_coeff_incr_count > 0) { fluid_real_t dsp_a1_incr = iir_filter->a1_incr; fluid_real_t dsp_a2_incr = iir_filter->a2_incr; fluid_real_t dsp_b02_incr = iir_filter->b02_incr; fluid_real_t dsp_b1_incr = iir_filter->b1_incr; /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ for (dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; dsp_hist2 = dsp_hist1; dsp_hist1 = dsp_centernode; if (dsp_filter_coeff_incr_count-- > 0) { fluid_real_t old_b02 = dsp_b02; dsp_a1 += dsp_a1_incr; dsp_a2 += dsp_a2_incr; dsp_b02 += dsp_b02_incr; dsp_b1 += dsp_b1_incr; /* Compensate history to avoid the filter going havoc with large frequency changes */ if (iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) { fluid_real_t compensate = old_b02 / dsp_b02; dsp_centernode *= compensate; dsp_hist1 *= compensate; dsp_hist2 *= compensate; } } } /* for dsp_i */ } else /* The filter parameters are constant. This is duplicated to save time. */ { for (dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; dsp_hist2 = dsp_hist1; dsp_hist1 = dsp_centernode; } } iir_filter->hist1 = dsp_hist1; iir_filter->hist2 = dsp_hist2; iir_filter->a1 = dsp_a1; iir_filter->a2 = dsp_a2; iir_filter->b02 = dsp_b02; iir_filter->b1 = dsp_b1; iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; fluid_check_fpe ("voice_filter"); } void fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter) { iir_filter->hist1 = 0; iir_filter->hist2 = 0; iir_filter->last_fres = -1.; iir_filter->filter_startup = 1; } void fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter, fluid_real_t fres) { iir_filter->fres = fres; iir_filter->last_fres = -1.; } void fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter, fluid_real_t q_dB) { /* The 'sound font' Q is defined in dB. The filter needs a linear q. Convert. */ iir_filter->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f)); /* SF 2.01 page 59: * * The SoundFont specs ask for a gain reduction equal to half the * height of the resonance peak (Q). For example, for a 10 dB * resonance peak, the gain is reduced by 5 dB. This is done by * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) * The gain is later factored into the 'b' coefficients * (numerator of the filter equation). This gain factor depends * only on Q, so this is the right place to calculate it. */ iir_filter->filter_gain = (fluid_real_t) (1.0 / sqrt(iir_filter->q_lin)); /* The synthesis loop will have to recalculate the filter coefficients. */ iir_filter->last_fres = -1.; } static inline void fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter, int transition_samples, fluid_real_t output_rate) { /* * Those equations from Robert Bristow-Johnson's `Cookbook * formulae for audio EQ biquad filter coefficients', obtained * from Harmony-central.com / Computer / Programming. They are * the result of the bilinear transform on an analogue filter * prototype. To quote, `BLT frequency warping has been taken * 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 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); fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); /* Calculate the filter coefficients. All coefficients are * normalized by a0. Think of `a1' as `a1/a0'. * * Here a couple of multiplications are saved by reusing common expressions. * The original equations should be: * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */ fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; /* both b0 -and- b2 */ fluid_real_t b02_temp = b1_temp * 0.5f; iir_filter->compensate_incr = 0; if (iir_filter->filter_startup || (transition_samples == 0)) { /* The filter is calculated, because the voice was started up. * In this case set the filter coefficients without delay. */ iir_filter->a1 = a1_temp; iir_filter->a2 = a2_temp; iir_filter->b02 = b02_temp; iir_filter->b1 = b1_temp; iir_filter->filter_coeff_incr_count = 0; iir_filter->filter_startup = 0; // printf("Setting initial filter coefficients.\n"); } else { /* The filter frequency is changed. Calculate an increment * factor, so that the new setting is reached after one buffer * length. x_incr is added to the current value FLUID_BUFSIZE * times. The length is arbitrarily chosen. Longer than one * buffer will sacrifice some performance, though. Note: If * the filter is still too 'grainy', then increase this number * at will. */ iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; if (fabs(iir_filter->b02) > 0.0001) { fluid_real_t quota = b02_temp / iir_filter->b02; iir_filter->compensate_incr = quota < 0.5 || quota > 2; } /* Have to add the increments filter_coeff_incr_count times. */ iir_filter->filter_coeff_incr_count = transition_samples; } fluid_check_fpe ("voice_write filter calculation"); } void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter, fluid_real_t output_rate, fluid_real_t fres_mod) { fluid_real_t fres; /* calculate the frequency of the resonant filter in Hz */ fres = fluid_ct2hz(iir_filter->fres + fres_mod); /* FIXME - Still potential for a click during turn on, can we interpolate between 20khz cutoff and 0 Q? */ /* I removed the optimization of turning the filter off when the * resonance frequence is above the maximum frequency. Instead, the * filter frequency is set to a maximum of 0.45 times the sampling * rate. For a 44100 kHz sampling rate, this amounts to 19845 * Hz. The reason is that there were problems with anti-aliasing when the * synthesizer was run at lower sampling rates. Thanks to Stephan * Tassart for pointing me to this bug. By turning the filter on and * clipping the maximum filter frequency at 0.45*srate, the filter * is used as an anti-aliasing filter. */ if (fres > 0.45f * output_rate) fres = 0.45f * output_rate; else if (fres < 5) fres = 5; /* if filter enabled and there is a significant frequency change.. */ if (fabs (fres - iir_filter->last_fres) > 0.01) { /* The filter coefficients have to be recalculated (filter * parameters have changed). Recalculation for various reasons is * forced by setting last_fres to -1. The flag filter_startup * indicates, that the DSP loop runs for the first time, in this * case, the filter is set directly, instead of smoothly fading * between old and new settings. */ iir_filter->last_fres = fres; fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, output_rate); } fluid_check_fpe ("voice_write DSP coefficients"); } fluidsynth-1.1.9/src/rvoice/fluid_iir_filter.h000066400000000000000000000054371322272076000214670ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_IIR_FILTER_H #define _FLUID_IIR_FILTER_H #include "fluidsynth_priv.h" typedef struct _fluid_iir_filter_t fluid_iir_filter_t; void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter, fluid_real_t *dsp_buf, int dsp_buf_count); void fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter); void fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter, fluid_real_t q_dB); void fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter, fluid_real_t fres); void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter, fluid_real_t output_rate, fluid_real_t fres_mod); /* We can't do information hiding here, as fluid_voice_t includes the struct without a pointer. */ struct _fluid_iir_filter_t { /* filter coefficients */ /* The coefficients are normalized to a0. */ /* b0 and b2 are identical => b02 */ fluid_real_t b02; /* b0 / a0 */ fluid_real_t b1; /* b1 / a0 */ fluid_real_t a1; /* a0 / a0 */ fluid_real_t a2; /* a1 / a0 */ fluid_real_t b02_incr; fluid_real_t b1_incr; fluid_real_t a1_incr; fluid_real_t a2_incr; int filter_coeff_incr_count; int compensate_incr; /* Flag: If set, must compensate history */ fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ int filter_startup; /* Flag: If set, the filter will be set directly. Else it changes smoothly. */ fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */ fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */ /* Serves as a flag: A deviation between fres and last_fres */ /* indicates, that the filter has to be recalculated. */ fluid_real_t q_lin; /* the q-factor on a linear scale */ fluid_real_t filter_gain; /* Gain correction factor, depends on q */ }; #endif fluidsynth-1.1.9/src/rvoice/fluid_lfo.c000066400000000000000000000003261322272076000201020ustar00rootroot00000000000000#include "fluid_lfo.h" void fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment) { lfo->increment = increment; } void fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay) { lfo->delay = delay; } fluidsynth-1.1.9/src/rvoice/fluid_lfo.h000066400000000000000000000037271322272076000201170ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_LFO_H #define _FLUID_LFO_H #include "fluidsynth_priv.h" typedef struct _fluid_lfo_t fluid_lfo_t; struct _fluid_lfo_t { fluid_real_t val; /* the current value of the LFO */ unsigned int delay; /* the delay of the lfo in samples */ fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */ }; static inline void fluid_lfo_reset(fluid_lfo_t* lfo) { lfo->val = 0.0f; } // These two cannot be inlined since they're used by event_dispatch void fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment); void fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay); static inline fluid_real_t fluid_lfo_get_val(fluid_lfo_t* lfo) { return lfo->val; } static inline void fluid_lfo_calc(fluid_lfo_t* lfo, unsigned int cur_delay) { if (cur_delay < lfo->delay) return; lfo->val += lfo->increment; if (lfo->val > (fluid_real_t) 1.0) { lfo->increment = -lfo->increment; lfo->val = (fluid_real_t) 2.0 - lfo->val; } else if (lfo->val < (fluid_real_t) -1.0) { lfo->increment = -lfo->increment; lfo->val = (fluid_real_t) -2.0 - lfo->val; } } #endif fluidsynth-1.1.9/src/rvoice/fluid_phase.h000066400000000000000000000072371322272076000204370ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_PHASE_H #define _FLUID_PHASE_H #if HAVE_CONFIG_H #include "config.h" #endif /* * phase */ #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) /* fluid_phase_t * Purpose: * Playing pointer for voice playback * * When a sample is played back at a different pitch, the playing pointer in the * source sample will not advance exactly one sample per output sample. * This playing pointer is implemented using fluid_phase_t. * It is a 64 bit number. The higher 32 bits contain the 'index' (number of * the current sample), the lower 32 bits the fractional part. */ typedef unsigned long long fluid_phase_t; /* Purpose: * Set a to b. * a: fluid_phase_t * b: fluid_phase_t */ #define fluid_phase_set(a,b) a=b; #define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32) /* Purpose: * Sets the phase a to a phase increment given in b. * For example, assume b is 0.9. After setting a to it, adding a to * the playing pointer will advance it by 0.9 samples. */ #define fluid_phase_set_float(a, b) \ (a) = (((unsigned long long)(b)) << 32) \ | (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) /* create a fluid_phase_t from an index and a fraction value */ #define fluid_phase_from_index_fract(index, fract) \ ((((unsigned long long)(index)) << 32) + (fract)) /* Purpose: * Return the index and the fractional part, respectively. */ #define fluid_phase_index(_x) \ ((unsigned int)((_x) >> 32)) #define fluid_phase_fract(_x) \ ((uint32)((_x) & 0xFFFFFFFF)) /* Get the phase index with fractional rounding */ #define fluid_phase_index_round(_x) \ ((unsigned int)(((_x) + 0x80000000) >> 32)) /* Purpose: * Takes the fractional part of the argument phase and * calculates the corresponding position in the interpolation table. * The fractional position of the playing pointer is calculated with a quite high * resolution (32 bits). It would be unpractical to keep a set of interpolation * coefficients for each possible fractional part... */ #define fluid_phase_fract_to_tablerow(_x) \ ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT) #define fluid_phase_double(_x) \ ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX)) /* Purpose: * Advance a by a step of b (both are fluid_phase_t). */ #define fluid_phase_incr(a, b) a += b /* Purpose: * Subtract b from a (both are fluid_phase_t). */ #define fluid_phase_decr(a, b) a -= b /* Purpose: * Subtract b samples from a. */ #define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32) /* Purpose: * Creates the expression a.index++. */ #define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL) #endif /* _FLUID_PHASE_H */ fluidsynth-1.1.9/src/rvoice/fluid_rev.c000066400000000000000000000367571322272076000201370ustar00rootroot00000000000000/* Freeverb Written by Jezar at Dreampoint, June 2000 http://www.dreampoint.co.uk This code is public domain Translated to C by Peter Hanappe, Mai 2001 */ #include "fluid_rev.h" /*************************************************************** * * REVERB */ /* Denormalising: * * According to music-dsp thread 'Denormalise', Pentium processors * have a hardware 'feature', that is of interest here, related to * numeric underflow. We have a recursive filter. The output decays * exponentially, if the input stops. So the numbers get smaller and * smaller... At some point, they reach 'denormal' level. This will * lead to drastic spikes in the CPU load. The effect was reproduced * with the reverb - sometimes the average load over 10 s doubles!!. * * The 'undenormalise' macro fixes the problem: As soon as the number * is close enough to denormal level, the macro forces the number to * 0.0f. The original macro is: * * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f * * This will zero out a number when it reaches the denormal level. * Advantage: Maximum dynamic range Disadvantage: We'll have to check * every sample, expensive. The alternative macro comes from a later * mail from Jon Watte. It will zap a number before it reaches * denormal level. Jon suggests to run it once per block instead of * every sample. */ # if defined(WITH_FLOATX) # define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample) # else /* 1e-20 was chosen as an arbitrary (small) threshold. */ #define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample; #endif /* Denormalising part II: * * Another method fixes the problem cheaper: Use a small DC-offset in * the filter calculations. Now the signals converge not against 0, * but against the offset. The constant offset is invisible from the * outside world (i.e. it does not appear at the output. There is a * very small turn-on transient response, which should not cause * problems. */ //#define DC_OFFSET 0 #define DC_OFFSET 1e-8 //#define DC_OFFSET 0.001f typedef struct _fluid_allpass fluid_allpass; typedef struct _fluid_comb fluid_comb; struct _fluid_allpass { fluid_real_t feedback; fluid_real_t *buffer; int bufsize; int bufidx; }; void fluid_allpass_init(fluid_allpass* allpass); void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val); fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass); void fluid_allpass_setbuffer(fluid_allpass* allpass, int size) { allpass->bufidx = 0; allpass->buffer = FLUID_ARRAY(fluid_real_t,size); allpass->bufsize = size; } void fluid_allpass_release(fluid_allpass* allpass) { FLUID_FREE(allpass->buffer); } void fluid_allpass_init(fluid_allpass* allpass) { int i; int len = allpass->bufsize; fluid_real_t* buf = allpass->buffer; for (i = 0; i < len; i++) { buf[i] = DC_OFFSET; /* this is not 100 % correct. */ } } void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val) { allpass->feedback = val; } fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass) { return allpass->feedback; } #define fluid_allpass_process(_allpass, _input) \ { \ fluid_real_t output; \ fluid_real_t bufout; \ bufout = _allpass.buffer[_allpass.bufidx]; \ output = bufout-_input; \ _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \ if (++_allpass.bufidx >= _allpass.bufsize) { \ _allpass.bufidx = 0; \ } \ _input = output; \ } /* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */ /* { */ /* fluid_real_t output; */ /* fluid_real_t bufout; */ /* bufout = allpass->buffer[allpass->bufidx]; */ /* undenormalise(bufout); */ /* output = -input + bufout; */ /* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */ /* if (++allpass->bufidx >= allpass->bufsize) { */ /* allpass->bufidx = 0; */ /* } */ /* return output; */ /* } */ struct _fluid_comb { fluid_real_t feedback; fluid_real_t filterstore; fluid_real_t damp1; fluid_real_t damp2; fluid_real_t *buffer; int bufsize; int bufidx; }; void fluid_comb_setbuffer(fluid_comb* comb, int size); void fluid_comb_release(fluid_comb* comb); void fluid_comb_init(fluid_comb* comb); void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val); fluid_real_t fluid_comb_getdamp(fluid_comb* comb); void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val); fluid_real_t fluid_comb_getfeedback(fluid_comb* comb); void fluid_comb_setbuffer(fluid_comb* comb, int size) { comb->filterstore = 0; comb->bufidx = 0; comb->buffer = FLUID_ARRAY(fluid_real_t,size); comb->bufsize = size; } void fluid_comb_release(fluid_comb* comb) { FLUID_FREE(comb->buffer); } void fluid_comb_init(fluid_comb* comb) { int i; fluid_real_t* buf = comb->buffer; int len = comb->bufsize; for (i = 0; i < len; i++) { buf[i] = DC_OFFSET; /* This is not 100 % correct. */ } } void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val) { comb->damp1 = val; comb->damp2 = 1 - val; } fluid_real_t fluid_comb_getdamp(fluid_comb* comb) { return comb->damp1; } void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val) { comb->feedback = val; } fluid_real_t fluid_comb_getfeedback(fluid_comb* comb) { return comb->feedback; } #define fluid_comb_process(_comb, _input, _output) \ { \ fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \ _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \ _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \ if (++_comb.bufidx >= _comb.bufsize) { \ _comb.bufidx = 0; \ } \ _output += _tmp; \ } /* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */ /* { */ /* fluid_real_t output; */ /* output = comb->buffer[comb->bufidx]; */ /* undenormalise(output); */ /* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */ /* undenormalise(comb->filterstore); */ /* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */ /* if (++comb->bufidx >= comb->bufsize) { */ /* comb->bufidx = 0; */ /* } */ /* return output; */ /* } */ #define numcombs 8 #define numallpasses 4 #define fixedgain 0.015f #define scalewet 3.0f #define scaledamp 1.0f #define scaleroom 0.28f #define offsetroom 0.7f #define initialroom 0.5f #define initialdamp 0.2f #define initialwet 1 #define initialdry 0 #define initialwidth 1 #define stereospread 23 /* These values assume 44.1KHz sample rate they will probably be OK for 48KHz sample rate but would need scaling for 96KHz (or other) sample rates. The values were obtained by listening tests. */ #define combtuningL1 1116 #define combtuningR1 (1116 + stereospread) #define combtuningL2 1188 #define combtuningR2 (1188 + stereospread) #define combtuningL3 1277 #define combtuningR3 (1277 + stereospread) #define combtuningL4 1356 #define combtuningR4 (1356 + stereospread) #define combtuningL5 1422 #define combtuningR5 (1422 + stereospread) #define combtuningL6 1491 #define combtuningR6 (1491 + stereospread) #define combtuningL7 1557 #define combtuningR7 (1557 + stereospread) #define combtuningL8 1617 #define combtuningR8 (1617 + stereospread) #define allpasstuningL1 556 #define allpasstuningR1 (556 + stereospread) #define allpasstuningL2 441 #define allpasstuningR2 (441 + stereospread) #define allpasstuningL3 341 #define allpasstuningR3 (341 + stereospread) #define allpasstuningL4 225 #define allpasstuningR4 (225 + stereospread) struct _fluid_revmodel_t { fluid_real_t roomsize; fluid_real_t damp; fluid_real_t wet, wet1, wet2; fluid_real_t width; fluid_real_t gain; /* The following are all declared inline to remove the need for dynamic allocation with its subsequent error-checking messiness */ /* Comb filters */ fluid_comb combL[numcombs]; fluid_comb combR[numcombs]; /* Allpass filters */ fluid_allpass allpassL[numallpasses]; fluid_allpass allpassR[numallpasses]; }; static void fluid_revmodel_update(fluid_revmodel_t* rev); static void fluid_revmodel_init(fluid_revmodel_t* rev); void fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate); fluid_revmodel_t* new_fluid_revmodel(fluid_real_t sample_rate) { fluid_revmodel_t* rev; rev = FLUID_NEW(fluid_revmodel_t); if (rev == NULL) { return NULL; } fluid_set_revmodel_buffers(rev, sample_rate); /* Set default values */ fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f); fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f); fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f); fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f); fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f); fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f); fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f); fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f); rev->gain = fixedgain; fluid_revmodel_set(rev,FLUID_REVMODEL_SET_ALL,initialroom,initialdamp,initialwidth,initialwet); return rev; } void delete_fluid_revmodel(fluid_revmodel_t* rev) { int i; for (i = 0; i < numcombs;i++) { fluid_comb_release(&rev->combL[i]); fluid_comb_release(&rev->combR[i]); } for (i = 0; i < numallpasses; i++) { fluid_allpass_release(&rev->allpassL[i]); fluid_allpass_release(&rev->allpassR[i]); } FLUID_FREE(rev); } void fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate) { float srfactor = sample_rate/44100.0f; fluid_comb_setbuffer(&rev->combL[0], combtuningL1*srfactor); fluid_comb_setbuffer(&rev->combR[0], combtuningR1*srfactor); fluid_comb_setbuffer(&rev->combL[1], combtuningL2*srfactor); fluid_comb_setbuffer(&rev->combR[1], combtuningR2*srfactor); fluid_comb_setbuffer(&rev->combL[2], combtuningL3*srfactor); fluid_comb_setbuffer(&rev->combR[2], combtuningR3*srfactor); fluid_comb_setbuffer(&rev->combL[3], combtuningL4*srfactor); fluid_comb_setbuffer(&rev->combR[3], combtuningR4*srfactor); fluid_comb_setbuffer(&rev->combL[4], combtuningL5*srfactor); fluid_comb_setbuffer(&rev->combR[4], combtuningR5*srfactor); fluid_comb_setbuffer(&rev->combL[5], combtuningL6*srfactor); fluid_comb_setbuffer(&rev->combR[5], combtuningR6*srfactor); fluid_comb_setbuffer(&rev->combL[6], combtuningL7*srfactor); fluid_comb_setbuffer(&rev->combR[6], combtuningR7*srfactor); fluid_comb_setbuffer(&rev->combL[7], combtuningL8*srfactor); fluid_comb_setbuffer(&rev->combR[7], combtuningR8*srfactor); fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1*srfactor); fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1*srfactor); fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2*srfactor); fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2*srfactor); fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3*srfactor); fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3*srfactor); fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4*srfactor); fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4*srfactor); /* Clear all buffers */ fluid_revmodel_init(rev); } static void fluid_revmodel_init(fluid_revmodel_t* rev) { int i; for (i = 0; i < numcombs;i++) { fluid_comb_init(&rev->combL[i]); fluid_comb_init(&rev->combR[i]); } for (i = 0; i < numallpasses; i++) { fluid_allpass_init(&rev->allpassL[i]); fluid_allpass_init(&rev->allpassR[i]); } } void fluid_revmodel_reset(fluid_revmodel_t* rev) { fluid_revmodel_init(rev); } void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k = 0; fluid_real_t outL, outR, input; for (k = 0; k < FLUID_BUFSIZE; k++) { outL = outR = 0; /* The original Freeverb code expects a stereo signal and 'input' * is set to the sum of the left and right input sample. Since * this code works on a mono signal, 'input' is set to twice the * input sample. */ input = (2.0f * in[k] + DC_OFFSET) * rev->gain; /* Accumulate comb filters in parallel */ for (i = 0; i < numcombs; i++) { fluid_comb_process(rev->combL[i], input, outL); fluid_comb_process(rev->combR[i], input, outR); } /* Feed through allpasses in series */ for (i = 0; i < numallpasses; i++) { fluid_allpass_process(rev->allpassL[i], outL); fluid_allpass_process(rev->allpassR[i], outR); } /* Remove the DC offset */ outL -= DC_OFFSET; outR -= DC_OFFSET; /* Calculate output REPLACING anything already there */ left_out[k] = outL * rev->wet1 + outR * rev->wet2; right_out[k] = outR * rev->wet1 + outL * rev->wet2; } } void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k = 0; fluid_real_t outL, outR, input; for (k = 0; k < FLUID_BUFSIZE; k++) { outL = outR = 0; /* The original Freeverb code expects a stereo signal and 'input' * is set to the sum of the left and right input sample. Since * this code works on a mono signal, 'input' is set to twice the * input sample. */ input = (2.0f * in[k] + DC_OFFSET) * rev->gain; /* Accumulate comb filters in parallel */ for (i = 0; i < numcombs; i++) { fluid_comb_process(rev->combL[i], input, outL); fluid_comb_process(rev->combR[i], input, outR); } /* Feed through allpasses in series */ for (i = 0; i < numallpasses; i++) { fluid_allpass_process(rev->allpassL[i], outL); fluid_allpass_process(rev->allpassR[i], outR); } /* Remove the DC offset */ outL -= DC_OFFSET; outR -= DC_OFFSET; /* Calculate output MIXING with anything already there */ left_out[k] += outL * rev->wet1 + outR * rev->wet2; right_out[k] += outR * rev->wet1 + outL * rev->wet2; } } static void fluid_revmodel_update(fluid_revmodel_t* rev) { /* Recalculate internal values after parameter change */ int i; rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f); rev->wet2 = rev->wet * ((1.0f - rev->width) / 2.0f); for (i = 0; i < numcombs; i++) { fluid_comb_setfeedback(&rev->combL[i], rev->roomsize); fluid_comb_setfeedback(&rev->combR[i], rev->roomsize); } for (i = 0; i < numcombs; i++) { fluid_comb_setdamp(&rev->combL[i], rev->damp); fluid_comb_setdamp(&rev->combR[i], rev->damp); } } /** * Set one or more reverb parameters. * @param rev Reverb instance * @param set One or more flags from #fluid_revmodel_set_t indicating what * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters) * @param roomsize Reverb room size * @param damping Reverb damping * @param width Reverb width * @param level Reverb level */ void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, float damping, float width, float level) { if (set & FLUID_REVMODEL_SET_ROOMSIZE) rev->roomsize = (roomsize * scaleroom) + offsetroom; if (set & FLUID_REVMODEL_SET_DAMPING) rev->damp = damping * scaledamp; if (set & FLUID_REVMODEL_SET_WIDTH) rev->width = width; if (set & FLUID_REVMODEL_SET_LEVEL) { fluid_clip(level, 0.0f, 1.0f); rev->wet = level * scalewet; } fluid_revmodel_update (rev); } void fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate) { int i; for (i = 0; i < numcombs;i++) { fluid_comb_release(&rev->combL[i]); fluid_comb_release(&rev->combR[i]); } for (i = 0; i < numallpasses; i++) { fluid_allpass_release(&rev->allpassL[i]); fluid_allpass_release(&rev->allpassR[i]); } fluid_set_revmodel_buffers(rev, sample_rate); } fluidsynth-1.1.9/src/rvoice/fluid_rev.h000066400000000000000000000043101322272076000201200ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_REV_H #define _FLUID_REV_H #include "fluidsynth_priv.h" typedef struct _fluid_revmodel_t fluid_revmodel_t; /** Flags for fluid_revmodel_set() */ typedef enum { FLUID_REVMODEL_SET_ROOMSIZE = 1 << 0, FLUID_REVMODEL_SET_DAMPING = 1 << 1, FLUID_REVMODEL_SET_WIDTH = 1 << 2, FLUID_REVMODEL_SET_LEVEL = 1 << 3 } fluid_revmodel_set_t; /** Value for fluid_revmodel_set() which sets all reverb parameters. */ #define FLUID_REVMODEL_SET_ALL 0x0F /* * reverb preset */ typedef struct _fluid_revmodel_presets_t { char* name; fluid_real_t roomsize; fluid_real_t damp; fluid_real_t width; fluid_real_t level; } fluid_revmodel_presets_t; /* * reverb */ 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, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_revmodel_reset(fluid_revmodel_t* rev); void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, float damping, float width, float level); void fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate); #endif /* _FLUID_REV_H */ fluidsynth-1.1.9/src/rvoice/fluid_rvoice.c000066400000000000000000000537651322272076000206300ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_rvoice.h" #include "fluid_conv.h" #include "fluid_sys.h" /** * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise */ static inline int fluid_rvoice_calc_amp(fluid_rvoice_t* voice) { fluid_real_t target_amp; /* target amplitude */ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) return -1; /* The volume amplitude is in hold phase. No sound is produced. */ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) { /* the envelope is in the attack section: ramp linearly to max value. * A positive modlfo_to_vol should increase volume (negative attenuation). */ target_amp = fluid_atten2amp (voice->dsp.attenuation) * fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) * fluid_adsr_env_get_val(&voice->envlfo.volenv); } else { fluid_real_t amplitude_that_reaches_noise_floor; fluid_real_t amp_max; target_amp = fluid_atten2amp (voice->dsp.attenuation) * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); /* We turn off a voice, if the volume has dropped low enough. */ /* A voice can be turned off, when an estimate for the volume * (upper bound) falls below that volume, that will drop the * sample below the noise floor. */ /* If the loop amplitude is known, we can use it if the voice loop is within * the sample loop */ /* Is the playing pointer already in the loop? */ if (voice->dsp.has_looped) amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; else amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; /* voice->attenuation_min is a lower boundary for the attenuation * now and in the future (possibly 0 in the worst case). Now the * amplitude of sample and volenv cannot exceed amp_max (since * volenv_val can only drop): */ amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) * fluid_adsr_env_get_val(&voice->envlfo.volenv); /* And if amp_max is already smaller than the known amplitude, * which will attenuate the sample below the noise floor, then we * can safely turn off the voice. Duh. */ if (amp_max < amplitude_that_reaches_noise_floor) { return 0; } } /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; fluid_check_fpe ("voice_write amplitude calculation"); /* no volume and not changing? - No need to process */ if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) return -1; return 1; } /* these should be the absolute minimum that FluidSynth can deal with */ #define FLUID_MIN_LOOP_SIZE 2 #define FLUID_MIN_LOOP_PAD 0 #define FLUID_SAMPLESANITY_CHECK (1 << 0) #define FLUID_SAMPLESANITY_STARTUP (1 << 1) /* Purpose: * * Make sure, that sample start / end point and loop points are in * proper order. When starting up, calculate the initial phase. * TODO: Investigate whether this can be moved from rvoice to voice. */ static void fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice) { int min_index_nonloop=(int) voice->dsp.sample->start; int max_index_nonloop=(int) voice->dsp.sample->end; /* make sure we have enough samples surrounding the loop */ int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ fluid_check_fpe("voice_check_sample_sanity start"); if (!voice->dsp.check_sample_sanity_flag){ return; } #if 0 printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end); printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend); printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end); printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend); #endif /* Keep the start point within the sample data */ if (voice->dsp.start < min_index_nonloop){ voice->dsp.start = min_index_nonloop; } else if (voice->dsp.start > max_index_nonloop){ voice->dsp.start = max_index_nonloop; } /* Keep the end point within the sample data */ if (voice->dsp.end < min_index_nonloop){ voice->dsp.end = min_index_nonloop; } else if (voice->dsp.end > max_index_nonloop){ voice->dsp.end = max_index_nonloop; } /* Keep start and end point in the right order */ if (voice->dsp.start > voice->dsp.end){ int temp = voice->dsp.start; voice->dsp.start = voice->dsp.end; voice->dsp.end = temp; /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ } /* Zero length? */ if (voice->dsp.start == voice->dsp.end){ fluid_rvoice_voiceoff(voice); return; } if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) { /* Keep the loop start point within the sample data */ if (voice->dsp.loopstart < min_index_loop){ voice->dsp.loopstart = min_index_loop; } else if (voice->dsp.loopstart > max_index_loop){ voice->dsp.loopstart = max_index_loop; } /* Keep the loop end point within the sample data */ if (voice->dsp.loopend < min_index_loop){ voice->dsp.loopend = min_index_loop; } else if (voice->dsp.loopend > max_index_loop){ voice->dsp.loopend = max_index_loop; } /* Keep loop start and end point in the right order */ if (voice->dsp.loopstart > voice->dsp.loopend){ int temp = voice->dsp.loopstart; voice->dsp.loopstart = voice->dsp.loopend; voice->dsp.loopend = temp; /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ } /* Loop too short? Then don't loop. */ if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){ voice->dsp.samplemode = FLUID_UNLOOPED; } /* The loop points may have changed. Obtain a new estimate for the loop volume. */ /* Is the voice loop within the sample loop? */ if ((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){ /* Is there a valid peak amplitude available for the loop, and can we use it? */ if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE){ voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; } else { /* Worst case */ voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop; }; }; } /* if sample mode is looped */ /* Run startup specific code (only once, when the voice is started) */ if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){ if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){ voice->dsp.samplemode = FLUID_UNLOOPED; } } /* Set the initial phase of the voice (using the result from the start offset modulators). */ fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); } /* if startup */ /* Is this voice run in loop mode, or does it run straight to the end of the waveform data? */ if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) { /* Yes, it will loop as soon as it reaches the loop point. In * this case we must prevent, that the playback pointer (phase) * happens to end up beyond the 2nd loop point, because the * point has moved. The DSP algorithm is unable to cope with * that situation. So if the phase is beyond the 2nd loop * point, set it to the start of the loop. No way to avoid some * noise here. Note: If the sample pointer ends up -before the * first loop point- instead, then the DSP loop will just play * the sample, enter the loop and proceed as expected => no * actions required. */ int index_in_sample = fluid_phase_index(voice->dsp.phase); if (index_in_sample >= voice->dsp.loopend){ /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); } } /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ /* Sample sanity has been assured. Don't check again, until some sample parameter is changed by modulation. */ voice->dsp.check_sample_sanity_flag=0; #if 0 printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); #endif fluid_check_fpe("voice_check_sample_sanity"); } /** * Synthesize a voice to a buffer. * * @param voice rvoice to synthesize * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) * @return Count of samples written to dsp_buf. (-1 means voice is currently * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) * * Panning, reverb and chorus are processed separately. The dsp interpolation * routine is in (fluid_dsp_float.c). */ int fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf) { int ticks = voice->envlfo.ticks; int count; /******************* sample sanity check **********/ if (!voice->dsp.sample) return 0; if (voice->dsp.check_sample_sanity_flag) fluid_rvoice_check_sample_sanity(voice); /******************* noteoff check ****************/ if (voice->envlfo.noteoff_ticks != 0 && voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) { fluid_rvoice_noteoff(voice, 0); } voice->envlfo.ticks += FLUID_BUFSIZE; /******************* vol env **********************/ fluid_adsr_env_calc(&voice->envlfo.volenv, 1); fluid_check_fpe ("voice_write vol env"); if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) return 0; /******************* mod env **********************/ fluid_adsr_env_calc(&voice->envlfo.modenv, 0); fluid_check_fpe ("voice_write mod env"); /******************* lfo **********************/ fluid_lfo_calc(&voice->envlfo.modlfo, ticks); fluid_check_fpe ("voice_write mod LFO"); fluid_lfo_calc(&voice->envlfo.viblfo, ticks); fluid_check_fpe ("voice_write vib LFO"); /******************* amplitude **********************/ count = fluid_rvoice_calc_amp(voice); if (count <= 0) return count; /******************* phase **********************/ /* 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 * waveform and output waveform.*/ voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + 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) / voice->dsp.root_pitch_hz; fluid_check_fpe ("voice_write phase calculation"); /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1; voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); /*********************** run the dsp chain ************************ * The sample is mixed with the output buffer. * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. * Depending on the position in the loop and the loop size, this * may require several runs. */ voice->dsp.dsp_buf = dsp_buf; switch (voice->dsp.interp_method) { case FLUID_INTERP_NONE: count = fluid_rvoice_dsp_interpolate_none (&voice->dsp); break; case FLUID_INTERP_LINEAR: count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp); break; case FLUID_INTERP_4THORDER: default: count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp); break; case FLUID_INTERP_7THORDER: count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp); break; } fluid_check_fpe ("voice_write interpolation"); if (count == 0) return count; /*************** resonant filter ******************/ 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); fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); return count; } static inline fluid_real_t* get_dest_buf(fluid_rvoice_buffers_t* buffers, int index, fluid_real_t** dest_bufs, int dest_bufcount) { int j = buffers->bufs[index].mapping; if (j >= dest_bufcount || j < 0) return NULL; return dest_bufs[j]; } /** * Mix data down to buffers * * @param buffers Destination buffer(s) * @param dsp_buf Mono sample source * @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction) * @param dest_bufs Array of buffers to mixdown to * @param dest_bufcount Length of dest_bufs */ void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, fluid_real_t* dsp_buf, int samplecount, fluid_real_t** dest_bufs, int dest_bufcount) { int bufcount = buffers->count; int i, dsp_i; if (!samplecount || !bufcount || !dest_bufcount) return; for (i=0; i < bufcount; i++) { fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); fluid_real_t* next_buf; fluid_real_t amp = buffers->bufs[i].amp; if (buf == NULL || amp == 0.0f) continue; /* Optimization for centered stereo samples - we can save one multiplication per sample */ next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount)); if (next_buf && buffers->bufs[i+1].amp == amp) { for (dsp_i = 0; dsp_i < samplecount; dsp_i++) { fluid_real_t samp = amp * dsp_buf[dsp_i]; buf[dsp_i] += samp; next_buf[dsp_i] += samp; } i++; } else { for (dsp_i = 0; dsp_i < samplecount; dsp_i++) buf[dsp_i] += amp * dsp_buf[dsp_i]; } } } /** * Initialize buffers up to (and including) bufnum */ static int fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum) { unsigned int i; if (bufnum < buffers->count) return FLUID_OK; if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED; for (i = buffers->count; i <= bufnum; i++) { buffers->bufs[bufnum].amp = 0.0f; buffers->bufs[bufnum].mapping = i; } buffers->count = bufnum+1; return FLUID_OK; } void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers, unsigned int bufnum, fluid_real_t value) { if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) return; buffers->bufs[bufnum].amp = value; } void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, unsigned int bufnum, int mapping) { if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) return; buffers->bufs[bufnum].mapping = mapping; } void fluid_rvoice_reset(fluid_rvoice_t* voice) { voice->dsp.has_looped = 0; voice->envlfo.ticks = 0; voice->envlfo.noteoff_ticks = 0; voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to calculate the volume increment during processing */ /* mod env initialization*/ fluid_adsr_env_reset(&voice->envlfo.modenv); /* vol env initialization */ fluid_adsr_env_reset(&voice->envlfo.volenv); /* Fixme: Retrieve from any other existing voice on this channel to keep LFOs in unison? */ fluid_lfo_reset(&voice->envlfo.viblfo); fluid_lfo_reset(&voice->envlfo.modlfo); /* Clear sample history in filter */ fluid_iir_filter_reset(&voice->resonant_filter); /* Force setting of the phase at the first DSP loop run * This cannot be done earlier, because it depends on modulators. [DH] Is that comment really true? */ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; } void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks) { if (min_ticks > voice->envlfo.ticks) { /* Delay noteoff */ voice->envlfo.noteoff_ticks = min_ticks; return; } voice->envlfo.noteoff_ticks = 0; if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) { /* 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 * 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) * pow (10.0, lfo / -200); fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); fluid_clip (env_value, 0.0, 1.0); fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); } } fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); } void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.output_rate = value; } void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value) { voice->dsp.interp_method = value; } void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.root_pitch_hz = value; } void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.pitch = value; } void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.attenuation = value; } void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.min_attenuation_cB = value; } void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.viblfo_to_pitch = value; } void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.modlfo_to_pitch = value; } void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.modlfo_to_vol = value; } void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.modlfo_to_fc = value; } void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.modenv_to_fc = value; } void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value) { voice->envlfo.modenv_to_pitch = value; } void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value) { voice->dsp.synth_gain = value; /* For a looped sample, this value will be overwritten as soon as the * loop parameters are initialized (they may depend on modulators). * This value can be kept, it is a worst-case estimate. */ voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value) { voice->dsp.start = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value) { voice->dsp.end = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value) { voice->dsp.loopstart = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value) { voice->dsp.loopend = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value) { voice->dsp.samplemode = value; voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; } void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value) { voice->dsp.sample = value; if (value) { voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; } } void fluid_rvoice_voiceoff(fluid_rvoice_t* voice) { fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); } fluidsynth-1.1.9/src/rvoice/fluid_rvoice.h000066400000000000000000000157671322272076000206350ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RVOICE_H #define _FLUID_RVOICE_H #include "fluidsynth_priv.h" #include "fluid_iir_filter.h" #include "fluid_adsr_env.h" #include "fluid_lfo.h" #include "fluid_phase.h" #include "fluid_sfont.h" typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t; typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t; typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t; typedef struct _fluid_rvoice_t fluid_rvoice_t; /* Smallest amplitude that can be perceived (full scale is +/- 0.5) * 16 bits => 96+4=100 dB dynamic range => 0.00001 * 0.00001 * 2 is approximately 0.00003 :) */ #define FLUID_NOISE_FLOOR 0.00003 enum fluid_loop { FLUID_UNLOOPED = 0, FLUID_LOOP_DURING_RELEASE = 1, FLUID_NOTUSED = 2, FLUID_LOOP_UNTIL_RELEASE = 3 }; /* * rvoice ticks-based parameters * These parameters must be updated even if the voice is currently quiet. */ struct _fluid_rvoice_envlfo_t { /* Note-off minimum length */ unsigned int ticks; unsigned int noteoff_ticks; /* vol env */ fluid_adsr_env_t volenv; /* mod env */ fluid_adsr_env_t modenv; fluid_real_t modenv_to_fc; fluid_real_t modenv_to_pitch; /* mod lfo */ fluid_lfo_t modlfo; fluid_real_t modlfo_to_fc; fluid_real_t modlfo_to_pitch; fluid_real_t modlfo_to_vol; /* vib lfo */ fluid_lfo_t viblfo; fluid_real_t viblfo_to_pitch; }; /* * rvoice parameters needed for dsp interpolation */ struct _fluid_rvoice_dsp_t { /* interpolation method, as in fluid_interp in fluidsynth.h */ int interp_method; fluid_sample_t* sample; int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters have to be checked. */ /* sample and loop start and end points (offset in sample memory). */ int start; int end; int loopstart; int loopend; /* Note: first point following the loop (superimposed on loopstart) */ enum fluid_loop samplemode; /* Stuff needed for phase calculations */ fluid_real_t pitch; /* the pitch in midicents */ fluid_real_t root_pitch_hz; fluid_real_t output_rate; /* Stuff needed for amplitude calculations */ int has_looped; /* Flag that is set as soon as the first loop is completed. */ fluid_real_t attenuation; /* the attenuation in centibels */ fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation * during the lifetime of the voice */ fluid_real_t amplitude_that_reaches_noise_floor_nonloop; fluid_real_t amplitude_that_reaches_noise_floor_loop; fluid_real_t synth_gain; /* master gain */ /* Dynamic input to the interpolator below */ fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */ fluid_real_t amp; /* current linear amplitude */ fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ int is_looping; }; /* Currently left, right, reverb, chorus. To be changed if we ever add surround positioning, or stereo reverb/chorus */ #define FLUID_RVOICE_MAX_BUFS (4) /* * rvoice mixer-related parameters */ struct _fluid_rvoice_buffers_t { unsigned int count; /* Number of records in "bufs" */ struct { fluid_real_t amp; int mapping; /* Mapping to mixdown buffer index */ } bufs[FLUID_RVOICE_MAX_BUFS]; }; /* * Hard real-time parameters needed to synthesize a voice */ struct _fluid_rvoice_t { fluid_rvoice_envlfo_t envlfo; fluid_rvoice_dsp_t dsp; fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ fluid_rvoice_buffers_t buffers; }; int fluid_rvoice_write(fluid_rvoice_t* voice, fluid_real_t *dsp_buf); void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, fluid_real_t* dsp_buf, int samplecount, fluid_real_t** dest_bufs, int dest_bufcount); void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers, unsigned int bufnum, fluid_real_t value); void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, unsigned int bufnum, int mapping); /* Dynamic update functions */ void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks); void fluid_rvoice_voiceoff(fluid_rvoice_t* voice); void fluid_rvoice_reset(fluid_rvoice_t* voice); void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t output_rate); void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int interp_method); void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t root_pitch_hz); void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value); void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value); void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value); void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value); void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value); void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value); void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value); /* defined in fluid_rvoice_dsp.c */ void fluid_rvoice_dsp_config (void); int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice); int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice); int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice); int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice); #endif fluidsynth-1.1.9/src/rvoice/fluid_rvoice_dsp.c000066400000000000000000000552331322272076000214660ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 "fluidsynth_priv.h" #include "fluid_phase.h" #include "fluid_rvoice.h" #include "fluid_sys.h" /* Purpose: * * Interpolates audio data (obtains values between the samples of the original * waveform data). * * Variables loaded from the voice structure (assigned in fluid_voice_write()): * - dsp_data: Pointer to the original waveform data * - dsp_phase: The position in the original waveform data. * This has an integer and a fractional part (between samples). * - dsp_phase_incr: For each output sample, the position in the original * waveform advances by dsp_phase_incr. This also has an integer * part and a fractional part. * If a sample is played at root pitch (no pitch change), * dsp_phase_incr is integer=1 and fractional=0. * - dsp_amp: The current amplitude envelope value. * - dsp_amp_incr: The changing rate of the amplitude envelope. * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) */ /* 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) { v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted); /* Hamming window */ v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (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"); } /* No interpolation. Just take the sample, which is closest to * the playback pointer. Questionable quality, but very * efficient. */ int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int end_index; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = voice->is_looping; end_index = looping ? voice->loopend - 1 : voice->end; while (1) { dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ /* interpolate sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ dsp_amp += dsp_amp_incr; } /* break out if not looping (buffer may not be full) */ if (!looping) break; /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); voice->has_looped = 1; } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* Straight line interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int end_index; short int point; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = voice->is_looping; /* last index before 2nd interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 1; /* 2nd interpolation point to use at end of loop or sample */ if (looping) point = dsp_data[voice->loopstart]; /* loop start */ else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */ while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + coeffs[1] * dsp_data[dsp_phase_index+1]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the last point */ /* interpolate within last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + coeffs[1] * point); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; /* increment amplitude */ } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start (if past */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); voice->has_looped = 1; } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index--; /* set end back to second to last sample point */ } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* 4th order (cubic) interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int start_index, end_index; short int start_point, end_point1, end_point2; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = voice->is_looping; /* last index before 4th interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 2; if (voice->has_looped) /* set start_index and start point if looped or not */ { start_index = voice->loopstart; start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */ } else { start_index = voice->start; start_point = dsp_data[voice->start]; /* just duplicate the point */ } /* get points off the end (loop start if looping, duplicate point if end) */ if (looping) { end_point1 = dsp_data[voice->loopstart]; end_point2 = dsp_data[voice->loopstart + 1]; } else { end_point1 = dsp_data[voice->end]; end_point2 = end_point1; } while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * dsp_data[dsp_phase_index+2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * dsp_data[dsp_phase_index+2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the 2nd to last point */ /* interpolate within 2nd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * end_point1); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the last point */ /* interpolate within the last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * end_point1 + coeffs[3] * end_point2); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); if (!voice->has_looped) { voice->has_looped = 1; start_index = voice->loopstart; start_point = dsp_data[voice->loopend - 1]; } } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index -= 2; /* set end back to third to last sample point */ } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* 7th order interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int start_index, end_index; short int start_points[3]; short int end_points[3]; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on * the 4th sample point */ fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000); /* voice is currently looping? */ looping = voice->is_looping; /* last index before 7th interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 3; if (voice->has_looped) /* set start_index and start point if looped or not */ { start_index = voice->loopstart; start_points[0] = dsp_data[voice->loopend - 1]; start_points[1] = dsp_data[voice->loopend - 2]; start_points[2] = dsp_data[voice->loopend - 3]; } else { start_index = voice->start; start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */ start_points[1] = start_points[0]; start_points[2] = start_points[0]; } /* get the 3 points off the end (loop start if looping, duplicate point if end) */ if (looping) { end_points[0] = dsp_data[voice->loopstart]; end_points[1] = dsp_data[voice->loopstart + 1]; end_points[2] = dsp_data[voice->loopstart + 2]; } else { end_points[0] = dsp_data[voice->end]; end_points[1] = end_points[0]; end_points[2] = end_points[0]; } while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[2] + coeffs[1] * (fluid_real_t)start_points[1] + coeffs[2] * (fluid_real_t)start_points[0] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index++; /* interpolate 2nd to first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[1] + coeffs[1] * (fluid_real_t)start_points[0] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index++; /* interpolate 3rd to first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[0] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index -= 2; /* set back to original start index */ /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the 3rd to last point */ /* interpolate within 3rd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)end_points[0]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the 2nd to last point */ /* interpolate within 2nd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)end_points[0] + coeffs[6] * (fluid_real_t)end_points[1]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the last point */ /* interpolate within last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)end_points[0] + coeffs[5] * (fluid_real_t)end_points[1] + coeffs[6] * (fluid_real_t)end_points[2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); if (!voice->has_looped) { voice->has_looped = 1; start_index = voice->loopstart; start_points[0] = dsp_data[voice->loopend - 1]; start_points[1] = dsp_data[voice->loopend - 2]; start_points[2] = dsp_data[voice->loopend - 3]; } } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index -= 3; /* set end back to 4th to last sample point */ } /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on * the 4th sample point (correct back to real value) */ fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000); voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } fluidsynth-1.1.9/src/rvoice/fluid_rvoice_event.c000066400000000000000000000245341322272076000220210ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_rvoice_event.h" #include "fluid_rvoice.h" #include "fluid_rvoice_mixer.h" #include "fluid_iir_filter.h" #include "fluid_lfo.h" #include "fluid_adsr_env.h" #define EVENTFUNC_0(proc, type) \ if (event->method == proc) { \ proc((type) event->object); \ return; } #define EVENTFUNC_R1(proc, type) \ if (event->method == proc) { \ if(event->intparam != 0) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \ proc((type) event->object, event->realparams[0]); \ return; } #define EVENTFUNC_PTR(proc, type, type2) \ if (event->method == proc) { \ proc((type) event->object, (type2) event->ptr); \ return; } #define EVENTFUNC_I1(proc, type) \ if (event->method == proc) { \ if(event->realparams[0] != 0.0f) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \ proc((type) event->object, event->intparam); \ return; } #define EVENTFUNC_IR(proc, type) \ if (event->method == proc) { \ proc((type) event->object, event->intparam, event->realparams[0]); \ return; } #define EVENTFUNC_ALL(proc, type) \ if (event->method == proc) { \ proc((type) event->object, event->intparam, event->realparams[0], \ event->realparams[1], event->realparams[2], event->realparams[3], \ event->realparams[4]); \ return; } #define EVENTFUNC_R4(proc, type) \ if (event->method == proc) { \ proc((type) event->object, event->intparam, event->realparams[0], \ event->realparams[1], event->realparams[2], event->realparams[3]); \ return; } static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t* handler, const fluid_rvoice_event_t* src_event); void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event) { EVENTFUNC_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*); EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*); EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*); EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*); EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*); EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*); EVENTFUNC_IR(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*); EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*); EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_output_rate, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_root_pitch_hz, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_synth_gain, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_pitch, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_attenuation, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_min_attenuation_cB, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_viblfo_to_pitch, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_pitch, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_vol, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_fc, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_modenv_to_fc, fluid_rvoice_t*); EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_interp_method, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_start, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_end, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_loopstart, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_loopend, fluid_rvoice_t*); EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*); EVENTFUNC_PTR(fluid_rvoice_set_sample, fluid_rvoice_t*, fluid_sample_t*); EVENTFUNC_R1(fluid_rvoice_mixer_set_samplerate, fluid_rvoice_mixer_t*); EVENTFUNC_I1(fluid_rvoice_mixer_set_polyphony, fluid_rvoice_mixer_t*); EVENTFUNC_I1(fluid_rvoice_mixer_set_reverb_enabled, fluid_rvoice_mixer_t*); EVENTFUNC_I1(fluid_rvoice_mixer_set_chorus_enabled, fluid_rvoice_mixer_t*); EVENTFUNC_I1(fluid_rvoice_mixer_set_mix_fx, fluid_rvoice_mixer_t*); EVENTFUNC_0(fluid_rvoice_mixer_reset_fx, fluid_rvoice_mixer_t*); EVENTFUNC_0(fluid_rvoice_mixer_reset_reverb, fluid_rvoice_mixer_t*); EVENTFUNC_0(fluid_rvoice_mixer_reset_chorus, fluid_rvoice_mixer_t*); EVENTFUNC_IR(fluid_rvoice_mixer_set_threads, fluid_rvoice_mixer_t*); EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*); EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*); FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method); } /** * In order to be able to push more than one event atomically, * use push for all events, then use flush to commit them to the * queue. If threadsafe is false, all events are processed immediately. */ int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler, void* method, void* object, int intparam, fluid_real_t realparam) { fluid_rvoice_event_t local_event; local_event.method = method; local_event.object = object; local_event.intparam = intparam; local_event.realparams[0] = realparam; return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler, void* method, void* object, void* ptr) { fluid_rvoice_event_t local_event; local_event.method = method; local_event.object = object; local_event.ptr = ptr; return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } int fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler, void* method, void* object, int intparam, fluid_real_t r1, fluid_real_t r2, fluid_real_t r3, fluid_real_t r4, fluid_real_t r5) { fluid_rvoice_event_t local_event; local_event.method = method; local_event.object = object; local_event.intparam = intparam; local_event.realparams[0] = r1; local_event.realparams[1] = r2; local_event.realparams[2] = r3; local_event.realparams[3] = r4; local_event.realparams[4] = r5; return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); } static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t* handler, const fluid_rvoice_event_t* src_event) { fluid_rvoice_event_t* event; int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1); event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored); if (event == NULL) { fluid_atomic_int_add(&handler->queue_stored, -1); FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); return FLUID_FAILED; // Buffer full... } memcpy(event, src_event, sizeof(*event)); return FLUID_OK; } static void finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice) { fluid_rvoice_eventhandler_t* eventhandler = userdata; fluid_rvoice_t** vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0); if (vptr == NULL) return; // Buffer full *vptr = rvoice; fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1); } fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize, int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate) { fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); if (eventhandler == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } eventhandler->mixer = NULL; eventhandler->queue = NULL; eventhandler->finished_voices = NULL; /* HACK 2017-08-27: always enforce threadsafety, i.e. enforce enqueuing events * otherwise we mess up rendering if more than one block is requested by the user * because fluid_rvoice_eventhandler_dispatch_count() always stays zero causing * that too many events are dispatched too early, causing incorrectly timed audio */ eventhandler->is_threadsafe = TRUE; eventhandler->queue_stored = 0; eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size, sizeof(fluid_rvoice_t*)); if (eventhandler->finished_voices == NULL) goto error_recovery; eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t)); if (eventhandler->queue == NULL) goto error_recovery; eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, sample_rate); if (eventhandler->mixer == NULL) goto error_recovery; fluid_rvoice_mixer_set_finished_voices_callback(eventhandler->mixer, finished_voice_callback, eventhandler); return eventhandler; error_recovery: delete_fluid_rvoice_eventhandler(eventhandler); return NULL; } int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler) { return fluid_ringbuffer_get_count(handler->queue); } /** * Call fluid_rvoice_event_dispatch for all events in queue * @return number of events dispatched */ int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler) { fluid_rvoice_event_t* event; int result = 0; while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) { fluid_rvoice_event_dispatch(event); result++; fluid_ringbuffer_next_outptr(handler->queue); } return result; } void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler) { if (handler == NULL) return; delete_fluid_rvoice_mixer(handler->mixer); delete_fluid_ringbuffer(handler->queue); delete_fluid_ringbuffer(handler->finished_voices); FLUID_FREE(handler); } fluidsynth-1.1.9/src/rvoice/fluid_rvoice_event.h000066400000000000000000000101141322272076000220130ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RVOICE_EVENT_H #define _FLUID_RVOICE_EVENT_H #include "fluidsynth_priv.h" #include "fluid_rvoice_mixer.h" #include "fluid_ringbuffer.h" #define EVENT_REAL_PARAMS (5) typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t; typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; struct _fluid_rvoice_event_t { void* method; void* object; void* ptr; int intparam; fluid_real_t realparams[EVENT_REAL_PARAMS]; }; void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event); /* * Bridge between the renderer thread and the midi state thread. * If is_threadsafe is true, that means fluid_rvoice_eventhandler_fetch_all * can be called in parallell with fluid_rvoice_eventhandler_push/flush */ struct _fluid_rvoice_eventhandler_t { int is_threadsafe; /* False for optimal performance, true for atomic operations */ fluid_ringbuffer_t* queue; /**< List of fluid_rvoice_event_t */ int queue_stored; /**< Extras pushed but not flushed */ fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */ fluid_rvoice_mixer_t* mixer; }; fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler( int is_threadsafe, int queuesize, int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate); void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*); int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t*); int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t*); static FLUID_INLINE void fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t* handler) { int queue_stored = fluid_atomic_int_get(&handler->queue_stored); if (queue_stored > 0) { fluid_atomic_int_set(&handler->queue_stored, 0); fluid_ringbuffer_next_inptr(handler->queue, queue_stored); } } /** * @return next finished voice, or NULL if nothing in queue */ static FLUID_INLINE fluid_rvoice_t* fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t* handler) { void* result = fluid_ringbuffer_get_outptr(handler->finished_voices); if (result == NULL) return NULL; result = * (fluid_rvoice_t**) result; fluid_ringbuffer_next_outptr(handler->finished_voices); return result; } int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler, void* method, void* object, int intparam, fluid_real_t realparam); int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler, void* method, void* object, void* ptr); int fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler, void* method, void* object, int intparam, fluid_real_t r1, fluid_real_t r2, fluid_real_t r3, fluid_real_t r4, fluid_real_t r5); static FLUID_INLINE void fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler, fluid_rvoice_t* rvoice) { if (handler->is_threadsafe) fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice, handler->mixer, rvoice); else fluid_rvoice_mixer_add_voice(handler->mixer, rvoice); } #endif fluidsynth-1.1.9/src/rvoice/fluid_rvoice_mixer.c000066400000000000000000000757431322272076000220340ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_rvoice_mixer.h" #include "fluid_rvoice.h" #include "fluid_sys.h" #include "fluid_rev.h" #include "fluid_chorus.h" #include "fluidsynth_priv.h" #include "fluid_ladspa.h" #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 typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t; struct _fluid_mixer_buffers_t { fluid_rvoice_mixer_t* mixer; /**< Owner of object */ #ifdef ENABLE_MIXER_THREADS fluid_thread_t* thread; /**< Thread object */ #endif fluid_rvoice_t** finished_voices; /* List of voices who have finished */ int finished_voice_count; int ready; /**< Atomic: buffers are ready for mixing */ int buf_blocks; /**< Number of blocks allocated in the buffers */ int buf_count; fluid_real_t** left_buf; fluid_real_t** right_buf; int fx_buf_count; fluid_real_t** fx_left_buf; fluid_real_t** fx_right_buf; }; typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t; struct _fluid_mixer_fx_t { fluid_revmodel_t* reverb; /**< Reverb unit */ fluid_chorus_t* chorus; /**< Chorus unit */ int with_reverb; /**< Should the synth use the built-in reverb unit? */ int with_chorus; /**< Should the synth use the built-in chorus unit? */ int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */ }; struct _fluid_rvoice_mixer_t { fluid_mixer_fx_t fx; fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Used by mixer only: Receive this callback every time a voice is removed */ void* remove_voice_callback_userdata; fluid_rvoice_t** rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */ int polyphony; /**< Read-only: Length of voices array */ int active_voices; /**< Read-only: Number of non-null voices */ int current_blockcount; /**< Read-only: how many blocks to process this time */ #ifdef LADSPA fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ #endif #ifdef ENABLE_MIXER_THREADS // int sleeping_threads; /**< Atomic: number of threads currently asleep */ // int active_threads; /**< Atomic: number of threads in the thread loop */ int threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ int current_rvoice; /**< Atomic: for the threads to know next voice to */ fluid_cond_t* wakeup_threads; /**< Signalled when the threads should wake up */ fluid_cond_mutex_t* wakeup_threads_m; /**< wakeup_threads mutex companion */ fluid_cond_t* thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */ fluid_cond_mutex_t* thread_ready_m; /**< thread_ready mutex companion */ int thread_count; /**< Number of extra mixer threads for multi-core rendering */ fluid_mixer_buffers_t* threads; /**< Array of mixer threads (thread_count in length) */ #endif }; static FLUID_INLINE void fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer) { int i; fluid_profile_ref_var(prof_ref); if (mixer->fx.with_reverb) { if (mixer->fx.mix_fx_to_out) { for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) fluid_revmodel_processmix(mixer->fx.reverb, &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], &mixer->buffers.left_buf[0][i], &mixer->buffers.right_buf[0][i]); } else { for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) fluid_revmodel_processreplace(mixer->fx.reverb, &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i], &mixer->buffers.fx_right_buf[SYNTH_REVERB_CHANNEL][i]); } fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref); } if (mixer->fx.with_chorus) { if (mixer->fx.mix_fx_to_out) { for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) fluid_chorus_processmix(mixer->fx.chorus, &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], &mixer->buffers.left_buf[0][i], &mixer->buffers.right_buf[0][i]); } else { for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) fluid_chorus_processreplace(mixer->fx.chorus, &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i], &mixer->buffers.fx_right_buf[SYNTH_CHORUS_CHANNEL][i]); } fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref); } #ifdef LADSPA /* Run the signal through the LADSPA Fx unit */ if (mixer->LADSPA_FxUnit) { int j; FLUID_DECLARE_VLA(fluid_real_t*, left_buf, mixer->buffers.buf_count); FLUID_DECLARE_VLA(fluid_real_t*, right_buf, mixer->buffers.buf_count); FLUID_DECLARE_VLA(fluid_real_t*, fx_left_buf, mixer->buffers.fx_buf_count); FLUID_DECLARE_VLA(fluid_real_t*, fx_right_buf, mixer->buffers.fx_buf_count); for (j=0; j < mixer->buffers.buf_count; j++) { left_buf[j] = mixer->buffers.left_buf[j]; right_buf[j] = mixer->buffers.right_buf[j]; } for (j=0; j < mixer->buffers.fx_buf_count; j++) { fx_left_buf[j] = mixer->buffers.fx_left_buf[j]; fx_right_buf[j] = mixer->buffers.fx_right_buf[j]; } for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) { fluid_LADSPA_run(mixer->LADSPA_FxUnit, left_buf, right_buf, fx_left_buf, fx_right_buf); for (j=0; j < mixer->buffers.buf_count; j++) { left_buf[j] += FLUID_BUFSIZE; right_buf[j] += FLUID_BUFSIZE; } for (j=0; j < mixer->buffers.fx_buf_count; j++) { fx_left_buf[j] += FLUID_BUFSIZE; fx_right_buf[j] += FLUID_BUFSIZE; } } fluid_check_fpe("LADSPA"); } #endif } /** * During rendering, rvoices might be finished. Set this callback * for getting a callback any time the rvoice is finished. */ void fluid_rvoice_mixer_set_finished_voices_callback( fluid_rvoice_mixer_t* mixer, void (*func)(void*, fluid_rvoice_t*), void* userdata) { mixer->remove_voice_callback_userdata = userdata; mixer->remove_voice_callback = func; } /** * Synthesize one voice and add to buffer. * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means * voice has been finished, removed and possibly replaced with another voice. * @return Number of samples written */ static int fluid_mix_one(fluid_rvoice_t* rvoice, fluid_real_t** bufs, unsigned int bufcount, int blockcount) { int i, result = 0; FLUID_DECLARE_VLA(fluid_real_t, local_buf, FLUID_BUFSIZE*blockcount); for (i=0; i < blockcount; i++) { int s = fluid_rvoice_write(rvoice, &local_buf[FLUID_BUFSIZE*i]); if (s == -1) { s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */ FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t)); } result += s; if (s < FLUID_BUFSIZE) { break; } } fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount); return result; } /** * Glue to get fluid_rvoice_buffers_mix what it wants * Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling */ static FLUID_INLINE int fluid_mixer_buffers_prepare(fluid_mixer_buffers_t* buffers, fluid_real_t** outbufs) { fluid_real_t* reverb_buf, *chorus_buf; int i; /* Set up the reverb / chorus buffers only, when the effect is * enabled on synth level. Nonexisting buffers are detected in the * DSP loop. Not sending the reverb / chorus signal saves some time * in that case. */ reverb_buf = buffers->mixer->fx.with_reverb ? buffers->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL; chorus_buf = buffers->mixer->fx.with_chorus ? buffers->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL; outbufs[buffers->buf_count*2 + SYNTH_REVERB_CHANNEL] = reverb_buf; outbufs[buffers->buf_count*2 + SYNTH_CHORUS_CHANNEL] = chorus_buf; /* The output associated with a MIDI channel is wrapped around * using the number of audio groups as modulo divider. This is * typically the number of output channels on the 'sound card', * as long as the LADSPA Fx unit is not used. In case of LADSPA * unit, think of it as subgroups on a mixer. * * For example: Assume that the number of groups is set to 2. * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2, * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to * output 2, 3, 6, 9, 12 etc to output 3. */ for (i = 0; i < buffers->buf_count; i++) { outbufs[i*2] = buffers->left_buf[i]; outbufs[i*2+1] = buffers->right_buf[i]; } return buffers->buf_count*2 + 2; } static FLUID_INLINE void fluid_finish_rvoice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* rvoice) { if (buffers->finished_voice_count < buffers->mixer->polyphony) buffers->finished_voices[buffers->finished_voice_count++] = rvoice; else FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony"); } static void fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t* buffers) { int i,j; for (i=0; i < buffers->finished_voice_count; i++) { fluid_rvoice_t* v = buffers->finished_voices[i]; int* av = &buffers->mixer->active_voices; for (j=0; j < *av; j++) { if (v == buffers->mixer->rvoices[j]) { (*av)--; /* Pack the array */ if (j < *av) buffers->mixer->rvoices[j] = buffers->mixer->rvoices[*av]; } } if (buffers->mixer->remove_voice_callback) buffers->mixer->remove_voice_callback( buffers->mixer->remove_voice_callback_userdata, v); } buffers->finished_voice_count = 0; } static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t* mixer) { #ifdef ENABLE_MIXER_THREADS int i; for (i=0; i < mixer->thread_count; i++) fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]); #endif fluid_mixer_buffer_process_finished_voices(&mixer->buffers); } static FLUID_INLINE void fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* voice, fluid_real_t** bufs, unsigned int bufcount) { int s = fluid_mix_one(voice, bufs, bufcount, buffers->mixer->current_blockcount); if (s < buffers->mixer->current_blockcount * FLUID_BUFSIZE) { fluid_finish_rvoice(buffers, voice); } } /* static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* voice) { int i, retval=0; int fvc = buffers->finished_voice_count; for (i=0; i < fvc; i++) if (buffers->finished_voices[i] == voice) { fvc--; if (i < fvc) buffers->finished_voices[i] = buffers->finished_voices[fvc]; retval++; } fvc = buffers->finished_voice_count; return retval; } */ int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice) { int i; if (mixer->active_voices < mixer->polyphony) { mixer->rvoices[mixer->active_voices++] = voice; return FLUID_OK; } /* See if any voices just finished, if so, take its place. This can happen in voice overflow conditions. */ for (i=0; i < mixer->active_voices; i++) { if (mixer->rvoices[i] == voice) { FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); return FLUID_FAILED; } if (mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) { fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); mixer->rvoices[i] = voice; return FLUID_OK; } } /* This should never happen */ FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); return FLUID_FAILED; } static int fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t* buffers, int value) { void* newptr; if (buffers->finished_voice_count > value) return FLUID_FAILED; newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t*)); if (newptr == NULL && value > 0) return FLUID_FAILED; buffers->finished_voices = newptr; return FLUID_OK; } /** * Update polyphony - max number of voices (NOTE: not hard real-time capable) * @return FLUID_OK or FLUID_FAILED */ int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value) { void* newptr; if (handler->active_voices > value) return FLUID_FAILED; newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*)); if (newptr == NULL) return FLUID_FAILED; handler->rvoices = newptr; if (fluid_mixer_buffers_update_polyphony(&handler->buffers, value) == FLUID_FAILED) return FLUID_FAILED; #ifdef ENABLE_MIXER_THREADS { int i; for (i=0; i < handler->thread_count; i++) if (fluid_mixer_buffers_update_polyphony(&handler->threads[i], value) == FLUID_FAILED) return FLUID_FAILED; } #endif handler->polyphony = value; return FLUID_OK; } static void fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer) { int i; FLUID_DECLARE_VLA(fluid_real_t*, bufs, mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); fluid_profile_ref_var(prof_ref); for (i=0; i < mixer->active_voices; i++) { fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs, bufcount); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref); } } static FLUID_INLINE void fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers) { int i; int size = buffers->mixer->current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); /* TODO: Optimize by only zero out the buffers we actually use later on. */ for (i=0; i < buffers->buf_count; i++) { FLUID_MEMSET(buffers->left_buf[i], 0, size); FLUID_MEMSET(buffers->right_buf[i], 0, size); } for (i=0; i < buffers->fx_buf_count; i++) { FLUID_MEMSET(buffers->fx_left_buf[i], 0, size); FLUID_MEMSET(buffers->fx_right_buf[i], 0, size); } } static int fluid_mixer_buffers_init(fluid_mixer_buffers_t* buffers, fluid_rvoice_mixer_t* mixer) { int i, samplecount; buffers->mixer = mixer; buffers->buf_count = buffers->mixer->buffers.buf_count; buffers->fx_buf_count = buffers->mixer->buffers.fx_buf_count; buffers->buf_blocks = buffers->mixer->buffers.buf_blocks; samplecount = FLUID_BUFSIZE * buffers->buf_blocks; /* Left and right audio buffers */ buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count); buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count); if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } FLUID_MEMSET(buffers->left_buf, 0, buffers->buf_count * sizeof(fluid_real_t*)); FLUID_MEMSET(buffers->right_buf, 0, buffers->buf_count * sizeof(fluid_real_t*)); for (i = 0; i < buffers->buf_count; i++) { buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } } /* Effects audio buffers */ buffers->fx_left_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count); buffers->fx_right_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count); if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } FLUID_MEMSET(buffers->fx_left_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*)); FLUID_MEMSET(buffers->fx_right_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*)); for (i = 0; i < buffers->fx_buf_count; i++) { buffers->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); buffers->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount); if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } } buffers->finished_voices = NULL; if (fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony) == FLUID_FAILED) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } return 1; } /** * Note: Not hard real-time capable (calls malloc) */ void fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate) { int i; if (mixer->fx.chorus) delete_fluid_chorus(mixer->fx.chorus); mixer->fx.chorus = new_fluid_chorus(samplerate); if (mixer->fx.reverb) fluid_revmodel_samplerate_change(mixer->fx.reverb, samplerate); for (i=0; i < mixer->active_voices; i++) fluid_rvoice_set_output_rate(mixer->rvoices[i], samplerate); } /** * @param buf_count number of primary stereo buffers * @param fx_buf_count number of stereo effect buffers */ fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate) { fluid_rvoice_mixer_t* mixer = FLUID_NEW(fluid_rvoice_mixer_t); if (mixer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t)); mixer->buffers.buf_count = buf_count; mixer->buffers.fx_buf_count = fx_buf_count; mixer->buffers.buf_blocks = FLUID_MIXER_MAX_BUFFERS_DEFAULT; /* allocate the reverb module */ mixer->fx.reverb = new_fluid_revmodel(sample_rate); mixer->fx.chorus = new_fluid_chorus(sample_rate); if (mixer->fx.reverb == NULL || mixer->fx.chorus == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_rvoice_mixer(mixer); return NULL; } if (!fluid_mixer_buffers_init(&mixer->buffers, mixer)) { delete_fluid_rvoice_mixer(mixer); return NULL; } #ifdef ENABLE_MIXER_THREADS mixer->thread_ready = new_fluid_cond(); mixer->wakeup_threads = new_fluid_cond(); mixer->thread_ready_m = new_fluid_cond_mutex(); mixer->wakeup_threads_m = new_fluid_cond_mutex(); if (!mixer->thread_ready || !mixer->wakeup_threads || !mixer->thread_ready_m || !mixer->wakeup_threads_m) { delete_fluid_rvoice_mixer(mixer); return NULL; } #endif return mixer; } static void fluid_mixer_buffers_free(fluid_mixer_buffers_t* buffers) { int i; FLUID_FREE(buffers->finished_voices); /* free all the sample buffers */ if (buffers->left_buf != NULL) { for (i = 0; i < buffers->buf_count; i++) { if (buffers->left_buf[i] != NULL) { FLUID_FREE(buffers->left_buf[i]); } } FLUID_FREE(buffers->left_buf); } if (buffers->right_buf != NULL) { for (i = 0; i < buffers->buf_count; i++) { if (buffers->right_buf[i] != NULL) { FLUID_FREE(buffers->right_buf[i]); } } FLUID_FREE(buffers->right_buf); } if (buffers->fx_left_buf != NULL) { for (i = 0; i < buffers->fx_buf_count; i++) { if (buffers->fx_left_buf[i] != NULL) { FLUID_FREE(buffers->fx_left_buf[i]); } } FLUID_FREE(buffers->fx_left_buf); } if (buffers->fx_right_buf != NULL) { for (i = 0; i < buffers->fx_buf_count; i++) { if (buffers->fx_right_buf[i] != NULL) { FLUID_FREE(buffers->fx_right_buf[i]); } } FLUID_FREE(buffers->fx_right_buf); } } void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer) { if (!mixer) return; fluid_rvoice_mixer_set_threads(mixer, 0, 0); #ifdef ENABLE_MIXER_THREADS if (mixer->thread_ready) delete_fluid_cond(mixer->thread_ready); if (mixer->wakeup_threads) delete_fluid_cond(mixer->wakeup_threads); if (mixer->thread_ready_m) delete_fluid_cond_mutex(mixer->thread_ready_m); if (mixer->wakeup_threads_m) delete_fluid_cond_mutex(mixer->wakeup_threads_m); #endif fluid_mixer_buffers_free(&mixer->buffers); if (mixer->fx.reverb) delete_fluid_revmodel(mixer->fx.reverb); if (mixer->fx.chorus) delete_fluid_chorus(mixer->fx.chorus); FLUID_FREE(mixer->rvoices); FLUID_FREE(mixer); } #ifdef LADSPA void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, fluid_LADSPA_FxUnit_t* ladspa) { mixer->LADSPA_FxUnit = ladspa; } #endif void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on) { mixer->fx.with_reverb = on; } void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on) { mixer->fx.with_chorus = on; } void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on) { mixer->fx.mix_fx_to_out = on; } void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set, int nr, double level, double speed, double depth_ms, int type) { fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type); } void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set, double roomsize, double damping, double width, double level) { fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level); } void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer) { fluid_revmodel_reset(mixer->fx.reverb); fluid_chorus_reset(mixer->fx.chorus); } void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer) { fluid_revmodel_reset(mixer->fx.reverb); } void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer) { fluid_chorus_reset(mixer->fx.chorus); } int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer, fluid_real_t*** left, fluid_real_t*** right) { *left = mixer->buffers.left_buf; *right = mixer->buffers.right_buf; return mixer->buffers.buf_count; } int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer, fluid_real_t*** fx_left, fluid_real_t*** fx_right) { *fx_left = mixer->buffers.fx_left_buf; *fx_right = mixer->buffers.fx_right_buf; return mixer->buffers.fx_buf_count; } int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t* mixer) { return mixer->buffers.buf_blocks; } #ifdef ENABLE_MIXER_THREADS static FLUID_INLINE fluid_rvoice_t* fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer) { int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1); if (i >= mixer->active_voices) return NULL; return mixer->rvoices[i]; } #define THREAD_BUF_PROCESSING 0 #define THREAD_BUF_VALID 1 #define THREAD_BUF_NODATA 2 #define THREAD_BUF_TERMINATE 3 /* Core thread function (processes voices in parallel to primary synthesis thread) */ static void fluid_mixer_thread_func (void* data) { fluid_mixer_buffers_t* buffers = data; fluid_rvoice_mixer_t* mixer = buffers->mixer; int hasValidData = 0; FLUID_DECLARE_VLA(fluid_real_t*, bufs, buffers->buf_count*2 + buffers->fx_buf_count*2); int bufcount = 0; while (!fluid_atomic_int_get(&mixer->threads_should_terminate)) { fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer); if (rvoice == NULL) { // if no voices: signal rendered buffers, sleep fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA); fluid_cond_mutex_lock(mixer->thread_ready_m); fluid_cond_signal(mixer->thread_ready); fluid_cond_mutex_unlock(mixer->thread_ready_m); fluid_cond_mutex_lock(mixer->wakeup_threads_m); while (1) { int j = fluid_atomic_int_get(&buffers->ready); if (j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE) break; fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m); } fluid_cond_mutex_unlock(mixer->wakeup_threads_m); hasValidData = 0; } else { // else: if buffer is not zeroed, zero buffers if (!hasValidData) { fluid_mixer_buffers_zero(buffers); bufcount = fluid_mixer_buffers_prepare(buffers, bufs); hasValidData = 1; } // then render voice to buffers fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount); } } } static void fluid_mixer_buffers_mix(fluid_mixer_buffers_t* dest, fluid_mixer_buffers_t* src) { int i,j; int scount = dest->mixer->current_blockcount * FLUID_BUFSIZE; int minbuf; minbuf = dest->buf_count; if (minbuf > src->buf_count) minbuf = src->buf_count; for (i=0; i < minbuf; i++) { for (j=0; j < scount; j++) { dest->left_buf[i][j] += src->left_buf[i][j]; dest->right_buf[i][j] += src->right_buf[i][j]; } } minbuf = dest->fx_buf_count; if (minbuf > src->fx_buf_count) minbuf = src->fx_buf_count; for (i=0; i < minbuf; i++) { for (j=0; j < scount; j++) { dest->fx_left_buf[i][j] += src->fx_left_buf[i][j]; dest->fx_right_buf[i][j] += src->fx_right_buf[i][j]; } } } /** * Go through all threads and see if someone is finished for mixing */ static FLUID_INLINE int fluid_mixer_mix_in(fluid_rvoice_mixer_t* mixer, int extra_threads) { int i, result, hasmixed; do { hasmixed = 0; result = 0; for (i=0; i < extra_threads; i++) { int j = fluid_atomic_int_get(&mixer->threads[i].ready); switch (j) { case THREAD_BUF_PROCESSING: result = 1; break; case THREAD_BUF_VALID: fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA); fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i]); hasmixed = 1; break; } } } while (hasmixed); return result; } static void fluid_render_loop_multithread(fluid_rvoice_mixer_t* mixer) { int i, bufcount; //int scount = mixer->current_blockcount * FLUID_BUFSIZE; FLUID_DECLARE_VLA(fluid_real_t*, bufs, mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); // How many threads should we start this time? int extra_threads = mixer->active_voices / VOICES_PER_THREAD; if (extra_threads > mixer->thread_count) extra_threads = mixer->thread_count; if (extra_threads == 0) { // No extra threads? No thread overhead! fluid_render_loop_singlethread(mixer); return; } bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); // Prepare voice list fluid_cond_mutex_lock(mixer->wakeup_threads_m); fluid_atomic_int_set(&mixer->current_rvoice, 0); for (i=0; i < extra_threads; i++) fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING); // Signal threads to wake up fluid_cond_broadcast(mixer->wakeup_threads); fluid_cond_mutex_unlock(mixer->wakeup_threads_m); // If thread is finished, mix it in while (fluid_mixer_mix_in(mixer, extra_threads)) { // Otherwise get a voice and render it fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer); if (rvoice != NULL) { fluid_profile_ref_var(prof_ref); fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref); //test++; } else { // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock int is_processing = 0; //waits++; fluid_cond_mutex_lock(mixer->thread_ready_m); for (i=0; i < extra_threads; i++) if (fluid_atomic_int_get(&mixer->threads[i].ready) == THREAD_BUF_PROCESSING) is_processing = 1; if (is_processing) fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m); fluid_cond_mutex_unlock(mixer->thread_ready_m); } } //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d", // mixer->current_blockcount, test, mixer->active_voices, waits); } #endif /** * Update amount of extra mixer threads. * @param thread_count Number of extra mixer threads for multi-core rendering * @param prio_level real-time prio level for the extra mixer threads */ void fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count, int prio_level) { #ifdef ENABLE_MIXER_THREADS char name[16]; int i; // Kill all existing threads first if (mixer->thread_count) { fluid_atomic_int_set(&mixer->threads_should_terminate, 1); // Signal threads to wake up fluid_cond_mutex_lock(mixer->wakeup_threads_m); for (i=0; i < mixer->thread_count; i++) fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); fluid_cond_broadcast(mixer->wakeup_threads); fluid_cond_mutex_unlock(mixer->wakeup_threads_m); for (i=0; i < mixer->thread_count; i++) { if (mixer->threads[i].thread) { fluid_thread_join(mixer->threads[i].thread); delete_fluid_thread(mixer->threads[i].thread); } fluid_mixer_buffers_free(&mixer->threads[i]); } FLUID_FREE(mixer->threads); mixer->thread_count = 0; mixer->threads = NULL; } if (thread_count == 0) return; // Now prepare the new threads fluid_atomic_int_set(&mixer->threads_should_terminate, 0); mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count); if (mixer->threads == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return; } FLUID_MEMSET(mixer->threads, 0, thread_count*sizeof(fluid_mixer_buffers_t)); mixer->thread_count = thread_count; for (i=0; i < thread_count; i++) { fluid_mixer_buffers_t* b = &mixer->threads[i]; if (!fluid_mixer_buffers_init(b, mixer)) return; fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); g_snprintf (name, sizeof (name), "mixer%d", i); b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0); if (!b->thread) return; } #endif } /** * Synthesize audio into buffers * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples * @return number of blocks rendered */ int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount) { fluid_profile_ref_var(prof_ref); mixer->current_blockcount = blockcount > mixer->buffers.buf_blocks ? mixer->buffers.buf_blocks : blockcount; // Zero buffers fluid_mixer_buffers_zero(&mixer->buffers); fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref); #ifdef ENABLE_MIXER_THREADS if (mixer->thread_count > 0) fluid_render_loop_multithread(mixer); else #endif fluid_render_loop_singlethread(mixer); fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref); // Process reverb & chorus fluid_rvoice_mixer_process_fx(mixer); // Call the callback and pack active voice array fluid_rvoice_mixer_process_finished_voices(mixer); return mixer->current_blockcount; } fluidsynth-1.1.9/src/rvoice/fluid_rvoice_mixer.h000066400000000000000000000061071322272076000220250ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RVOICE_MIXER_H #define _FLUID_RVOICE_MIXER_H #include "fluidsynth_priv.h" #include "fluid_rvoice.h" #include "fluid_ladspa.h" typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t; #define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) void fluid_rvoice_mixer_set_finished_voices_callback( fluid_rvoice_mixer_t* mixer, void (*func)(void*, fluid_rvoice_t*), void* userdata); int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount); int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer, fluid_real_t*** left, fluid_real_t*** right); int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer, fluid_real_t*** fx_left, fluid_real_t*** fx_right); int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t* mixer); fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate); void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t*); void fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate); void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on); void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on); void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on); int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value); int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice); void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set, int nr, double level, double speed, double depth_ms, int type); void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set, double roomsize, double damping, double width, double level); void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer); void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer); void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer); void fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count, int prio_level); #ifdef LADSPA void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, fluid_LADSPA_FxUnit_t* ladspa); #endif #endif fluidsynth-1.1.9/src/sfloader/000077500000000000000000000000001322272076000163025ustar00rootroot00000000000000fluidsynth-1.1.9/src/sfloader/fluid_defsfont.c000066400000000000000000002714411322272076000214520ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * SoundFont file loading code borrowed from Smurf SoundFont Editor * Copyright (C) 1999-2001 Josh Green * * 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_defsfont.h" /* Todo: Get rid of that 'include' */ #include "fluid_sys.h" #if LIBSNDFILE_SUPPORT #include #endif /*************************************************************** * * SFONT LOADER */ fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings) { fluid_sfloader_t* loader; loader = FLUID_NEW(fluid_sfloader_t); if (loader == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } loader->data = settings; loader->free = delete_fluid_defsfloader; loader->load = fluid_defsfloader_load; return loader; } int delete_fluid_defsfloader(fluid_sfloader_t* loader) { if (loader) { FLUID_FREE(loader); } return FLUID_OK; } fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename) { fluid_defsfont_t* defsfont; fluid_sfont_t* sfont; defsfont = new_fluid_defsfont(loader->data); if (defsfont == NULL) { return NULL; } if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) { delete_fluid_defsfont(defsfont); return NULL; } sfont = FLUID_NEW(fluid_sfont_t); if (sfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } sfont->data = defsfont; sfont->free = fluid_defsfont_sfont_delete; sfont->get_name = fluid_defsfont_sfont_get_name; sfont->get_preset = fluid_defsfont_sfont_get_preset; sfont->iteration_start = fluid_defsfont_sfont_iteration_start; sfont->iteration_next = fluid_defsfont_sfont_iteration_next; return sfont; } /*************************************************************** * * PUBLIC INTERFACE */ int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont) { if (delete_fluid_defsfont(sfont->data) != 0) { return -1; } FLUID_FREE(sfont); return 0; } char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont) { return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data); } fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s) { /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */ return NULL; } fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) { fluid_preset_t* preset = NULL; fluid_defpreset_t* defpreset; fluid_defsfont_t* defsfont = sfont->data; defpreset = fluid_defsfont_get_preset(defsfont, bank, prenum); if (defpreset == NULL) { return NULL; } if (defsfont->preset_stack_size > 0) { defsfont->preset_stack_size--; preset = defsfont->preset_stack[defsfont->preset_stack_size]; } if (!preset) preset = FLUID_NEW(fluid_preset_t); if (!preset) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } preset->sfont = sfont; preset->data = defpreset; preset->free = fluid_defpreset_preset_delete; preset->get_name = fluid_defpreset_preset_get_name; preset->get_banknum = fluid_defpreset_preset_get_banknum; preset->get_num = fluid_defpreset_preset_get_num; preset->noteon = fluid_defpreset_preset_noteon; preset->notify = NULL; return preset; } void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont) { fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data); } int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) { preset->free = fluid_defpreset_preset_delete; preset->get_name = fluid_defpreset_preset_get_name; preset->get_banknum = fluid_defpreset_preset_get_banknum; preset->get_num = fluid_defpreset_preset_get_num; preset->noteon = fluid_defpreset_preset_noteon; preset->notify = NULL; return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset); } int fluid_defpreset_preset_delete(fluid_preset_t* preset) { fluid_defpreset_t* defpreset = preset ? preset->data : NULL; fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL; if (sfont && sfont->preset_stack_size < sfont->preset_stack_capacity) { sfont->preset_stack[sfont->preset_stack_size] = preset; sfont->preset_stack_size++; } else FLUID_FREE(preset); return 0; } char* fluid_defpreset_preset_get_name(fluid_preset_t* preset) { return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data); } int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset) { return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data); } int fluid_defpreset_preset_get_num(fluid_preset_t* preset) { return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data); } int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) { return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel); } /*************************************************************** * * CACHED SAMPLEDATA LOADER */ typedef struct _fluid_cached_sampledata_t { struct _fluid_cached_sampledata_t *next; char* filename; time_t modification_time; int num_references; int mlock; const short* sampledata; unsigned int samplesize; } fluid_cached_sampledata_t; static fluid_cached_sampledata_t* all_cached_sampledata = NULL; static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT; static int fluid_get_file_modification_time(char *filename, time_t *modification_time) { #if defined(WIN32) || defined(__OS2__) *modification_time = 0; return FLUID_OK; #else struct stat buf; if (stat(filename, &buf) == -1) { return FLUID_FAILED; } *modification_time = buf.st_mtime; return FLUID_OK; #endif } static int fluid_cached_sampledata_load(char *filename, unsigned int samplepos, unsigned int samplesize, short **sampledata, int try_mlock) { fluid_file fd = NULL; short *loaded_sampledata = NULL; fluid_cached_sampledata_t* cached_sampledata = NULL; time_t modification_time; fluid_mutex_lock(cached_sampledata_mutex); if (fluid_get_file_modification_time(filename, &modification_time) == FLUID_FAILED) { FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file."); modification_time = 0; } for (cached_sampledata = all_cached_sampledata; cached_sampledata; cached_sampledata = cached_sampledata->next) { if (strcmp(filename, cached_sampledata->filename)) continue; if (cached_sampledata->modification_time != modification_time) continue; if (cached_sampledata->samplesize != samplesize) { FLUID_LOG(FLUID_ERR, "Cached size of soundfont doesn't match actual size of soundfont (cached: %u. actual: %u)", cached_sampledata->samplesize, samplesize); continue; } if (try_mlock && !cached_sampledata->mlock) { if (fluid_mlock(cached_sampledata->sampledata, samplesize) != 0) FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); else cached_sampledata->mlock = try_mlock; } cached_sampledata->num_references++; loaded_sampledata = (short*) cached_sampledata->sampledata; goto success_exit; } fd = FLUID_FOPEN(filename, "rb"); if (fd == NULL) { FLUID_LOG(FLUID_ERR, "Can't open soundfont file"); goto error_exit; } if (FLUID_FSEEK(fd, samplepos, SEEK_SET) == -1) { perror("error"); FLUID_LOG(FLUID_ERR, "Failed to seek position in data file"); goto error_exit; } loaded_sampledata = (short*) FLUID_MALLOC(samplesize); if (loaded_sampledata == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_exit; } if (FLUID_FREAD(loaded_sampledata, 1, samplesize, fd) < samplesize) { FLUID_LOG(FLUID_ERR, "Failed to read sample data"); goto error_exit; } FLUID_FCLOSE(fd); fd = NULL; cached_sampledata = (fluid_cached_sampledata_t*) FLUID_MALLOC(sizeof(fluid_cached_sampledata_t)); if (cached_sampledata == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_exit; } /* Lock the memory to disable paging. It's okay if this fails. It probably means that the user doesn't have to required permission. */ cached_sampledata->mlock = 0; if (try_mlock) { if (fluid_mlock(loaded_sampledata, samplesize) != 0) FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); else cached_sampledata->mlock = try_mlock; } /* If this machine is big endian, the sample have to byte swapped */ if (FLUID_IS_BIG_ENDIAN) { unsigned char* cbuf; unsigned char hi, lo; unsigned int i, j; short s; cbuf = (unsigned char*)loaded_sampledata; for (i = 0, j = 0; j < samplesize; i++) { lo = cbuf[j++]; hi = cbuf[j++]; s = (hi << 8) | lo; loaded_sampledata[i] = s; } } cached_sampledata->filename = (char*) FLUID_MALLOC(strlen(filename) + 1); if (cached_sampledata->filename == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); goto error_exit; } sprintf(cached_sampledata->filename, "%s", filename); cached_sampledata->modification_time = modification_time; cached_sampledata->num_references = 1; cached_sampledata->sampledata = loaded_sampledata; cached_sampledata->samplesize = samplesize; cached_sampledata->next = all_cached_sampledata; all_cached_sampledata = cached_sampledata; success_exit: fluid_mutex_unlock(cached_sampledata_mutex); *sampledata = loaded_sampledata; return FLUID_OK; error_exit: if (fd != NULL) { FLUID_FCLOSE(fd); } if (loaded_sampledata != NULL) { FLUID_FREE(loaded_sampledata); } if (cached_sampledata != NULL) { if (cached_sampledata->filename != NULL) { FLUID_FREE(cached_sampledata->filename); } FLUID_FREE(cached_sampledata); } fluid_mutex_unlock(cached_sampledata_mutex); *sampledata = NULL; return FLUID_FAILED; } static int fluid_cached_sampledata_unload(const short *sampledata) { fluid_cached_sampledata_t* prev = NULL; fluid_cached_sampledata_t* cached_sampledata; fluid_mutex_lock(cached_sampledata_mutex); cached_sampledata = all_cached_sampledata; while (cached_sampledata != NULL) { if (sampledata == cached_sampledata->sampledata) { cached_sampledata->num_references--; if (cached_sampledata->num_references == 0) { if (cached_sampledata->mlock) fluid_munlock(cached_sampledata->sampledata, cached_sampledata->samplesize); FLUID_FREE((short*) cached_sampledata->sampledata); FLUID_FREE(cached_sampledata->filename); if (prev != NULL) { prev->next = cached_sampledata->next; } else { all_cached_sampledata = cached_sampledata->next; } FLUID_FREE(cached_sampledata); } goto success_exit; } prev = cached_sampledata; cached_sampledata = cached_sampledata->next; } FLUID_LOG(FLUID_ERR, "Trying to free sampledata not found in cache."); goto error_exit; success_exit: fluid_mutex_unlock(cached_sampledata_mutex); return FLUID_OK; error_exit: fluid_mutex_unlock(cached_sampledata_mutex); return FLUID_FAILED; } /*************************************************************** * * SFONT */ /* * new_fluid_defsfont */ fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings) { fluid_defsfont_t* sfont; int i; sfont = FLUID_NEW(fluid_defsfont_t); if (sfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } sfont->filename = NULL; sfont->samplepos = 0; sfont->samplesize = 0; sfont->sample = NULL; sfont->sampledata = NULL; sfont->preset = NULL; fluid_settings_getint(settings, "synth.lock-memory", &sfont->mlock); /* Initialise preset cache, so we don't have to call malloc on program changes. Usually, we have at most one preset per channel plus one temporarily used, so optimise for that case. */ fluid_settings_getint(settings, "synth.midi-channels", &sfont->preset_stack_capacity); sfont->preset_stack_capacity++; sfont->preset_stack_size = 0; sfont->preset_stack = FLUID_ARRAY(fluid_preset_t*, sfont->preset_stack_capacity); if (!sfont->preset_stack) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(sfont); return NULL; } for (i = 0; i < sfont->preset_stack_capacity; i++) { sfont->preset_stack[i] = FLUID_NEW(fluid_preset_t); if (!sfont->preset_stack[i]) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_defsfont(sfont); return NULL; } sfont->preset_stack_size++; } return sfont; } /* * delete_fluid_defsfont */ int delete_fluid_defsfont(fluid_defsfont_t* sfont) { fluid_list_t *list; fluid_defpreset_t* preset; fluid_sample_t* sample; /* Check that no samples are currently used */ for (list = sfont->sample; list; list = fluid_list_next(list)) { sample = (fluid_sample_t*) fluid_list_get(list); if (fluid_sample_refcount(sample) != 0) { return -1; } } if (sfont->filename != NULL) { FLUID_FREE(sfont->filename); } for (list = sfont->sample; list; list = fluid_list_next(list)) { delete_fluid_sample((fluid_sample_t*) fluid_list_get(list)); } if (sfont->sample) { delete_fluid_list(sfont->sample); } if (sfont->sampledata != NULL) { fluid_cached_sampledata_unload(sfont->sampledata); } while (sfont->preset_stack_size > 0) FLUID_FREE(sfont->preset_stack[--sfont->preset_stack_size]); FLUID_FREE(sfont->preset_stack); preset = sfont->preset; while (preset != NULL) { sfont->preset = preset->next; delete_fluid_defpreset(preset); preset = sfont->preset; } FLUID_FREE(sfont); return FLUID_OK; } /* * fluid_defsfont_get_name */ char* fluid_defsfont_get_name(fluid_defsfont_t* sfont) { return sfont->filename; } /* * fluid_defsfont_load */ int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file) { SFData* sfdata; fluid_list_t *p; SFPreset* sfpreset; SFSample* sfsample; fluid_sample_t* sample; fluid_defpreset_t* preset = NULL; sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file)); if (sfont->filename == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } FLUID_STRCPY(sfont->filename, file); /* The actual loading is done in the sfont and sffile files */ sfdata = sfload_file(file); if (sfdata == NULL) { FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file"); return FLUID_FAILED; } /* Keep track of the position and size of the sample data because it's loaded separately (and might be unoaded/reloaded in future) */ sfont->samplepos = sfdata->samplepos; sfont->samplesize = sfdata->samplesize; /* load sample data in one block */ if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK) goto err_exit; /* Create all the sample headers */ p = sfdata->sample; while (p != NULL) { sfsample = (SFSample *) p->data; sample = new_fluid_sample(); if (sample == NULL) goto err_exit; if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK) goto err_exit; /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ sfsample->fluid_sample = sample; fluid_defsfont_add_sample(sfont, sample); fluid_voice_optimize_sample(sample); p = fluid_list_next(p); } /* Load all the presets */ p = sfdata->preset; while (p != NULL) { sfpreset = (SFPreset *) p->data; preset = new_fluid_defpreset(sfont); if (preset == NULL) goto err_exit; if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK) goto err_exit; fluid_defsfont_add_preset(sfont, preset); p = fluid_list_next(p); } sfont_close (sfdata); return FLUID_OK; err_exit: sfont_close (sfdata); if (preset != NULL) delete_fluid_defpreset(preset); return FLUID_FAILED; } /* fluid_defsfont_add_sample * * Add a sample to the SoundFont */ int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample) { sfont->sample = fluid_list_append(sfont->sample, sample); return FLUID_OK; } /* fluid_defsfont_add_preset * * Add a preset to the SoundFont */ int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset) { fluid_defpreset_t *cur, *prev; if (sfont->preset == NULL) { preset->next = NULL; sfont->preset = preset; } else { /* sort them as we go along. very basic sorting trick. */ cur = sfont->preset; prev = NULL; while (cur != NULL) { if ((preset->bank < cur->bank) || ((preset->bank == cur->bank) && (preset->num < cur->num))) { if (prev == NULL) { preset->next = cur; sfont->preset = preset; } else { preset->next = cur; prev->next = preset; } return FLUID_OK; } prev = cur; cur = cur->next; } preset->next = NULL; prev->next = preset; } return FLUID_OK; } /* * fluid_defsfont_load_sampledata */ int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont) { return fluid_cached_sampledata_load(sfont->filename, sfont->samplepos, sfont->samplesize, &sfont->sampledata, sfont->mlock); } /* * fluid_defsfont_get_preset */ fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num) { fluid_defpreset_t* preset = sfont->preset; while (preset != NULL) { if ((preset->bank == bank) && ((preset->num == num))) { return preset; } preset = preset->next; } return NULL; } /* * fluid_defsfont_iteration_start */ void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont) { sfont->iter_cur = sfont->preset; } /* * fluid_defsfont_iteration_next */ int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset) { if (sfont->iter_cur == NULL) { return 0; } preset->data = (void*) sfont->iter_cur; sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur); return 1; } /*************************************************************** * * PRESET */ /* * new_fluid_defpreset */ fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont) { fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } preset->next = NULL; preset->sfont = sfont; preset->name[0] = 0; preset->bank = 0; preset->num = 0; preset->global_zone = NULL; preset->zone = NULL; return preset; } /* * delete_fluid_defpreset */ int delete_fluid_defpreset(fluid_defpreset_t* preset) { int err = FLUID_OK; fluid_preset_zone_t* zone; if (preset->global_zone != NULL) { if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) { err = FLUID_FAILED; } preset->global_zone = NULL; } zone = preset->zone; while (zone != NULL) { preset->zone = zone->next; if (delete_fluid_preset_zone(zone) != FLUID_OK) { err = FLUID_FAILED; } zone = preset->zone; } FLUID_FREE(preset); return err; } int fluid_defpreset_get_banknum(fluid_defpreset_t* preset) { return preset->bank; } int fluid_defpreset_get_num(fluid_defpreset_t* preset) { return preset->num; } char* fluid_defpreset_get_name(fluid_defpreset_t* preset) { return preset->name; } /* * fluid_defpreset_next */ fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset) { return preset->next; } /* * fluid_defpreset_noteon */ int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) { fluid_preset_zone_t *preset_zone, *global_preset_zone; fluid_inst_t* inst; fluid_inst_zone_t *inst_zone, *global_inst_zone; fluid_sample_t* sample; fluid_voice_t* voice; fluid_mod_t * mod; fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ int mod_list_count; int i; global_preset_zone = fluid_defpreset_get_global_zone(preset); /* run thru all the zones of this preset */ preset_zone = fluid_defpreset_get_zone(preset); while (preset_zone != NULL) { /* check if the note falls into the key and velocity range of this preset */ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { inst = fluid_preset_zone_get_inst(preset_zone); global_inst_zone = fluid_inst_get_global_zone(inst); /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); if ((sample == NULL) || fluid_sample_in_rom(sample)) { inst_zone = fluid_inst_zone_next(inst_zone); continue; } /* check if the note falls into the key and velocity range of this instrument */ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { /* this is a good zone. allocate a new synthesis process and initialize it */ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); if (voice == NULL) { return FLUID_FAILED; } /* Instrument level, generators */ for (i = 0; i < GEN_LAST; i++) { /* SF 2.01 section 9.4 'bullet' 4: * * A generator in a local instrument zone supersedes a * global instrument zone generator. Both cases supersede * the default generator -> voice_gen_set */ if (inst_zone->gen[i].flags){ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) { fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); } else { /* The generator has not been defined in this instrument. * Do nothing, leave it at the default. */ } } /* for all generators */ /* global instrument zone, modulators: Put them all into a * list. */ mod_list_count = 0; if (global_inst_zone){ mod = global_inst_zone->mod; while (mod){ mod_list[mod_list_count++] = mod; mod = mod->next; } } /* local instrument zone, modulators. * Replace modulators with the same definition in the list: * SF 2.01 page 69, 'bullet' 8 */ mod = inst_zone->mod; while (mod){ /* 'Identical' modulators will be deleted by setting their * list entry to NULL. The list length is known, NULL * entries will be ignored later. SF2.01 section 9.5.1 * page 69, 'bullet' 3 defines 'identical'. */ for (i = 0; i < mod_list_count; i++){ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){ mod_list[i] = NULL; } } /* Finally add the new modulator to to the list. */ mod_list[mod_list_count++] = mod; mod = mod->next; } /* Add instrument modulators (global / local) to the voice. */ for (i = 0; i < mod_list_count; i++){ mod = mod_list[i]; if (mod != NULL){ /* disabled modulators CANNOT be skipped. */ /* Instrument modulators -supersede- existing (default) * modulators. SF 2.01 page 69, 'bullet' 6 */ fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); } } /* Preset level, generators */ for (i = 0; i < GEN_LAST; i++) { /* SF 2.01 section 8.5 page 58: If some generators are * encountered at preset level, they should be ignored */ if ((i != GEN_STARTADDROFS) && (i != GEN_ENDADDROFS) && (i != GEN_STARTLOOPADDROFS) && (i != GEN_ENDLOOPADDROFS) && (i != GEN_STARTADDRCOARSEOFS) && (i != GEN_ENDADDRCOARSEOFS) && (i != GEN_STARTLOOPADDRCOARSEOFS) && (i != GEN_KEYNUM) && (i != GEN_VELOCITY) && (i != GEN_ENDLOOPADDRCOARSEOFS) && (i != GEN_SAMPLEMODE) && (i != GEN_EXCLUSIVECLASS) && (i != GEN_OVERRIDEROOTKEY)) { /* SF 2.01 section 9.4 'bullet' 9: A generator in a * local preset zone supersedes a global preset zone * generator. The effect is -added- to the destination * summing node -> voice_gen_incr */ if (preset_zone->gen[i].flags) { fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) { fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); } else { /* The generator has not been defined in this preset * Do nothing, leave it unchanged. */ } } /* if available at preset level */ } /* for all generators */ /* Global preset zone, modulators: put them all into a * list. */ mod_list_count = 0; if (global_preset_zone){ mod = global_preset_zone->mod; while (mod){ mod_list[mod_list_count++] = mod; mod = mod->next; } } /* Process the modulators of the local preset zone. Kick * out all identical modulators from the global preset zone * (SF 2.01 page 69, second-last bullet) */ mod = preset_zone->mod; while (mod){ for (i = 0; i < mod_list_count; i++){ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){ mod_list[i] = NULL; } } /* Finally add the new modulator to the list. */ mod_list[mod_list_count++] = mod; mod = mod->next; } /* Add preset modulators (global / local) to the voice. */ for (i = 0; i < mod_list_count; i++){ mod = mod_list[i]; if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */ /* Preset modulators -add- to existing instrument / * default modulators. SF2.01 page 70 first bullet on * page */ fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); } } /* add the synthesis process to the synthesis loop. */ fluid_synth_start_voice(synth, voice); /* Store the ID of the first voice that was created by this noteon event. * Exclusive class may only terminate older voices. * That avoids killing voices, which have just been created. * (a noteon event can create several voice processes with the same exclusive * class - for example when using stereo samples) */ } inst_zone = fluid_inst_zone_next(inst_zone); } } preset_zone = fluid_preset_zone_next(preset_zone); } return FLUID_OK; } /* * fluid_defpreset_set_global_zone */ int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) { preset->global_zone = zone; return FLUID_OK; } /* * fluid_defpreset_import_sfont */ int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont) { fluid_list_t *p; SFZone* sfzone; fluid_preset_zone_t* zone; int count; char zone_name[256]; if (FLUID_STRLEN(sfpreset->name) > 0) { FLUID_STRCPY(preset->name, sfpreset->name); } else { FLUID_SPRINTF(preset->name, "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum); } preset->bank = sfpreset->bank; preset->num = sfpreset->prenum; p = sfpreset->zone; count = 0; while (p != NULL) { sfzone = (SFZone *) p->data; FLUID_SPRINTF(zone_name, "%s/%d", preset->name, count); zone = new_fluid_preset_zone(zone_name); if (zone == NULL) { return FLUID_FAILED; } if (fluid_preset_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { delete_fluid_preset_zone(zone); return FLUID_FAILED; } if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) { fluid_defpreset_set_global_zone(preset, zone); } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) { return FLUID_FAILED; } p = fluid_list_next(p); count++; } return FLUID_OK; } /* * fluid_defpreset_add_zone */ int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone) { if (preset->zone == NULL) { zone->next = NULL; preset->zone = zone; } else { zone->next = preset->zone; preset->zone = zone; } return FLUID_OK; } /* * fluid_defpreset_get_zone */ fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset) { return preset->zone; } /* * fluid_defpreset_get_global_zone */ fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset) { return preset->global_zone; } /* * fluid_preset_zone_next */ fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset) { return preset->next; } /* * new_fluid_preset_zone */ fluid_preset_zone_t* new_fluid_preset_zone(char *name) { int size; fluid_preset_zone_t* zone = NULL; zone = FLUID_NEW(fluid_preset_zone_t); if (zone == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } zone->next = NULL; size = 1 + FLUID_STRLEN(name); zone->name = FLUID_MALLOC(size); if (zone->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(zone); return NULL; } FLUID_STRCPY(zone->name, name); zone->inst = NULL; zone->keylo = 0; zone->keyhi = 128; zone->vello = 0; zone->velhi = 128; /* Flag all generators as unused (default, they will be set when they are found * in the sound font). * This also sets the generator values to default, but that is of no concern here.*/ fluid_gen_set_default_values(&zone->gen[0]); zone->mod = NULL; /* list of modulators */ return zone; } /*************************************************************** * * PRESET_ZONE */ /* * delete_fluid_preset_zone */ int delete_fluid_preset_zone(fluid_preset_zone_t* zone) { fluid_mod_t *mod, *tmp; mod = zone->mod; while (mod) /* delete the modulators */ { tmp = mod; mod = mod->next; fluid_mod_delete (tmp); } if (zone->name) FLUID_FREE (zone->name); if (zone->inst) delete_fluid_inst (zone->inst); FLUID_FREE(zone); return FLUID_OK; } /* * fluid_preset_zone_import_sfont */ int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) { fluid_list_t *r; SFGen* sfgen; int count; for (count = 0, r = sfzone->gen; r != NULL; count++) { sfgen = (SFGen *) r->data; switch (sfgen->id) { case GEN_KEYRANGE: zone->keylo = (int) sfgen->amount.range.lo; zone->keyhi = (int) sfgen->amount.range.hi; break; case GEN_VELRANGE: zone->vello = (int) sfgen->amount.range.lo; zone->velhi = (int) sfgen->amount.range.hi; break; default: /* FIXME: some generators have an unsigne word amount value but i don't know which ones */ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; zone->gen[sfgen->id].flags = GEN_SET; break; } r = fluid_list_next(r); } if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { zone->inst = (fluid_inst_t*) new_fluid_inst(); if (zone->inst == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { return FLUID_FAILED; } } /* Import the modulators (only SF2.1 and higher) */ for (count = 0, r = sfzone->mod; r != NULL; count++) { SFMod* mod_src = (SFMod *)r->data; fluid_mod_t * mod_dest = fluid_mod_new(); int type; if (mod_dest == NULL){ return FLUID_FAILED; } mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ /* *** Amount *** */ mod_dest->amount = mod_src->amount; /* *** Source *** */ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ mod_dest->flags1 = 0; /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ if (mod_src->src & (1<<7)){ mod_dest->flags1 |= FLUID_MOD_CC; } else { mod_dest->flags1 |= FLUID_MOD_GC; } /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ if (mod_src->src & (1<<8)){ mod_dest->flags1 |= FLUID_MOD_NEGATIVE; } else { mod_dest->flags1 |= FLUID_MOD_POSITIVE; } /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ if (mod_src->src & (1<<9)){ mod_dest->flags1 |= FLUID_MOD_BIPOLAR; } else { mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; } /* modulator source types: SF2.01 section 8.2.1 page 52 */ type=(mod_src->src) >> 10; type &= 63; /* type is a 6-bit value */ if (type == 0){ mod_dest->flags1 |= FLUID_MOD_LINEAR; } else if (type == 1){ mod_dest->flags1 |= FLUID_MOD_CONCAVE; } else if (type == 2){ mod_dest->flags1 |= FLUID_MOD_CONVEX; } else if (type == 3){ mod_dest->flags1 |= FLUID_MOD_SWITCH; } else { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount=0; } /* *** Dest *** */ mod_dest->dest = mod_src->dest; /* index of controlled generator */ /* *** Amount source *** */ mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */ mod_dest->flags2 = 0; /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ if (mod_src->amtsrc & (1<<7)){ mod_dest->flags2 |= FLUID_MOD_CC; } else { mod_dest->flags2 |= FLUID_MOD_GC; } /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ if (mod_src->amtsrc & (1<<8)){ mod_dest->flags2 |= FLUID_MOD_NEGATIVE; } else { mod_dest->flags2 |= FLUID_MOD_POSITIVE; } /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ if (mod_src->amtsrc & (1<<9)){ mod_dest->flags2 |= FLUID_MOD_BIPOLAR; } else { mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; } /* modulator source types: SF2.01 section 8.2.1 page 52 */ type = (mod_src->amtsrc) >> 10; type &= 63; /* type is a 6-bit value */ if (type == 0){ mod_dest->flags2 |= FLUID_MOD_LINEAR; } else if (type == 1){ mod_dest->flags2 |= FLUID_MOD_CONCAVE; } else if (type == 2){ mod_dest->flags2 |= FLUID_MOD_CONVEX; } else if (type == 3){ mod_dest->flags2 |= FLUID_MOD_SWITCH; } else { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount=0; } /* *** Transform *** */ /* SF2.01 only uses the 'linear' transform (0). * Deactivate the modulator by setting the amount to 0 in any other case. */ if (mod_src->trans !=0){ mod_dest->amount = 0; } /* Store the new modulator in the zone The order of modulators * will make a difference, at least in an instrument context: The * second modulator overwrites the first one, if they only differ * in amount. */ if (count == 0){ zone->mod = mod_dest; } else { fluid_mod_t * last_mod = zone->mod; /* Find the end of the list */ while (last_mod->next != NULL){ last_mod=last_mod->next; } last_mod->next = mod_dest; } r = fluid_list_next(r); } /* foreach modulator */ return FLUID_OK; } /* * fluid_preset_zone_get_inst */ fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone) { return zone->inst; } /* * fluid_preset_zone_inside_range */ int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel) { return ((zone->keylo <= key) && (zone->keyhi >= key) && (zone->vello <= vel) && (zone->velhi >= vel)); } /*************************************************************** * * INST */ /* * new_fluid_inst */ fluid_inst_t* new_fluid_inst() { fluid_inst_t* inst = FLUID_NEW(fluid_inst_t); if (inst == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } inst->name[0] = 0; inst->global_zone = NULL; inst->zone = NULL; return inst; } /* * delete_fluid_inst */ int delete_fluid_inst(fluid_inst_t* inst) { fluid_inst_zone_t* zone; int err = FLUID_OK; if (inst->global_zone != NULL) { if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) { err = FLUID_FAILED; } inst->global_zone = NULL; } zone = inst->zone; while (zone != NULL) { inst->zone = zone->next; if (delete_fluid_inst_zone(zone) != FLUID_OK) { err = FLUID_FAILED; } zone = inst->zone; } FLUID_FREE(inst); return err; } /* * fluid_inst_set_global_zone */ int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) { inst->global_zone = zone; return FLUID_OK; } /* * fluid_inst_import_sfont */ int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) { fluid_list_t *p; SFZone* sfzone; fluid_inst_zone_t* zone; char zone_name[256]; int count; p = sfinst->zone; if (FLUID_STRLEN(sfinst->name) > 0) { FLUID_STRCPY(inst->name, sfinst->name); } else { FLUID_STRCPY(inst->name, ""); } count = 0; while (p != NULL) { sfzone = (SFZone *) p->data; FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count); zone = new_fluid_inst_zone(zone_name); if (zone == NULL) { return FLUID_FAILED; } if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { delete_fluid_inst_zone(zone); return FLUID_FAILED; } if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) { fluid_inst_set_global_zone(inst, zone); } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) { return FLUID_FAILED; } p = fluid_list_next(p); count++; } return FLUID_OK; } /* * fluid_inst_add_zone */ int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone) { if (inst->zone == NULL) { zone->next = NULL; inst->zone = zone; } else { zone->next = inst->zone; inst->zone = zone; } return FLUID_OK; } /* * fluid_inst_get_zone */ fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst) { return inst->zone; } /* * fluid_inst_get_global_zone */ fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst) { return inst->global_zone; } /*************************************************************** * * INST_ZONE */ /* * new_fluid_inst_zone */ fluid_inst_zone_t* new_fluid_inst_zone(char* name) { int size; fluid_inst_zone_t* zone = NULL; zone = FLUID_NEW(fluid_inst_zone_t); if (zone == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } zone->next = NULL; size = 1 + FLUID_STRLEN(name); zone->name = FLUID_MALLOC(size); if (zone->name == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(zone); return NULL; } FLUID_STRCPY(zone->name, name); zone->sample = NULL; zone->keylo = 0; zone->keyhi = 128; zone->vello = 0; zone->velhi = 128; /* Flag the generators as unused. * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ fluid_gen_set_default_values(&zone->gen[0]); zone->mod=NULL; /* list of modulators */ return zone; } /* * delete_fluid_inst_zone */ int delete_fluid_inst_zone(fluid_inst_zone_t* zone) { fluid_mod_t *mod, *tmp; mod = zone->mod; while (mod) /* delete the modulators */ { tmp = mod; mod = mod->next; fluid_mod_delete (tmp); } if (zone->name) FLUID_FREE (zone->name); FLUID_FREE(zone); return FLUID_OK; } /* * fluid_inst_zone_next */ fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone) { return zone->next; } /* * fluid_inst_zone_import_sfont */ int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) { fluid_list_t *r; SFGen* sfgen; int count; for (count = 0, r = sfzone->gen; r != NULL; count++) { sfgen = (SFGen *) r->data; switch (sfgen->id) { case GEN_KEYRANGE: zone->keylo = (int) sfgen->amount.range.lo; zone->keyhi = (int) sfgen->amount.range.hi; break; case GEN_VELRANGE: zone->vello = (int) sfgen->amount.range.lo; zone->velhi = (int) sfgen->amount.range.hi; break; default: /* FIXME: some generators have an unsigned word amount value but i don't know which ones */ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; zone->gen[sfgen->id].flags = GEN_SET; break; } r = fluid_list_next(r); } /* FIXME */ /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ /* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ /* } */ /* fixup sample pointer */ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample; /* Import the modulators (only SF2.1 and higher) */ for (count = 0, r = sfzone->mod; r != NULL; count++) { SFMod* mod_src = (SFMod *) r->data; int type; fluid_mod_t* mod_dest; mod_dest = fluid_mod_new(); if (mod_dest == NULL){ return FLUID_FAILED; } mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ /* *** Amount *** */ mod_dest->amount = mod_src->amount; /* *** Source *** */ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ mod_dest->flags1 = 0; /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ if (mod_src->src & (1<<7)){ mod_dest->flags1 |= FLUID_MOD_CC; } else { mod_dest->flags1 |= FLUID_MOD_GC; } /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ if (mod_src->src & (1<<8)){ mod_dest->flags1 |= FLUID_MOD_NEGATIVE; } else { mod_dest->flags1 |= FLUID_MOD_POSITIVE; } /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ if (mod_src->src & (1<<9)){ mod_dest->flags1 |= FLUID_MOD_BIPOLAR; } else { mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; } /* modulator source types: SF2.01 section 8.2.1 page 52 */ type = (mod_src->src) >> 10; type &= 63; /* type is a 6-bit value */ if (type == 0){ mod_dest->flags1 |= FLUID_MOD_LINEAR; } else if (type == 1){ mod_dest->flags1 |= FLUID_MOD_CONCAVE; } else if (type == 2){ mod_dest->flags1 |= FLUID_MOD_CONVEX; } else if (type == 3){ mod_dest->flags1 |= FLUID_MOD_SWITCH; } else { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } /* *** Dest *** */ mod_dest->dest=mod_src->dest; /* index of controlled generator */ /* *** Amount source *** */ mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */ mod_dest->flags2 = 0; /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ if (mod_src->amtsrc & (1<<7)){ mod_dest->flags2 |= FLUID_MOD_CC; } else { mod_dest->flags2 |= FLUID_MOD_GC; } /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ if (mod_src->amtsrc & (1<<8)){ mod_dest->flags2 |= FLUID_MOD_NEGATIVE; } else { mod_dest->flags2 |= FLUID_MOD_POSITIVE; } /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ if (mod_src->amtsrc & (1<<9)){ mod_dest->flags2 |= FLUID_MOD_BIPOLAR; } else { mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; } /* modulator source types: SF2.01 section 8.2.1 page 52 */ type=(mod_src->amtsrc) >> 10; type &= 63; /* type is a 6-bit value */ if (type == 0){ mod_dest->flags2 |= FLUID_MOD_LINEAR; } else if (type == 1){ mod_dest->flags2 |= FLUID_MOD_CONCAVE; } else if (type == 2){ mod_dest->flags2 |= FLUID_MOD_CONVEX; } else if (type == 3){ mod_dest->flags2 |= FLUID_MOD_SWITCH; } else { /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } /* *** Transform *** */ /* SF2.01 only uses the 'linear' transform (0). * Deactivate the modulator by setting the amount to 0 in any other case. */ if (mod_src->trans !=0){ mod_dest->amount = 0; } /* Store the new modulator in the zone * The order of modulators will make a difference, at least in an instrument context: * The second modulator overwrites the first one, if they only differ in amount. */ if (count == 0){ zone->mod=mod_dest; } else { fluid_mod_t * last_mod=zone->mod; /* Find the end of the list */ while (last_mod->next != NULL){ last_mod=last_mod->next; } last_mod->next=mod_dest; } r = fluid_list_next(r); } /* foreach modulator */ return FLUID_OK; } /* * fluid_inst_zone_get_sample */ fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone) { return zone->sample; } /* * fluid_inst_zone_inside_range */ int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) { return ((zone->keylo <= key) && (zone->keyhi >= key) && (zone->vello <= vel) && (zone->velhi >= vel)); } /*************************************************************** * * SAMPLE */ /* * new_fluid_sample */ fluid_sample_t* new_fluid_sample() { fluid_sample_t* sample = NULL; sample = FLUID_NEW(fluid_sample_t); if (sample == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } memset(sample, 0, sizeof(fluid_sample_t)); sample->valid = 1; return sample; } /* * delete_fluid_sample */ int delete_fluid_sample(fluid_sample_t* sample) { if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) { #if LIBSNDFILE_SUPPORT if (sample->data) FLUID_FREE(sample->data); #endif } FLUID_FREE(sample); return FLUID_OK; } /* * fluid_sample_in_rom */ int fluid_sample_in_rom(fluid_sample_t* sample) { return (sample->sampletype & FLUID_SAMPLETYPE_ROM); } /* * fluid_sample_import_sfont */ #if LIBSNDFILE_SUPPORT // virtual file access rountines to allow for handling // samples as virtual files in memory static sf_count_t sfvio_get_filelen(void* user_data) { fluid_sample_t *sample = (fluid_sample_t *)user_data; return (sf_count_t)(sample->end + 1 - sample->start); } static sf_count_t sfvio_seek(sf_count_t offset, int whence, void* user_data) { fluid_sample_t *sample = (fluid_sample_t *)user_data; switch (whence) { case SEEK_SET: sample->userdata = (void *)offset; break; case SEEK_CUR: sample->userdata = (void *)((sf_count_t)sample->userdata + offset); break; case SEEK_END: sample->userdata = (void *)(sfvio_get_filelen(user_data) + offset); break; } return (sf_count_t)sample->userdata; } static sf_count_t sfvio_read(void* ptr, sf_count_t count, void* user_data) { fluid_sample_t *sample = (fluid_sample_t *)user_data; sf_count_t remain = sfvio_get_filelen(user_data) - (sf_count_t)sample->userdata; if (count > remain) count = remain; memcpy(ptr, (char *)sample->data + sample->start + (sf_count_t)sample->userdata, count); sample->userdata = (void *)((sf_count_t)sample->userdata + count); return count; } static sf_count_t sfvio_tell (void* user_data) { fluid_sample_t *sample = (fluid_sample_t *)user_data; return (sf_count_t)sample->userdata; } #endif int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont) { FLUID_STRCPY(sample->name, sfsample->name); sample->data = sfont->sampledata; sample->start = sfsample->start; sample->end = sfsample->start + sfsample->end; sample->loopstart = sfsample->start + sfsample->loopstart; sample->loopend = sfsample->start + sfsample->loopend; sample->samplerate = sfsample->samplerate; sample->origpitch = sfsample->origpitch; sample->pitchadj = sfsample->pitchadj; sample->sampletype = sfsample->sampletype; if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) { #if LIBSNDFILE_SUPPORT SNDFILE *sndfile; SF_INFO sfinfo; SF_VIRTUAL_IO sfvio = { sfvio_get_filelen, sfvio_seek, sfvio_read, NULL, sfvio_tell }; short *sampledata_ogg; int inv_loop = FALSE; // initialize file position indicator and SF_INFO structure g_assert(sample->userdata == NULL); memset(&sfinfo, 0, sizeof(sfinfo)); // open sample as a virtual file in memory sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, sample); if (!sndfile) { FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); return FLUID_FAILED; } // empty sample if (!sfinfo.frames || !sfinfo.channels) { sample->start = sample->end = sample->loopstart = sample->loopend = sample->valid = 0; sample->data = NULL; sf_close(sndfile); return FLUID_OK; } // allocate memory for uncompressed sample data stream sampledata_ogg = (short *)FLUID_MALLOC(sfinfo.frames * sfinfo.channels * sizeof(short)); if (!sampledata_ogg) { FLUID_LOG(FLUID_ERR, "Out of memory"); sf_close(sndfile); return FLUID_FAILED; } // uncompress sample data stream if (sf_readf_short(sndfile, sampledata_ogg, sfinfo.frames) < sfinfo.frames) { FLUID_FREE(sampledata_ogg); FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); sf_close(sndfile); return FLUID_FAILED; } sf_close(sndfile); // point sample data to uncompressed data stream sample->data = sampledata_ogg; sample->start = 0; sample->end = sfinfo.frames - 1; /* loop is fowled?? (cluck cluck :) */ if (sample->loopend-1 > sample->end /* loopend may point one sample after valid sample data, as this one will never be played */ || sample->loopstart >= sample->loopend) { FLUID_LOG (FLUID_DBG, _("Vorbis sample '%s' has unusable loop stop '%d'," " setting to sample end '%d'+1"), sample->name, sample->loopend, sample->end); /* though illegal, loopend may be set to loopstart to disable loop */ /* is it worth informing the user? */ inv_loop |= (sample->loopend != sample->loopstart); sample->loopend = sample->end+1; } if(sample->loopstart < sample->start || sample->loopstart >= sample->loopend) { FLUID_LOG (FLUID_DBG, _("Vorbis sample '%s' has unusable loop start '%d'," " setting to sample start '%d'"), sample->name, sample->loopstart, sample->start); sample->loopstart = sample->start; inv_loop |= TRUE; } if(inv_loop) { FLUID_LOG (FLUID_WARN, _("Vorbis sample '%s' has invalid loop points"), sample->name); } #else return FLUID_FAILED; #endif } if (sample->sampletype & FLUID_SAMPLETYPE_ROM) { sample->valid = 0; FLUID_LOG(FLUID_WARN, "Ignoring sample '%s': can't use ROM samples", sample->name); } if (sample->end - sample->start < 8) { sample->valid = 0; FLUID_LOG(FLUID_WARN, "Ignoring sample '%s': too few sample data points", sample->name); } else { /* if (sample->loopstart < sample->start + 8) { */ /* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */ /* sample->loopstart = sample->start + 8; */ /* } */ /* if (sample->loopend > sample->end - 8) { */ /* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */ /* sample->loopend = sample->end - 8; */ /* } */ } return FLUID_OK; } /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /*=================================sfload.c======================== Borrowed from Smurf SoundFont Editor by Josh Green =================================================================*/ /* 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 */ #if FLUID_IS_BIG_ENDIAN #define READCHUNK(var,fd) G_STMT_START { \ if (!safe_fread(var, 8, fd)) \ return(FAIL); \ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \ } G_STMT_END #define READD(var,fd) G_STMT_START { \ unsigned int _temp; \ if (!safe_fread(&_temp, 4, fd)) \ return(FAIL); \ var = GINT32_FROM_LE(_temp); \ } G_STMT_END #define READW(var,fd) G_STMT_START { \ unsigned short _temp; \ if (!safe_fread(&_temp, 2, fd)) \ return(FAIL); \ var = GINT16_FROM_LE(_temp); \ } G_STMT_END #else #define READCHUNK(var,fd) G_STMT_START { \ if (!safe_fread(var, 8, fd)) \ return(FAIL); \ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \ } G_STMT_END #define READD(var,fd) G_STMT_START { \ unsigned int _temp; \ if (!safe_fread(&_temp, 4, fd)) \ return(FAIL); \ var = GINT32_FROM_LE(_temp); \ } G_STMT_END #define READW(var,fd) G_STMT_START { \ unsigned short _temp; \ if (!safe_fread(&_temp, 2, fd)) \ return(FAIL); \ var = GINT16_FROM_LE(_temp); \ } G_STMT_END #endif #define READID(var,fd) G_STMT_START { \ if (!safe_fread(var, 4, fd)) \ return(FAIL); \ } G_STMT_END #define READSTR(var,fd) G_STMT_START { \ if (!safe_fread(var, 20, fd)) \ return(FAIL); \ (*var)[20] = '\0'; \ } G_STMT_END #define READB(var,fd) G_STMT_START { \ if (!safe_fread(&var, 1, fd)) \ return(FAIL); \ } G_STMT_END #define FSKIP(size,fd) G_STMT_START { \ if (!safe_fseek(fd, size, SEEK_CUR)) \ return(FAIL); \ } G_STMT_END #define FSKIPW(fd) G_STMT_START { \ if (!safe_fseek(fd, 2, SEEK_CUR)) \ return(FAIL); \ } G_STMT_END /* removes and advances a fluid_list_t pointer */ #define SLADVREM(list, item) G_STMT_START { \ fluid_list_t *_temp = item; \ item = fluid_list_next(item); \ list = fluid_list_remove_link(list, _temp); \ delete1_fluid_list(_temp); \ } G_STMT_END static int chunkid (unsigned int id); static int load_body (unsigned int size, SFData * sf, FILE * fd); static int read_listchunk (SFChunk * chunk, FILE * fd); static int process_info (int size, SFData * sf, FILE * fd); static int process_sdta (unsigned int size, SFData * sf, FILE * fd); static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, int * size, FILE * fd); static int process_pdta (int size, SFData * sf, FILE * fd); static int load_phdr (int size, SFData * sf, FILE * fd); static int load_pbag (int size, SFData * sf, FILE * fd); static int load_pmod (int size, SFData * sf, FILE * fd); static int load_pgen (int size, SFData * sf, FILE * fd); static int load_ihdr (int size, SFData * sf, FILE * fd); static int load_ibag (int size, SFData * sf, FILE * fd); static int load_imod (int size, SFData * sf, FILE * fd); static int load_igen (int size, SFData * sf, FILE * fd); static int load_shdr (unsigned int size, SFData * sf, FILE * fd); static int fixup_pgen (SFData * sf); static int fixup_igen (SFData * sf); static int fixup_sample (SFData * sf); char idlist[] = { "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr" }; static unsigned int sdtachunk_size; /* sound font file load functions */ static int chunkid (unsigned int id) { unsigned int i; unsigned int *p; p = (unsigned int *) & idlist; for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1) if (*p == id) return (i + 1); return (UNKN_ID); } SFData * sfload_file (const char * fname) { SFData *sf = NULL; FILE *fd; int fsize = 0; int err = FALSE; if (!(fd = fopen (fname, "rb"))) { FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname); return (NULL); } if (!(sf = FLUID_NEW (SFData))) { FLUID_LOG(FLUID_ERR, "Out of memory"); fclose(fd); err = TRUE; } if (!err) { memset (sf, 0, sizeof (SFData)); /* zero sfdata */ sf->fname = FLUID_STRDUP (fname); /* copy file name */ sf->sffd = fd; } /* get size of file */ if (!err && fseek (fd, 0L, SEEK_END) == -1) { /* seek to end of file */ err = TRUE; FLUID_LOG (FLUID_ERR, _("Seek to end of file failed")); } if (!err && (fsize = ftell (fd)) == -1) { /* position = size */ err = TRUE; FLUID_LOG (FLUID_ERR, _("Get end of file position failed")); } if (!err) rewind (fd); if (!err && !load_body (fsize, sf, fd)) err = TRUE; /* load the sfont */ if (err) { if (sf) sfont_close (sf); return (NULL); } return (sf); } static int load_body (unsigned int size, SFData * sf, FILE * fd) { SFChunk chunk; READCHUNK (&chunk, fd); /* load RIFF chunk */ if (chunkid (chunk.id) != RIFF_ID) { /* error if not RIFF */ FLUID_LOG (FLUID_ERR, _("Not a RIFF file")); return (FAIL); } READID (&chunk.id, fd); /* load file ID */ if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */ FLUID_LOG (FLUID_ERR, _("Not a SoundFont file")); return (FAIL); } if (chunk.size != size - 8) { gerr (ErrCorr, _("SoundFont file size mismatch")); return (FAIL); } /* Process INFO block */ if (!read_listchunk (&chunk, fd)) return (FAIL); if (chunkid (chunk.id) != INFO_ID) return (gerr (ErrCorr, _("Invalid ID found when expecting INFO chunk"))); if (!process_info (chunk.size, sf, fd)) return (FAIL); /* Process sample chunk */ if (!read_listchunk (&chunk, fd)) return (FAIL); if (chunkid (chunk.id) != SDTA_ID) return (gerr (ErrCorr, _("Invalid ID found when expecting SAMPLE chunk"))); if (!process_sdta (chunk.size, sf, fd)) return (FAIL); /* process HYDRA chunk */ if (!read_listchunk (&chunk, fd)) return (FAIL); if (chunkid (chunk.id) != PDTA_ID) return (gerr (ErrCorr, _("Invalid ID found when expecting HYDRA chunk"))); if (!process_pdta (chunk.size, sf, fd)) return (FAIL); if (!fixup_pgen (sf)) return (FAIL); if (!fixup_igen (sf)) return (FAIL); if (!fixup_sample (sf)) return (FAIL); /* sort preset list by bank, preset # */ sf->preset = fluid_list_sort (sf->preset, (fluid_compare_func_t) sfont_preset_compare_func); return (OK); } static int read_listchunk (SFChunk * chunk, FILE * fd) { READCHUNK (chunk, fd); /* read list chunk */ if (chunkid (chunk->id) != LIST_ID) /* error if ! list chunk */ return (gerr (ErrCorr, _("Invalid chunk id in level 0 parse"))); READID (&chunk->id, fd); /* read id string */ chunk->size -= 4; return (OK); } static int process_info (int size, SFData * sf, FILE * fd) { SFChunk chunk; unsigned char id; char *item; unsigned short ver; while (size > 0) { READCHUNK (&chunk, fd); size -= 8; id = chunkid (chunk.id); if (id == IFIL_ID) { /* sound font version chunk? */ if (chunk.size != 4) return (gerr (ErrCorr, _("Sound font version info chunk has invalid size"))); READW (ver, fd); sf->version.major = ver; READW (ver, fd); sf->version.minor = ver; if (sf->version.major < 2) { FLUID_LOG (FLUID_ERR, _("Sound font version is %d.%d which is not" " supported, convert to version 2.0x"), sf->version.major, sf->version.minor); return (FAIL); } if (sf->version.major == 3) { #if !LIBSNDFILE_SUPPORT FLUID_LOG (FLUID_WARN, _("Sound font version is %d.%d but fluidsynth was compiled without" " support for (v3.x)"), sf->version.major, sf->version.minor); return (FAIL); #endif } else if (sf->version.major > 2) { FLUID_LOG (FLUID_WARN, _("Sound font version is %d.%d which is newer than" " what this version of fluidsynth was designed for (v2.0x)"), sf->version.major, sf->version.minor); return (FAIL); } } else if (id == IVER_ID) { /* ROM version chunk? */ if (chunk.size != 4) return (gerr (ErrCorr, _("ROM version info chunk has invalid size"))); READW (ver, fd); sf->romver.major = ver; READW (ver, fd); sf->romver.minor = ver; } else if (id != UNKN_ID) { if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) return (gerr (ErrCorr, _("INFO sub chunk %.4s has invalid chunk size" " of %d bytes"), &chunk.id, chunk.size)); /* alloc for chunk id and da chunk */ if (!(item = FLUID_MALLOC (chunk.size + 1))) { FLUID_LOG(FLUID_ERR, "Out of memory"); return (FAIL); } /* attach to INFO list, sfont_close will cleanup if FAIL occurs */ sf->info = fluid_list_append (sf->info, item); *(unsigned char *) item = id; if (!safe_fread (&item[1], chunk.size, fd)) return (FAIL); /* force terminate info item (don't forget uint8 info ID) */ *(item + chunk.size) = '\0'; } else return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk"))); size -= chunk.size; } if (size < 0) return (gerr (ErrCorr, _("INFO chunk size mismatch"))); return (OK); } static int process_sdta (unsigned int size, SFData * sf, FILE * fd) { SFChunk chunk; if (size == 0) return (OK); /* no sample data? */ /* read sub chunk */ READCHUNK (&chunk, fd); size -= 8; if (chunkid (chunk.id) != SMPL_ID) return (gerr (ErrCorr, _("Expected SMPL chunk found invalid id instead"))); /* SDTA chunk may also contain sm24 chunk for 24 bit samples * (not yet supported), only an error if SMPL chunk size is * greater than SDTA. */ if (chunk.size > size) return (gerr (ErrCorr, _("SDTA chunk size mismatch"))); /* sample data follows */ sf->samplepos = ftell (fd); /* used in fixup_sample() to check validity of sample headers */ sdtachunk_size = chunk.size; sf->samplesize = chunk.size; FSKIP (size, fd); return (OK); } static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk, int * size, FILE * fd) { unsigned int id; char *expstr; expstr = CHNKIDSTR (expid); /* in case we need it */ READCHUNK (chunk, fd); *size -= 8; if ((id = chunkid (chunk->id)) != expid) return (gerr (ErrCorr, _("Expected" " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr)); if (chunk->size % reclen) /* valid chunk size? */ return (gerr (ErrCorr, _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr, reclen)); if ((*size -= chunk->size) < 0) return (gerr (ErrCorr, _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr)); return (OK); } static int process_pdta (int size, SFData * sf, FILE * fd) { SFChunk chunk; if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd)) return (FAIL); if (!load_phdr (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd)) return (FAIL); if (!load_pbag (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd)) return (FAIL); if (!load_pmod (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd)) return (FAIL); if (!load_pgen (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd)) return (FAIL); if (!load_ihdr (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd)) return (FAIL); if (!load_ibag (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd)) return (FAIL); if (!load_imod (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd)) return (FAIL); if (!load_igen (chunk.size, sf, fd)) return (FAIL); if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd)) return (FAIL); if (!load_shdr (chunk.size, sf, fd)) return (FAIL); return (OK); } /* preset header loader */ static int load_phdr (int size, SFData * sf, FILE * fd) { int i, i2; SFPreset *p, *pr = NULL; /* ptr to current & previous preset */ unsigned short zndx, pzndx = 0; if (size % SFPHDRSIZE || size == 0) return (gerr (ErrCorr, _("Preset header chunk size is invalid"))); i = size / SFPHDRSIZE - 1; if (i == 0) { /* at least one preset + term record */ FLUID_LOG (FLUID_WARN, _("File contains no presets")); FSKIP (SFPHDRSIZE, fd); return (OK); } for (; i > 0; i--) { /* load all preset headers */ p = FLUID_NEW (SFPreset); sf->preset = fluid_list_append (sf->preset, p); p->zone = NULL; /* In case of failure, sfont_close can cleanup */ READSTR (&p->name, fd); /* possible read failure ^ */ READW (p->prenum, fd); READW (p->bank, fd); READW (zndx, fd); READD (p->libr, fd); READD (p->genre, fd); READD (p->morph, fd); if (pr) { /* not first preset? */ if (zndx < pzndx) return (gerr (ErrCorr, _("Preset header indices not monotonic"))); i2 = zndx - pzndx; while (i2--) { pr->zone = fluid_list_prepend (pr->zone, NULL); } } else if (zndx > 0) /* 1st preset, warn if ofs >0 */ FLUID_LOG (FLUID_WARN, _("%d preset zones not referenced, discarding"), zndx); pr = p; /* update preset ptr */ pzndx = zndx; } FSKIP (24, fd); READW (zndx, fd); /* Read terminal generator index */ FSKIP (12, fd); if (zndx < pzndx) return (gerr (ErrCorr, _("Preset header indices not monotonic"))); i2 = zndx - pzndx; while (i2--) { pr->zone = fluid_list_prepend (pr->zone, NULL); } return (OK); } /* preset bag loader */ static int load_pbag (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2; SFZone *z, *pz = NULL; unsigned short genndx, modndx; unsigned short pgenndx = 0, pmodndx = 0; unsigned short i; if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ return (gerr (ErrCorr, _("Preset bag chunk size is invalid"))); p = sf->preset; while (p) { /* traverse through presets */ p2 = ((SFPreset *) (p->data))->zone; while (p2) { /* traverse preset's zones */ if ((size -= SFBAGSIZE) < 0) return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); z = FLUID_NEW (SFZone); p2->data = z; z->gen = NULL; /* Init gen and mod before possible failure, */ z->mod = NULL; /* to ensure proper cleanup (sfont_close) */ READW (genndx, fd); /* possible read failure ^ */ READW (modndx, fd); z->instsamp = NULL; if (pz) { /* if not first zone */ if (genndx < pgenndx) return (gerr (ErrCorr, _("Preset bag generator indices not monotonic"))); if (modndx < pmodndx) return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic"))); i = genndx - pgenndx; while (i--) pz->gen = fluid_list_prepend (pz->gen, NULL); i = modndx - pmodndx; while (i--) pz->mod = fluid_list_prepend (pz->mod, NULL); } pz = z; /* update previous zone ptr */ pgenndx = genndx; /* update previous zone gen index */ pmodndx = modndx; /* update previous zone mod index */ p2 = fluid_list_next (p2); } p = fluid_list_next (p); } size -= SFBAGSIZE; if (size != 0) return (gerr (ErrCorr, _("Preset bag chunk size mismatch"))); READW (genndx, fd); READW (modndx, fd); if (!pz) { if (genndx > 0) FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0")); if (modndx > 0) FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0")); return (OK); } if (genndx < pgenndx) return (gerr (ErrCorr, _("Preset bag generator indices not monotonic"))); if (modndx < pmodndx) return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic"))); i = genndx - pgenndx; while (i--) pz->gen = fluid_list_prepend (pz->gen, NULL); i = modndx - pmodndx; while (i--) pz->mod = fluid_list_prepend (pz->mod, NULL); return (OK); } /* preset modulator loader */ static int load_pmod (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2, *p3; SFMod *m; p = sf->preset; while (p) { /* traverse through all presets */ p2 = ((SFPreset *) (p->data))->zone; while (p2) { /* traverse this preset's zones */ p3 = ((SFZone *) (p2->data))->mod; while (p3) { /* load zone's modulators */ if ((size -= SFMODSIZE) < 0) return (gerr (ErrCorr, _("Preset modulator chunk size mismatch"))); m = FLUID_NEW (SFMod); p3->data = m; READW (m->src, fd); READW (m->dest, fd); READW (m->amount, fd); READW (m->amtsrc, fd); READW (m->trans, fd); p3 = fluid_list_next (p3); } p2 = fluid_list_next (p2); } p = fluid_list_next (p); } /* If there isn't even a terminal record Hmmm, the specs say there should be one, but.. */ if (size == 0) return (OK); size -= SFMODSIZE; if (size != 0) return (gerr (ErrCorr, _("Preset modulator chunk size mismatch"))); FSKIP (SFMODSIZE, fd); /* terminal mod */ return (OK); } /* ------------------------------------------------------------------- * preset generator loader * generator (per preset) loading rules: * Zones with no generators or modulators shall be annihilated * Global zone must be 1st zone, discard additional ones (instrumentless zones) * * generator (per zone) loading rules (in order of decreasing precedence): * KeyRange is 1st in list (if exists), else discard * if a VelRange exists only preceded by a KeyRange, else discard * if a generator follows an instrument discard it * if a duplicate generator exists replace previous one * ------------------------------------------------------------------- */ static int load_pgen (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; SFZone *z; SFGen *g; SFGenAmount genval; unsigned short genid; int level, skip, drop, gzone, discarded; p = sf->preset; while (p) { /* traverse through all presets */ gzone = FALSE; discarded = FALSE; p2 = ((SFPreset *) (p->data))->zone; if (p2) hz = &p2; while (p2) { /* traverse preset's zones */ level = 0; z = (SFZone *) (p2->data); p3 = z->gen; while (p3) { /* load zone's generators */ dup = NULL; skip = FALSE; drop = FALSE; if ((size -= SFGENSIZE) < 0) return (gerr (ErrCorr, _("Preset generator chunk size mismatch"))); READW (genid, fd); if (genid == Gen_KeyRange) { /* nothing precedes */ if (level == 0) { level = 1; READB (genval.range.lo, fd); READB (genval.range.hi, fd); } else skip = TRUE; } else if (genid == Gen_VelRange) { /* only KeyRange precedes */ if (level <= 1) { level = 2; READB (genval.range.lo, fd); READB (genval.range.hi, fd); } else skip = TRUE; } else if (genid == Gen_Instrument) { /* inst is last gen */ level = 3; READW (genval.uword, fd); ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); break; /* break out of generator loop */ } else { level = 2; if (gen_validp (genid)) { /* generator valid? */ READW (genval.sword, fd); dup = gen_inlist (genid, z->gen); } else skip = TRUE; } if (!skip) { if (!dup) { /* if gen ! dup alloc new */ g = FLUID_NEW (SFGen); p3->data = g; g->id = genid; } else { g = (SFGen *) (dup->data); /* ptr to orig gen */ drop = TRUE; } g->amount = genval; } else { /* Skip this generator */ discarded = TRUE; drop = TRUE; FSKIPW (fd); } if (!drop) p3 = fluid_list_next (p3); /* next gen */ else SLADVREM (z->gen, p3); /* drop place holder */ } /* generator loop */ if (level == 3) SLADVREM (z->gen, p3); /* zone has inst? */ else { /* congratulations its a global zone */ if (!gzone) { /* Prior global zones? */ gzone = TRUE; /* if global zone is not 1st zone, relocate */ if (*hz != p2) { void* save = p2->data; FLUID_LOG (FLUID_WARN, _("Preset \"%s\": Global zone is not first zone"), ((SFPreset *) (p->data))->name); SLADVREM (*hz, p2); *hz = fluid_list_prepend (*hz, save); continue; } } else { /* previous global zone exists, discard */ FLUID_LOG (FLUID_WARN, _("Preset \"%s\": Discarding invalid global zone"), ((SFPreset *) (p->data))->name); sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); } } while (p3) { /* Kill any zones following an instrument */ discarded = TRUE; if ((size -= SFGENSIZE) < 0) return (gerr (ErrCorr, _("Preset generator chunk size mismatch"))); FSKIP (SFGENSIZE, fd); SLADVREM (z->gen, p3); } p2 = fluid_list_next (p2); /* next zone */ } if (discarded) FLUID_LOG(FLUID_WARN, _("Preset \"%s\": Some invalid generators were discarded"), ((SFPreset *) (p->data))->name); p = fluid_list_next (p); } /* in case there isn't a terminal record */ if (size == 0) return (OK); size -= SFGENSIZE; if (size != 0) return (gerr (ErrCorr, _("Preset generator chunk size mismatch"))); FSKIP (SFGENSIZE, fd); /* terminal gen */ return (OK); } /* instrument header loader */ static int load_ihdr (int size, SFData * sf, FILE * fd) { int i, i2; SFInst *p, *pr = NULL; /* ptr to current & previous instrument */ unsigned short zndx, pzndx = 0; if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */ return (gerr (ErrCorr, _("Instrument header has invalid size"))); size = size / SFIHDRSIZE - 1; if (size == 0) { /* at least one preset + term record */ FLUID_LOG (FLUID_WARN, _("File contains no instruments")); FSKIP (SFIHDRSIZE, fd); return (OK); } for (i = 0; i < size; i++) { /* load all instrument headers */ p = FLUID_NEW (SFInst); sf->inst = fluid_list_append (sf->inst, p); p->zone = NULL; /* For proper cleanup if fail (sfont_close) */ READSTR (&p->name, fd); /* Possible read failure ^ */ READW (zndx, fd); if (pr) { /* not first instrument? */ if (zndx < pzndx) return (gerr (ErrCorr, _("Instrument header indices not monotonic"))); i2 = zndx - pzndx; while (i2--) pr->zone = fluid_list_prepend (pr->zone, NULL); } else if (zndx > 0) /* 1st inst, warn if ofs >0 */ FLUID_LOG (FLUID_WARN, _("%d instrument zones not referenced, discarding"), zndx); pzndx = zndx; pr = p; /* update instrument ptr */ } FSKIP (20, fd); READW (zndx, fd); if (zndx < pzndx) return (gerr (ErrCorr, _("Instrument header indices not monotonic"))); i2 = zndx - pzndx; while (i2--) pr->zone = fluid_list_prepend (pr->zone, NULL); return (OK); } /* instrument bag loader */ static int load_ibag (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2; SFZone *z, *pz = NULL; unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; int i; if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */ return (gerr (ErrCorr, _("Instrument bag chunk size is invalid"))); p = sf->inst; while (p) { /* traverse through inst */ p2 = ((SFInst *) (p->data))->zone; while (p2) { /* load this inst's zones */ if ((size -= SFBAGSIZE) < 0) return (gerr (ErrCorr, _("Instrument bag chunk size mismatch"))); z = FLUID_NEW (SFZone); p2->data = z; z->gen = NULL; /* In case of failure, */ z->mod = NULL; /* sfont_close can clean up */ READW (genndx, fd); /* READW = possible read failure */ READW (modndx, fd); z->instsamp = NULL; if (pz) { /* if not first zone */ if (genndx < pgenndx) return (gerr (ErrCorr, _("Instrument generator indices not monotonic"))); if (modndx < pmodndx) return (gerr (ErrCorr, _("Instrument modulator indices not monotonic"))); i = genndx - pgenndx; while (i--) pz->gen = fluid_list_prepend (pz->gen, NULL); i = modndx - pmodndx; while (i--) pz->mod = fluid_list_prepend (pz->mod, NULL); } pz = z; /* update previous zone ptr */ pgenndx = genndx; pmodndx = modndx; p2 = fluid_list_next (p2); } p = fluid_list_next (p); } size -= SFBAGSIZE; if (size != 0) return (gerr (ErrCorr, _("Instrument chunk size mismatch"))); READW (genndx, fd); READW (modndx, fd); if (!pz) { /* in case that all are no zoners */ if (genndx > 0) FLUID_LOG (FLUID_WARN, _("No instrument generators and terminal index not 0")); if (modndx > 0) FLUID_LOG (FLUID_WARN, _("No instrument modulators and terminal index not 0")); return (OK); } if (genndx < pgenndx) return (gerr (ErrCorr, _("Instrument generator indices not monotonic"))); if (modndx < pmodndx) return (gerr (ErrCorr, _("Instrument modulator indices not monotonic"))); i = genndx - pgenndx; while (i--) pz->gen = fluid_list_prepend (pz->gen, NULL); i = modndx - pmodndx; while (i--) pz->mod = fluid_list_prepend (pz->mod, NULL); return (OK); } /* instrument modulator loader */ static int load_imod (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2, *p3; SFMod *m; p = sf->inst; while (p) { /* traverse through all inst */ p2 = ((SFInst *) (p->data))->zone; while (p2) { /* traverse this inst's zones */ p3 = ((SFZone *) (p2->data))->mod; while (p3) { /* load zone's modulators */ if ((size -= SFMODSIZE) < 0) return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch"))); m = FLUID_NEW (SFMod); p3->data = m; READW (m->src, fd); READW (m->dest, fd); READW (m->amount, fd); READW (m->amtsrc, fd); READW (m->trans, fd); p3 = fluid_list_next (p3); } p2 = fluid_list_next (p2); } p = fluid_list_next (p); } /* If there isn't even a terminal record Hmmm, the specs say there should be one, but.. */ if (size == 0) return (OK); size -= SFMODSIZE; if (size != 0) return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch"))); FSKIP (SFMODSIZE, fd); /* terminal mod */ return (OK); } /* load instrument generators (see load_pgen for loading rules) */ static int load_igen (int size, SFData * sf, FILE * fd) { fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; SFZone *z; SFGen *g; SFGenAmount genval; unsigned short genid; int level, skip, drop, gzone, discarded; p = sf->inst; while (p) { /* traverse through all instruments */ gzone = FALSE; discarded = FALSE; p2 = ((SFInst *) (p->data))->zone; if (p2) hz = &p2; while (p2) { /* traverse this instrument's zones */ level = 0; z = (SFZone *) (p2->data); p3 = z->gen; while (p3) { /* load zone's generators */ dup = NULL; skip = FALSE; drop = FALSE; if ((size -= SFGENSIZE) < 0) return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); READW (genid, fd); if (genid == Gen_KeyRange) { /* nothing precedes */ if (level == 0) { level = 1; READB (genval.range.lo, fd); READB (genval.range.hi, fd); } else skip = TRUE; } else if (genid == Gen_VelRange) { /* only KeyRange precedes */ if (level <= 1) { level = 2; READB (genval.range.lo, fd); READB (genval.range.hi, fd); } else skip = TRUE; } else if (genid == Gen_SampleId) { /* sample is last gen */ level = 3; READW (genval.uword, fd); ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1); break; /* break out of generator loop */ } else { level = 2; if (gen_valid (genid)) { /* gen valid? */ READW (genval.sword, fd); dup = gen_inlist (genid, z->gen); } else skip = TRUE; } if (!skip) { if (!dup) { /* if gen ! dup alloc new */ g = FLUID_NEW (SFGen); p3->data = g; g->id = genid; } else { g = (SFGen *) (dup->data); drop = TRUE; } g->amount = genval; } else { /* skip this generator */ discarded = TRUE; drop = TRUE; FSKIPW (fd); } if (!drop) p3 = fluid_list_next (p3); /* next gen */ else SLADVREM (z->gen, p3); } /* generator loop */ if (level == 3) SLADVREM (z->gen, p3); /* zone has sample? */ else { /* its a global zone */ if (!gzone) { gzone = TRUE; /* if global zone is not 1st zone, relocate */ if (*hz != p2) { void* save = p2->data; FLUID_LOG (FLUID_WARN, _("Instrument \"%s\": Global zone is not first zone"), ((SFPreset *) (p->data))->name); SLADVREM (*hz, p2); *hz = fluid_list_prepend (*hz, save); continue; } } else { /* previous global zone exists, discard */ FLUID_LOG (FLUID_WARN, _("Instrument \"%s\": Discarding invalid global zone"), ((SFInst *) (p->data))->name); sfont_zone_delete (sf, hz, (SFZone *) (p2->data)); } } while (p3) { /* Kill any zones following a sample */ discarded = TRUE; if ((size -= SFGENSIZE) < 0) return (gerr (ErrCorr, _("Instrument generator chunk size mismatch"))); FSKIP (SFGENSIZE, fd); SLADVREM (z->gen, p3); } p2 = fluid_list_next (p2); /* next zone */ } if (discarded) FLUID_LOG(FLUID_WARN, _("Instrument \"%s\": Some invalid generators were discarded"), ((SFInst *) (p->data))->name); p = fluid_list_next (p); } /* for those non-terminal record cases, grr! */ if (size == 0) return (OK); size -= SFGENSIZE; if (size != 0) return (gerr (ErrCorr, _("IGEN chunk size mismatch"))); FSKIP (SFGENSIZE, fd); /* terminal gen */ return (OK); } /* sample header loader */ static int load_shdr (unsigned int size, SFData * sf, FILE * fd) { unsigned int i; SFSample *p; if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */ return (gerr (ErrCorr, _("Sample header has invalid size"))); size = size / SFSHDRSIZE - 1; if (size == 0) { /* at least one sample + term record? */ FLUID_LOG (FLUID_WARN, _("File contains no samples")); FSKIP (SFSHDRSIZE, fd); return (OK); } /* load all sample headers */ for (i = 0; i < size; i++) { p = FLUID_NEW (SFSample); sf->sample = fluid_list_append (sf->sample, p); READSTR (&p->name, fd); READD (p->start, fd); READD (p->end, fd); /* - end, loopstart and loopend */ READD (p->loopstart, fd); /* - will be checked and turned into */ READD (p->loopend, fd); /* - offsets in fixup_sample() */ READD (p->samplerate, fd); READB (p->origpitch, fd); READB (p->pitchadj, fd); FSKIPW (fd); /* skip sample link */ READW (p->sampletype, fd); p->samfile = 0; } FSKIP (SFSHDRSIZE, fd); /* skip terminal shdr */ return (OK); } /* "fixup" (inst # -> inst ptr) instrument references in preset list */ static int fixup_pgen (SFData * sf) { fluid_list_t *p, *p2, *p3; SFZone *z; int i; p = sf->preset; while (p) { p2 = ((SFPreset *) (p->data))->zone; while (p2) { /* traverse this preset's zones */ z = (SFZone *) (p2->data); if ((i = GPOINTER_TO_INT (z->instsamp))) { /* load instrument # */ p3 = fluid_list_nth (sf->inst, i - 1); if (!p3) return (gerr (ErrCorr, _("Preset %03d %03d: Invalid instrument reference"), ((SFPreset *) (p->data))->bank, ((SFPreset *) (p->data))->prenum)); z->instsamp = p3; } else z->instsamp = NULL; p2 = fluid_list_next (p2); } p = fluid_list_next (p); } return (OK); } /* "fixup" (sample # -> sample ptr) sample references in instrument list */ static int fixup_igen (SFData * sf) { fluid_list_t *p, *p2, *p3; SFZone *z; int i; p = sf->inst; while (p) { p2 = ((SFInst *) (p->data))->zone; while (p2) { /* traverse instrument's zones */ z = (SFZone *) (p2->data); if ((i = GPOINTER_TO_INT (z->instsamp))) { /* load sample # */ p3 = fluid_list_nth (sf->sample, i - 1); if (!p3) return (gerr (ErrCorr, _("Instrument \"%s\": Invalid sample reference"), ((SFInst *) (p->data))->name)); z->instsamp = p3; } p2 = fluid_list_next (p2); } p = fluid_list_next (p); } return (OK); } /* convert sample end, loopstart and loopend to offsets and check if valid */ static int fixup_sample (SFData * sf) { fluid_list_t *p; SFSample *sam; int invalid_loops=FALSE; int invalid_loopstart; int invalid_loopend, loopend_end_mismatch; p = sf->sample; while (p) { sam = (SFSample *) (p->data); /* The SoundFont 2.4 spec defines the loopstart index as the first sample point of the loop */ invalid_loopstart = (sam->loopstart < sam->start) || (sam->loopstart >= sam->loopend); /* while loopend is the first point AFTER the last sample of the loop. * this is as it should be. however we cannot be sure whether any of sam.loopend or sam.end * is correct. hours of thinking through this have concluded, that it would be best practice * to mangle with loops as little as necessary by only making sure loopend is within * sdtachunk_size. incorrect soundfont shall preferably fail loudly. */ invalid_loopend = (sam->loopend > sdtachunk_size) || (sam->loopstart >= sam->loopend); loopend_end_mismatch = (sam->loopend > sam->end); /* if sample is not a ROM sample and end is over the sample data chunk or sam start is greater than 4 less than the end (at least 4 samples) */ if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM) && sam->end > sdtachunk_size) || sam->start > (sam->end - 4)) { FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid," " disabling and will not be saved"), sam->name); /* disable sample by setting all sample markers to 0 */ sam->start = sam->end = sam->loopstart = sam->loopend = 0; return (OK); } else if (sam->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) { /* * compressed samples get fixed up after decompression * * however we cant use the logic below, because uncompressed samples are stored in individual buffers */ } else if (invalid_loopstart || invalid_loopend || loopend_end_mismatch) /* loop is fowled?? (cluck cluck :) */ { /* though illegal, loopend may be set to loopstart to disable loop */ /* is it worth informing the user? */ invalid_loops |= (sam->loopend != sam->loopstart); /* force incorrect loop points into the sample range, ignore padding */ if(invalid_loopstart) { FLUID_LOG (FLUID_DBG, _("Sample '%s' has unusable loop start '%d'," " setting to sample start at '%d'"), sam->name, sam->loopstart, sam->start); sam->loopstart = sam->start; } if(invalid_loopend) { FLUID_LOG (FLUID_DBG, _("Sample '%s' has unusable loop stop '%d'," " setting to sample stop at '%d'"), sam->name, sam->loopend, sam->end); /* since at this time sam->end points after valid sample data (will correct that few lines below), * set loopend to that first invalid sample, since it should never be played, but instead the last * valid sample will be played */ sam->loopend = sam->end; } if(loopend_end_mismatch) { FLUID_LOG (FLUID_DBG, _("Sample '%s' has invalid loop stop '%d'," " sample stop at '%d', using it anyway"), sam->name, sam->loopend, sam->end); } } /* convert sample end, loopstart, loopend to offsets from sam->start */ sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */ sam->loopstart -= sam->start; sam->loopend -= sam->start; p = fluid_list_next (p); } if(invalid_loops) { FLUID_LOG (FLUID_WARN, _("Found samples with invalid loops, audible glitches possible.")); } return (OK); } /*=================================sfont.c======================== Smurf SoundFont Editor ================================================================*/ /* optimum chunk area sizes (could be more optimum) */ #define PRESET_CHUNK_OPTIMUM_AREA 256 #define INST_CHUNK_OPTIMUM_AREA 256 #define SAMPLE_CHUNK_OPTIMUM_AREA 256 #define ZONE_CHUNK_OPTIMUM_AREA 256 #define MOD_CHUNK_OPTIMUM_AREA 256 #define GEN_CHUNK_OPTIMUM_AREA 256 unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4, Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0 }; unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs, Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass, Gen_OverrideRootKey, 0 }; /* close SoundFont file and delete a SoundFont structure */ void sfont_close (SFData * sf) { fluid_list_t *p, *p2; if (sf->sffd) fclose (sf->sffd); if (sf->fname) free (sf->fname); p = sf->info; while (p) { free (p->data); p = fluid_list_next (p); } delete_fluid_list(sf->info); sf->info = NULL; p = sf->preset; while (p) { /* loop over presets */ p2 = ((SFPreset *) (p->data))->zone; while (p2) { /* loop over preset's zones */ sfont_free_zone (p2->data); p2 = fluid_list_next (p2); } /* free preset's zone list */ delete_fluid_list (((SFPreset *) (p->data))->zone); FLUID_FREE (p->data); /* free preset chunk */ p = fluid_list_next (p); } delete_fluid_list (sf->preset); sf->preset = NULL; p = sf->inst; while (p) { /* loop over instruments */ p2 = ((SFInst *) (p->data))->zone; while (p2) { /* loop over inst's zones */ sfont_free_zone (p2->data); p2 = fluid_list_next (p2); } /* free inst's zone list */ delete_fluid_list (((SFInst *) (p->data))->zone); FLUID_FREE (p->data); p = fluid_list_next (p); } delete_fluid_list (sf->inst); sf->inst = NULL; p = sf->sample; while (p) { FLUID_FREE (p->data); p = fluid_list_next (p); } delete_fluid_list (sf->sample); sf->sample = NULL; FLUID_FREE (sf); } /* free all elements of a zone (Preset or Instrument) */ void sfont_free_zone (SFZone * zone) { fluid_list_t *p; if (!zone) return; p = zone->gen; while (p) { /* Free gen chunks for this zone */ if (p->data) FLUID_FREE (p->data); p = fluid_list_next (p); } delete_fluid_list (zone->gen); /* free genlist */ p = zone->mod; while (p) { /* Free mod chunks for this zone */ if (p->data) FLUID_FREE (p->data); p = fluid_list_next (p); } delete_fluid_list (zone->mod); /* free modlist */ FLUID_FREE (zone); /* free zone chunk */ } /* preset sort function, first by bank, then by preset # */ int sfont_preset_compare_func (void* a, void* b) { int aval, bval; aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum; bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum; return (aval - bval); } /* delete zone from zone list */ void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone) { *zlist = fluid_list_remove (*zlist, (void*) zone); sfont_free_zone (zone); } /* Find generator in gen list */ fluid_list_t * gen_inlist (int gen, fluid_list_t * genlist) { /* is generator in gen list? */ fluid_list_t *p; p = genlist; while (p) { if (p->data == NULL) return (NULL); if (gen == ((SFGen *) p->data)->id) break; p = fluid_list_next (p); } return (p); } /* check validity of instrument generator */ int gen_valid (int gen) { /* is generator id valid? */ int i = 0; if (gen > Gen_MaxValid) return (FALSE); while (badgen[i] && badgen[i] != gen) i++; return (badgen[i] == 0); } /* check validity of preset generator */ int gen_validp (int gen) { /* is preset generator valid? */ int i = 0; if (!gen_valid (gen)) return (FALSE); while (badpgen[i] && badpgen[i] != (unsigned short) gen) i++; return (badpgen[i] == 0); } /*================================util.c===========================*/ /* Logging function, returns FAIL to use as a return value in calling funcs */ int gerr (int ev, char * fmt, ...) { va_list args; va_start (args, fmt); vprintf(fmt, args); va_end (args); printf("\n"); return (FAIL); } int safe_fread (void *buf, int count, FILE * fd) { if (fread (buf, count, 1, fd) != 1) { /* size_t = count, nmemb = 1 */ if (feof (fd)) gerr (ErrEof, _("EOF while attemping to read %d bytes"), count); else FLUID_LOG (FLUID_ERR, _("File read failed")); return (FAIL); } return (OK); } int safe_fseek (FILE * fd, long ofs, int whence) { if (fseek (fd, ofs, whence) == -1) { FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence); return (FAIL); } return (OK); } fluidsynth-1.1.9/src/sfloader/fluid_defsfont.h000066400000000000000000000445111322272076000214530ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green * * 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 */ #ifndef _FLUID_DEFSFONT_H #define _FLUID_DEFSFONT_H #include "fluidsynth.h" #include "fluidsynth_priv.h" #include "fluid_list.h" /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /*-----------------------------------sfont.h----------------------------*/ #define SF_SAMPMODES_LOOP 1 #define SF_SAMPMODES_UNROLL 2 #define SF_MIN_SAMPLERATE 400 #define SF_MAX_SAMPLERATE 50000 #define SF_MIN_SAMPLE_LENGTH 32 /* Sound Font structure defines */ typedef struct _SFVersion { /* version structure */ unsigned short major; unsigned short minor; } SFVersion; typedef struct _SFMod { /* Modulator structure */ unsigned short src; /* source modulator */ unsigned short dest; /* destination generator */ signed short amount; /* signed, degree of modulation */ unsigned short amtsrc; /* second source controls amnt of first */ unsigned short trans; /* transform applied to source */ } SFMod; typedef union _SFGenAmount { /* Generator amount structure */ signed short sword; /* signed 16 bit value */ unsigned short uword; /* unsigned 16 bit value */ struct { unsigned char lo; /* low value for ranges */ unsigned char hi; /* high value for ranges */ } range; } SFGenAmount; typedef struct _SFGen { /* Generator structure */ unsigned short id; /* generator ID */ SFGenAmount amount; /* generator value */ } SFGen; typedef struct _SFZone { /* Sample/instrument zone structure */ fluid_list_t *instsamp; /* instrument/sample pointer for zone */ fluid_list_t *gen; /* list of generators */ fluid_list_t *mod; /* list of modulators */ } SFZone; typedef struct _SFSample { /* Sample structure */ char name[21]; /* Name of sample */ unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */ unsigned int start; /* Offset in sample area to start of sample */ unsigned int end; /* Offset from start to end of sample, this is the last point of the sample, the SF spec has this as the 1st point after, corrected on load/save */ unsigned int loopstart; /* Offset from start to start of loop */ unsigned int loopend; /* Offset from start to end of loop, marks the first point after loop, whose sample value is ideally equivalent to loopstart */ unsigned int samplerate; /* Sample rate recorded at */ unsigned char origpitch; /* root midi key number */ signed char pitchadj; /* pitch correction in cents */ unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */ } SFSample; typedef struct _SFInst { /* Instrument structure */ char name[21]; /* Name of instrument */ fluid_list_t *zone; /* list of instrument zones */ } SFInst; typedef struct _SFPreset { /* Preset structure */ char name[21]; /* preset name */ unsigned short prenum; /* preset number */ unsigned short bank; /* bank number */ unsigned int libr; /* Not used (preserved) */ unsigned int genre; /* Not used (preserved) */ unsigned int morph; /* Not used (preserved) */ fluid_list_t *zone; /* list of preset zones */ } SFPreset; /* NOTE: sffd is also used to determine if sound font is new (NULL) */ typedef struct _SFData { /* Sound font data structure */ SFVersion version; /* sound font version */ SFVersion romver; /* ROM version */ unsigned int samplepos; /* position within sffd of the sample chunk */ unsigned int samplesize; /* length within sffd of the sample chunk */ char *fname; /* file name */ FILE *sffd; /* loaded sfont file descriptor */ fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ fluid_list_t *preset; /* linked list of preset info */ fluid_list_t *inst; /* linked list of instrument info */ fluid_list_t *sample; /* linked list of sample info */ } SFData; /* sf file chunk IDs */ enum { UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, INFO_ID, SDTA_ID, PDTA_ID, /* info/sample/preset */ 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 */ 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 */ }; /* generator types */ typedef enum { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch, Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ, Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs, Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan, Gen_Unused2, Gen_Unused3, Gen_Unused4, Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq, Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay, Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold, Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack, Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease, Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument, Gen_Reserved1, Gen_KeyRange, Gen_VelRange, Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs, Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes, Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey, Gen_Dummy } Gen_Type; #define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */ #define Gen_Count Gen_Dummy /* count of generators */ #define GenArrSize sizeof(SFGenAmount)*Gen_Count /* gen array size */ /* generator unit type */ typedef enum { None, /* No unit type */ Unit_Smpls, /* in samples */ Unit_32kSmpls, /* in 32k samples */ Unit_Cent, /* in cents (1/100th of a semitone) */ Unit_HzCent, /* in Hz Cents */ Unit_TCent, /* in Time Cents */ Unit_cB, /* in centibels (1/100th of a decibel) */ Unit_Percent, /* in percentage */ Unit_Semitone, /* in semitones */ Unit_Range /* a range of values */ } Gen_Unit; /* global data */ extern unsigned short badgen[]; /* list of bad generators */ extern unsigned short badpgen[]; /* list of bad preset generators */ /* functions */ void sfont_init_chunks (void); void sfont_close (SFData * sf); void sfont_free_zone (SFZone * zone); int sfont_preset_compare_func (void* a, void* b); void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone); fluid_list_t *gen_inlist (int gen, fluid_list_t * genlist); int gen_valid (int gen); int gen_validp (int gen); /*-----------------------------------sffile.h----------------------------*/ /* File structures and routines (used to be in sffile.h) */ #define CHNKIDSTR(id) &idlist[(id - 1) * 4] /* sfont file chunk sizes */ #define SFPHDRSIZE 38 #define SFBAGSIZE 4 #define SFMODSIZE 10 #define SFGENSIZE 4 #define SFIHDRSIZE 22 #define SFSHDRSIZE 46 /* sfont file data structures */ typedef struct _SFChunk { /* RIFF file chunk structure */ unsigned int id; /* chunk id */ unsigned int size; /* size of the following chunk */ } SFChunk; typedef struct _SFPhdr { unsigned char name[20]; /* preset name */ unsigned short preset; /* preset number */ unsigned short bank; /* bank number */ unsigned short pbagndx; /* index into preset bag */ unsigned int library; /* just for preserving them */ unsigned int genre; /* Not used */ unsigned int morphology; /* Not used */ } SFPhdr; typedef struct _SFBag { unsigned short genndx; /* index into generator list */ unsigned short modndx; /* index into modulator list */ } SFBag; typedef struct _SFIhdr { char name[20]; /* Name of instrument */ unsigned short ibagndx; /* Instrument bag index */ } SFIhdr; typedef struct _SFShdr { /* Sample header loading struct */ char name[20]; /* Sample name */ unsigned int start; /* Offset to start of sample */ unsigned int end; /* Offset to end of sample */ unsigned int loopstart; /* Offset to start of loop */ unsigned int loopend; /* Offset to end of loop */ unsigned int samplerate; /* Sample rate recorded at */ unsigned char origpitch; /* root midi key number */ signed char pitchadj; /* pitch correction in cents */ unsigned short samplelink; /* Not used */ unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ } SFShdr; /* data */ extern char idlist[]; /* functions */ SFData *sfload_file (const char * fname); /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02110-1301, USA. */ #include /*-----------------------------------util.h----------------------------*/ /* Utility functions (formerly in util.h) */ #define FAIL 0 #define OK 1 enum { ErrWarn, ErrFatal, ErrStatus, ErrCorr, ErrEof, ErrMem, Errno, ErrRead, ErrWrite }; #define ErrMax ErrWrite #define ErrnoStart Errno #define ErrnoEnd ErrWrite int gerr (int ev, char * fmt, ...); int safe_fread (void *buf, int count, FILE * fd); int safe_fwrite (void *buf, int count, FILE * fd); int safe_fseek (FILE * fd, long ofs, int whence); /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /*************************************************************** * * FORWARD DECLARATIONS */ typedef struct _fluid_defsfont_t fluid_defsfont_t; typedef struct _fluid_defpreset_t fluid_defpreset_t; typedef struct _fluid_preset_zone_t fluid_preset_zone_t; typedef struct _fluid_inst_t fluid_inst_t; typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /* Public interface */ fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings); int delete_fluid_defsfloader(fluid_sfloader_t* loader); fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename); int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont); char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont); fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont); int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); int fluid_defpreset_preset_delete(fluid_preset_t* preset); char* fluid_defpreset_preset_get_name(fluid_preset_t* preset); int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset); int fluid_defpreset_preset_get_num(fluid_preset_t* preset); int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); /* * fluid_defsfont_t */ struct _fluid_defsfont_t { char* filename; /* the filename of this soundfont */ unsigned int samplepos; /* the position in the file at which the sample data starts */ unsigned int samplesize; /* the size of the sample data */ short* sampledata; /* the sample data, loaded in ram */ fluid_list_t* sample; /* the samples in this soundfont */ fluid_defpreset_t* preset; /* the presets of this soundfont */ int mlock; /* Should we try memlock (avoid swapping)? */ fluid_preset_t iter_preset; /* preset interface used in the iteration */ fluid_defpreset_t* iter_cur; /* the current preset in the iteration */ fluid_preset_t** preset_stack; /* List of presets that are available to use */ int preset_stack_capacity; /* Length of preset_stack array */ int preset_stack_size; /* Current number of items in the stack */ }; fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings); int delete_fluid_defsfont(fluid_defsfont_t* sfont); int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file); char* fluid_defsfont_get_name(fluid_defsfont_t* sfont); fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int prenum); void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont); int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset); int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont); int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample); int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset); /* * fluid_preset_t */ struct _fluid_defpreset_t { fluid_defpreset_t* next; fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */ char name[21]; /* the name of the preset */ unsigned int bank; /* the bank number */ unsigned int num; /* the preset number */ fluid_preset_zone_t* global_zone; /* the global zone of the preset */ fluid_preset_zone_t* zone; /* the chained list of preset zones */ }; fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont); int delete_fluid_defpreset(fluid_defpreset_t* preset); fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset); int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont); int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone); fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset); fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset); int fluid_defpreset_get_banknum(fluid_defpreset_t* preset); int fluid_defpreset_get_num(fluid_defpreset_t* preset); char* fluid_defpreset_get_name(fluid_defpreset_t* preset); int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); /* * fluid_preset_zone */ struct _fluid_preset_zone_t { fluid_preset_zone_t* next; char* name; fluid_inst_t* inst; int keylo; int keyhi; int vello; int velhi; fluid_gen_t gen[GEN_LAST]; fluid_mod_t * mod; /* List of modulators */ }; fluid_preset_zone_t* new_fluid_preset_zone(char* name); int delete_fluid_preset_zone(fluid_preset_zone_t* zone); fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset); int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont); int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel); fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone); /* * fluid_inst_t */ struct _fluid_inst_t { char name[21]; fluid_inst_zone_t* global_zone; fluid_inst_zone_t* zone; }; fluid_inst_t* new_fluid_inst(void); int delete_fluid_inst(fluid_inst_t* inst); int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont); 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); fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst); fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst); /* * fluid_inst_zone_t */ struct _fluid_inst_zone_t { fluid_inst_zone_t* next; char* name; fluid_sample_t* sample; int keylo; int keyhi; int vello; int velhi; fluid_gen_t gen[GEN_LAST]; fluid_mod_t * mod; /* List of modulators */ }; fluid_inst_zone_t* new_fluid_inst_zone(char* name); int delete_fluid_inst_zone(fluid_inst_zone_t* zone); fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone); int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel); fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); fluid_sample_t* new_fluid_sample(void); int delete_fluid_sample(fluid_sample_t* sample); int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont); int fluid_sample_in_rom(fluid_sample_t* sample); #endif /* _FLUID_SFONT_H */ fluidsynth-1.1.9/src/sfloader/fluid_ramsfont.c000066400000000000000000001060751322272076000214730ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_ramsfont.h" #include "fluid_sys.h" #include "fluid_synth.h" /* thenumber of samples before the start and after the end */ #define SAMPLE_LOOP_MARGIN 8 /* Prototypes */ static int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont); static char *fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont); static fluid_preset_t *fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); static void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont); static int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); static int fluid_rampreset_preset_delete(fluid_preset_t* preset); static char *fluid_rampreset_preset_get_name(fluid_preset_t* preset); static int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset); static int fluid_rampreset_preset_get_num(fluid_preset_t* preset); static int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); static fluid_ramsfont_t *new_fluid_ramsfont (void); static int delete_fluid_ramsfont (fluid_ramsfont_t* sfont); static char *fluid_ramsfont_get_name(fluid_ramsfont_t* sfont); static int fluid_ramsfont_add_preset (fluid_ramsfont_t* sfont, fluid_rampreset_t* preset); static fluid_rampreset_t *fluid_ramsfont_get_preset (fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num); static void fluid_ramsfont_iteration_start (fluid_ramsfont_t* sfont); static int fluid_ramsfont_iteration_next (fluid_ramsfont_t* sfont, fluid_preset_t* preset); static fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont); static int delete_fluid_rampreset (fluid_rampreset_t* preset); static int fluid_rampreset_get_banknum (fluid_rampreset_t* preset); static int fluid_rampreset_get_num (fluid_rampreset_t* preset); static char *fluid_rampreset_get_name (fluid_rampreset_t* preset); static fluid_rampreset_t *fluid_rampreset_next (fluid_rampreset_t* preset); static int fluid_rampreset_add_zone(fluid_rampreset_t* preset, fluid_preset_zone_t* zone); static int fluid_rampreset_add_sample (fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey); static fluid_inst_zone_t *fluid_rampreset_izoneforsample (fluid_rampreset_t* preset, fluid_sample_t* sample); static int fluid_rampreset_izone_set_loop (fluid_rampreset_t* preset, fluid_sample_t* sample, int on, float loopstart, float loopend); static int fluid_rampreset_izone_set_gen (fluid_rampreset_t* preset, fluid_sample_t* sample, int gen_type, float value); static int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample); static int fluid_rampreset_remembervoice (fluid_rampreset_t* preset, fluid_voice_t* voice); static void fluid_rampreset_updatevoices (fluid_rampreset_t* preset, int gen_type, float val); static int fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); /** * Create a #fluid_sfont_t wrapping a #fluid_ramsfont_t * @return New #fluid_sfont_t or NULL if out of memory */ fluid_sfont_t* fluid_ramsfont_create_sfont() { fluid_sfont_t* sfont; fluid_ramsfont_t* ramsfont; ramsfont = new_fluid_ramsfont(); if (ramsfont == NULL) { return NULL; } sfont = FLUID_NEW(fluid_sfont_t); if (sfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); delete_fluid_ramsfont(ramsfont); return NULL; } sfont->data = ramsfont; sfont->free = fluid_ramsfont_sfont_delete; sfont->get_name = fluid_ramsfont_sfont_get_name; sfont->get_preset = fluid_ramsfont_sfont_get_preset; sfont->iteration_start = fluid_ramsfont_sfont_iteration_start; sfont->iteration_next = fluid_ramsfont_sfont_iteration_next; return sfont; } /* RAM SoundFont loader method to delete SoundFont */ static int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont) { if (delete_fluid_ramsfont(sfont->data) != 0) return -1; FLUID_FREE(sfont); return 0; } /* RAM SoundFont loader method to get name */ static char * fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont) { return fluid_ramsfont_get_name((fluid_ramsfont_t*) sfont->data); } /* RAM SoundFont loader method to get a preset */ static fluid_preset_t * fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum) { fluid_preset_t* preset; fluid_rampreset_t* rampreset; rampreset = fluid_ramsfont_get_preset((fluid_ramsfont_t*) sfont->data, bank, prenum); if (rampreset == NULL) { return NULL; } preset = FLUID_NEW(fluid_preset_t); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } preset->sfont = sfont; preset->data = rampreset; preset->free = fluid_rampreset_preset_delete; preset->get_name = fluid_rampreset_preset_get_name; preset->get_banknum = fluid_rampreset_preset_get_banknum; preset->get_num = fluid_rampreset_preset_get_num; preset->noteon = fluid_rampreset_preset_noteon; preset->notify = NULL; return preset; } /* RAM SoundFont loader method to start preset iteration */ static void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont) { fluid_ramsfont_iteration_start((fluid_ramsfont_t*) sfont->data); } /* RAM SoundFont loader method to advance preset iteration */ static int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) { preset->free = fluid_rampreset_preset_delete; preset->get_name = fluid_rampreset_preset_get_name; preset->get_banknum = fluid_rampreset_preset_get_banknum; preset->get_num = fluid_rampreset_preset_get_num; preset->noteon = fluid_rampreset_preset_noteon; preset->notify = NULL; return fluid_ramsfont_iteration_next((fluid_ramsfont_t*) sfont->data, preset); } /* RAM SoundFont loader delete preset method */ static int fluid_rampreset_preset_delete(fluid_preset_t* preset) { FLUID_FREE(preset); /* TODO: free modulators */ return 0; } /* RAM SoundFont loader get preset name method */ static char * fluid_rampreset_preset_get_name(fluid_preset_t* preset) { return fluid_rampreset_get_name((fluid_rampreset_t*) preset->data); } /* RAM SoundFont loader get preset bank method */ static int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset) { return fluid_rampreset_get_banknum((fluid_rampreset_t*) preset->data); } /* RAM SoundFont loader get preset program method */ static int fluid_rampreset_preset_get_num(fluid_preset_t* preset) { return fluid_rampreset_get_num((fluid_rampreset_t*) preset->data); } /* RAM SoundFont loader preset noteon method */ static int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) { return fluid_rampreset_noteon((fluid_rampreset_t*) preset->data, synth, chan, key, vel); } /*************************************************************** * * SFONT */ static fluid_ramsfont_t * new_fluid_ramsfont (void) { fluid_ramsfont_t* sfont; sfont = FLUID_NEW(fluid_ramsfont_t); if (sfont == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } sfont->name[0] = 0; sfont->sample = NULL; sfont->preset = NULL; return sfont; } static int delete_fluid_ramsfont (fluid_ramsfont_t* sfont) { fluid_list_t *list; fluid_rampreset_t* preset; /* Check that no samples are currently used */ for (list = sfont->sample; list; list = fluid_list_next(list)) { fluid_sample_t* sam = (fluid_sample_t*) fluid_list_get(list); if (fluid_sample_refcount(sam) != 0) { return -1; } } for (list = sfont->sample; list; list = fluid_list_next(list)) { /* in ram soundfonts, the samples hold their data : so we should free it ourselves */ fluid_sample_t* sam = (fluid_sample_t*)fluid_list_get(list); delete_fluid_ramsample(sam); } if (sfont->sample) { delete_fluid_list(sfont->sample); } preset = sfont->preset; while (preset != NULL) { sfont->preset = preset->next; delete_fluid_rampreset(preset); preset = sfont->preset; } FLUID_FREE(sfont); return FLUID_OK; } static char * fluid_ramsfont_get_name(fluid_ramsfont_t* sfont) { return sfont->name; } /** * Set a RAM SoundFont name. * @param sfont RAM SoundFont * @param name Name to assign (should be 20 chars in length with a NULL terminator) * @return #FLUID_OK */ int fluid_ramsfont_set_name (fluid_ramsfont_t *sfont, const char *name) { FLUID_MEMCPY(sfont->name, name, 20); return FLUID_OK; } /* Add a preset to a RAM SoundFont */ static int fluid_ramsfont_add_preset (fluid_ramsfont_t* sfont, fluid_rampreset_t* preset) { fluid_rampreset_t *cur, *prev; if (sfont->preset == NULL) { preset->next = NULL; sfont->preset = preset; } else { /* sort them as we go along. very basic sorting trick. */ cur = sfont->preset; prev = NULL; while (cur != NULL) { if ((preset->bank < cur->bank) || ((preset->bank == cur->bank) && (preset->num < cur->num))) { if (prev == NULL) { preset->next = cur; sfont->preset = preset; } else { preset->next = cur; prev->next = preset; } return FLUID_OK; } prev = cur; cur = cur->next; } preset->next = NULL; prev->next = preset; } return FLUID_OK; } /** * Creates one instrument zone for the sample inside the preset defined by * \a bank and \a num * @param sfont RAM SoundFont * @param bank Preset bank number * @param num Preset program number * @param sample Sample to use for instrument zone * @param lokey Lower MIDI key range of zone (0-127, <= \a hikey) * @param hikey Upper MIDI key range of zone (0-127, >= \a lokey) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int lokey, int hikey) { /*- find or create a preset - add it the sample using the fluid_rampreset_add_sample fucntion - add the sample to the list of samples */ int err; fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); if (preset == NULL) { // Create it preset = new_fluid_rampreset(sfont); if (preset == NULL) { return FLUID_FAILED; } preset->bank = bank; preset->num = num; err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); if (err != FLUID_OK) { delete_fluid_rampreset(preset); return FLUID_FAILED; } // sort the preset fluid_ramsfont_add_preset(sfont, preset); } else { // just add it err = fluid_rampreset_add_sample(preset, sample, lokey, hikey); if (err != FLUID_OK) { return FLUID_FAILED; } } sfont->sample = fluid_list_append(sfont->sample, sample); return FLUID_OK; } /** * Removes the instrument zone corresponding to \a bank, \a num and \a sample * @param sfont RAM SoundFont * @param bank Preset bank number * @param num Preset program number * @param sample Sample of the preset zone * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_ramsfont_remove_izone (fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample) { int err; fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); if (preset == NULL) { return FLUID_FAILED; } // Fixed a crash bug : remove the sample from the sfont list after // removing the izone (aschmitt august 2005) err = fluid_rampreset_remove_izone(preset, sample); if (err != FLUID_OK) return err; // now we must remove the sample from sfont->sample sfont->sample = fluid_list_remove(sfont->sample, sample); return FLUID_OK; } /** * Sets a generator on an instrument zone identified by \a bank, \a num and \a sample * @param sfont RAM SoundFont * @param bank Preset bank number * @param num Preset program number * @param sample Sample of the instrument zone. * @param gen_type Generator ID (#fluid_gen_type) * @param value Generator value * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_ramsfont_izone_set_gen (fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int gen_type, float value) { fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); if (preset == NULL) { return FLUID_FAILED; } return fluid_rampreset_izone_set_gen(preset, sample, gen_type, value); } /** * Sets loop start/end values of the instrument zone identified by \a bank, * \a num and \a sample. * @param sfont RAM SoundFont * @param bank Preset bank number * @param num Preset program number * @param sample Sample of the instrument zone * @param on TRUE to enable looping, FALSE for one shot (\a loopstart and \a loopend * not used) * @param loopstart Loop start, in frames (counted from 0) * @param loopend Loop end, in frames (counted from last frame, thus is < 0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ int fluid_ramsfont_izone_set_loop (fluid_ramsfont_t *sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int on, float loopstart, float loopend) { fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num); if (preset == NULL) { return FLUID_FAILED; } return fluid_rampreset_izone_set_loop(preset, sample, on, loopstart, loopend); } /* Get a preset from a RAM SoundFont */ static fluid_rampreset_t * fluid_ramsfont_get_preset (fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num) { fluid_rampreset_t* preset = sfont->preset; while (preset != NULL) { if ((preset->bank == bank) && ((preset->num == num))) { return preset; } preset = preset->next; } return NULL; } /* Start preset iteration in a RAM SoundFont */ static void fluid_ramsfont_iteration_start (fluid_ramsfont_t* sfont) { sfont->iter_cur = sfont->preset; } /* Advance preset iteration in a RAM SoundFont */ static int fluid_ramsfont_iteration_next (fluid_ramsfont_t* sfont, fluid_preset_t* preset) { if (sfont->iter_cur == NULL) { return 0; } preset->data = (void*) sfont->iter_cur; sfont->iter_cur = fluid_rampreset_next(sfont->iter_cur); return 1; } /*************************************************************** * * PRESET */ typedef struct _fluid_rampreset_voice_t fluid_rampreset_voice_t; struct _fluid_rampreset_voice_t { fluid_voice_t *voice; unsigned int voiceID; }; /* Create a new RAM SoundFont preset */ static fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont) { fluid_rampreset_t* preset = FLUID_NEW(fluid_rampreset_t); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } preset->next = NULL; preset->sfont = sfont; preset->name[0] = 0; preset->bank = 0; preset->num = 0; preset->global_zone = NULL; preset->zone = NULL; preset->presetvoices = NULL; return preset; } /* Delete a RAM SoundFont preset */ static int delete_fluid_rampreset (fluid_rampreset_t* preset) { int err = FLUID_OK; fluid_preset_zone_t* zone; fluid_rampreset_voice_t *data; if (preset->global_zone != NULL) { if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) { err = FLUID_FAILED; } preset->global_zone = NULL; } zone = preset->zone; while (zone != NULL) { preset->zone = zone->next; if (delete_fluid_preset_zone(zone) != FLUID_OK) { err = FLUID_FAILED; } zone = preset->zone; } if (preset->presetvoices != NULL) { fluid_list_t *tmp = preset->presetvoices, *next; while (tmp) { data = (fluid_rampreset_voice_t *)(tmp->data); FLUID_FREE(data); next = tmp->next; FLUID_FREE(tmp); tmp = next; } } preset->presetvoices = NULL; FLUID_FREE(preset); return err; } /* Get a RAM SoundFont preset bank */ static int fluid_rampreset_get_banknum (fluid_rampreset_t* preset) { return preset->bank; } /* Get a RAM SoundFont preset program */ static int fluid_rampreset_get_num (fluid_rampreset_t* preset) { return preset->num; } /* Get a RAM SoundFont preset name */ static char * fluid_rampreset_get_name (fluid_rampreset_t* preset) { return preset->name; } /* Advance to next preset */ static fluid_rampreset_t * fluid_rampreset_next (fluid_rampreset_t* preset) { return preset->next; } /* Add a zone to a RAM SoundFont preset */ static int fluid_rampreset_add_zone(fluid_rampreset_t* preset, fluid_preset_zone_t* zone) { if (preset->zone == NULL) { zone->next = NULL; preset->zone = zone; } else { zone->next = preset->zone; preset->zone = zone; } return FLUID_OK; } /* Add a sample to a RAM SoundFont preset */ static int fluid_rampreset_add_sample (fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey) { /* create a new instrument zone, with the given sample */ /* one preset zone */ if (preset->zone == NULL) { fluid_preset_zone_t* zone; zone = new_fluid_preset_zone(""); if (zone == NULL) { return FLUID_FAILED; } /* its instrument */ zone->inst = (fluid_inst_t*) new_fluid_inst(); if (zone->inst == NULL) { delete_fluid_preset_zone(zone); return FLUID_FAILED; } fluid_rampreset_add_zone(preset, zone); } /* add an instrument zone for each sample */ { fluid_inst_t* inst = fluid_preset_zone_get_inst(preset->zone); fluid_inst_zone_t* izone = new_fluid_inst_zone(""); if (izone == NULL) { return FLUID_FAILED; } if (fluid_inst_add_zone(inst, izone) != FLUID_OK) { delete_fluid_inst_zone(izone); return FLUID_FAILED; } izone->sample = sample; izone->keylo = lokey; izone->keyhi = hikey; // give the preset the name of the sample FLUID_MEMCPY(preset->name, sample->name, 20); } return FLUID_OK; } /* Find an instrument zone with the given sample */ static fluid_inst_zone_t * fluid_rampreset_izoneforsample (fluid_rampreset_t* preset, fluid_sample_t* sample) { fluid_inst_t* inst; fluid_inst_zone_t* izone; if (preset->zone == NULL) return NULL; inst = fluid_preset_zone_get_inst(preset->zone); izone = inst->zone; while (izone) { if (izone->sample == sample) return izone; izone = izone->next; } return NULL; } /* Set loop of an instrument zone */ static int fluid_rampreset_izone_set_loop (fluid_rampreset_t* preset, fluid_sample_t* sample, int on, float loopstart, float loopend) { fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); short coarse, fine; if (izone == NULL) return FLUID_FAILED; if (!on) { izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; izone->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_UNLOOPED); return FLUID_OK; } /* NOTE : We should check that (sample->startloop + loopStart <= sample->endloop - loopend - 32) */ /* loopstart */ if (loopstart > 32767. || loopstart < -32767.) { coarse = (short)(loopstart/32768.); fine = (short)(loopstart - (float)(coarse)*32768.); } else { coarse = 0; fine = (short)loopstart; } izone->gen[GEN_STARTLOOPADDROFS].flags = GEN_SET; izone->gen[GEN_STARTLOOPADDROFS].val = fine; fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDROFS, fine); if (coarse) { izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_SET; izone->gen[GEN_STARTLOOPADDRCOARSEOFS].val = coarse; } else { izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_UNUSED; } fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDRCOARSEOFS, coarse); /* loopend */ if (loopend > 32767. || loopend < -32767.) { coarse = (short)(loopend/32768.); fine = (short)(loopend - (float)(coarse)*32768.); } else { coarse = 0; fine = (short)loopend; } izone->gen[GEN_ENDLOOPADDROFS].flags = GEN_SET; izone->gen[GEN_ENDLOOPADDROFS].val = fine; fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDROFS, fine); if (coarse) { izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_SET; izone->gen[GEN_ENDLOOPADDRCOARSEOFS].val = coarse; } else { izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_UNUSED; } fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDRCOARSEOFS, coarse); izone->gen[GEN_SAMPLEMODE].flags = GEN_SET; izone->gen[GEN_SAMPLEMODE].val = FLUID_LOOP_DURING_RELEASE; fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_LOOP_DURING_RELEASE); /* If the loop points are the whole samples, we are supposed to copy the frames around in the margins (the start to the end margin and the end to the start margin), but it works fine without this. Maybe some time it will be needed (see SAMPLE_LOOP_MARGIN) -- Antoie Schmitt May 2003 */ return FLUID_OK; } /* Set a generator on the instrument zone in preset having sample */ static int fluid_rampreset_izone_set_gen (fluid_rampreset_t* preset, fluid_sample_t* sample, int gen_type, float value) { fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample); if (izone == NULL) return FLUID_FAILED; izone->gen[gen_type].flags = GEN_SET; izone->gen[gen_type].val = value; fluid_rampreset_updatevoices(preset, gen_type, value); return FLUID_OK; } /* Remove the instrument zone from preset having sample */ static int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample) { fluid_inst_t* inst; fluid_inst_zone_t* izone, * prev; int found = 0; if (preset->zone == NULL) return FLUID_FAILED; inst = fluid_preset_zone_get_inst(preset->zone); izone = inst->zone; prev = NULL; while (izone && !found) { if (izone->sample == sample) { if (prev == NULL) { inst->zone = izone->next; } else { prev->next = izone->next; } izone->next = NULL; delete_fluid_inst_zone(izone); found = 1; } else { prev = izone; izone = izone->next; } } if (!found) return FLUID_FAILED; // stop all the voices that use this sample, so that // the sample can be cleared up { fluid_list_t *tmp = preset->presetvoices; while (tmp) { fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); fluid_voice_t *voice = presetvoice->voice; if (fluid_voice_is_playing(voice) && (fluid_voice_get_id(voice) == presetvoice->voiceID)) { // still belongs to the preset if (voice->sample == sample) { // uses this sample : turn it off. // our presetvoices struct will be cleaneup at the next update fluid_voice_off(voice); } } tmp = tmp->next; } } return FLUID_OK; } /* Stores the voice and the its ID in the preset for later update on gen_set */ static int fluid_rampreset_remembervoice (fluid_rampreset_t* preset, fluid_voice_t* voice) { fluid_rampreset_voice_t *presetvoice = FLUID_NEW(fluid_rampreset_voice_t); if (presetvoice == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } presetvoice->voice = voice; presetvoice->voiceID = fluid_voice_get_id(voice); preset->presetvoices = fluid_list_append(preset->presetvoices, (void *)presetvoice); if (preset->presetvoices == NULL) { FLUID_FREE(presetvoice); FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } return FLUID_OK; } /* Update a generator in realtime for a preset */ static void fluid_rampreset_updatevoices (fluid_rampreset_t* preset, int gen_type, float val) { fluid_list_t *tmp = preset->presetvoices, *prev = NULL, *next; /* Walk the presetvoice to update them if they are still active and ours. * If their ID has changed or their state is not playing, they are not * ours, so we forget them. */ while (tmp) { fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data); fluid_voice_t *voice = presetvoice->voice; if (!fluid_voice_is_playing(voice) || (fluid_voice_get_id(voice) != presetvoice->voiceID)) { /* forget it */ FLUID_FREE(presetvoice); /* unlink it */ next = tmp->next; FLUID_FREE(tmp); if (prev) { prev->next = next; } else { preset->presetvoices = next; } tmp = next; } else { /* update */ fluid_voice_gen_set(voice, gen_type, val); fluid_voice_update_param(voice, gen_type); /* next */ prev = tmp; tmp = tmp->next; } } } /* RAM SoundFont preset note on */ static int fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) { fluid_preset_zone_t *preset_zone; fluid_inst_t* inst; fluid_inst_zone_t *inst_zone, *global_inst_zone; fluid_sample_t* sample; fluid_voice_t* voice; fluid_mod_t * mod; fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ int mod_list_count; int i; /* run thru all the zones of this preset */ preset_zone = preset->zone; while (preset_zone != NULL) { /* check if the note falls into the key and velocity range of this preset */ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) { inst = fluid_preset_zone_get_inst(preset_zone); global_inst_zone = fluid_inst_get_global_zone(inst); /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); if ((sample == NULL) || fluid_sample_in_rom(sample)) { inst_zone = fluid_inst_zone_next(inst_zone); continue; } /* check if the note falls into the key and velocity range of this instrument */ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { /* this is a good zone. allocate a new synthesis process and initialize it */ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); if (voice == NULL) { return FLUID_FAILED; } if (fluid_rampreset_remembervoice(preset, voice) != FLUID_OK) { return FLUID_FAILED; } /* Instrument level, generators */ for (i = 0; i < GEN_LAST; i++) { /* SF 2.01 section 9.4 'bullet' 4: * * A generator in a local instrument zone supersedes a * global instrument zone generator. Both cases supersede * the default generator -> voice_gen_set */ if (inst_zone->gen[i].flags){ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); } else if (global_inst_zone != NULL && global_inst_zone->gen[i].flags){ fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); } else { /* The generator has not been defined in this instrument. * Do nothing, leave it at the default. */ }; }; /* for all generators */ /* global instrument zone, modulators: Put them all into a * list. */ mod_list_count = 0; if (global_inst_zone){ mod = global_inst_zone->mod; while (mod){ mod_list[mod_list_count++] = mod; mod = mod->next; }; }; /* local instrument zone, modulators. * Replace modulators with the same definition in the list: * SF 2.01 page 69, 'bullet' 8 */ mod = inst_zone->mod; while (mod){ /* 'Identical' modulators will be deleted by setting their * list entry to NULL. The list length is known, NULL * entries will be ignored later. SF2.01 section 9.5.1 * page 69, 'bullet' 3 defines 'identical'. */ for (i = 0; i < mod_list_count; i++){ if (fluid_mod_test_identity(mod,mod_list[i])){ mod_list[i] = NULL; }; }; /* Finally add the new modulator to to the list. */ mod_list[mod_list_count++] = mod; mod = mod->next; }; /* Add instrument modulators (global / local) to the voice. */ for (i = 0; i < mod_list_count; i++){ mod = mod_list[i]; if (mod != NULL){ /* disabled modulators CANNOT be skipped. */ /* Instrument modulators -supersede- existing (default) * modulators. SF 2.01 page 69, 'bullet' 6 */ fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); }; }; /* Preset level, generators */ for (i = 0; i < GEN_LAST; i++) { /* SF 2.01 section 8.5 page 58: If some generators are * encountered at preset level, they should be ignored */ if ((i != GEN_STARTADDROFS) && (i != GEN_ENDADDROFS) && (i != GEN_STARTLOOPADDROFS) && (i != GEN_ENDLOOPADDROFS) && (i != GEN_STARTADDRCOARSEOFS) && (i != GEN_ENDADDRCOARSEOFS) && (i != GEN_STARTLOOPADDRCOARSEOFS) && (i != GEN_KEYNUM) && (i != GEN_VELOCITY) && (i != GEN_ENDLOOPADDRCOARSEOFS) && (i != GEN_SAMPLEMODE) && (i != GEN_EXCLUSIVECLASS) && (i != GEN_OVERRIDEROOTKEY)) { /* SF 2.01 section 9.4 'bullet' 9: A generator in a * local preset zone supersedes a global preset zone * generator. The effect is -added- to the destination * summing node -> voice_gen_incr */ if (preset_zone->gen[i].flags){ fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); } else { /* The generator has not been defined in this preset * Do nothing, leave it unchanged. */ }; }; /* if available at preset level */ }; /* for all generators */ /* Global preset zone, modulators: put them all into a * list. */ mod_list_count = 0; /* Process the modulators of the local preset zone. Kick * out all identical modulators from the global preset zone * (SF 2.01 page 69, second-last bullet) */ mod = preset_zone->mod; while (mod){ for (i = 0; i < mod_list_count; i++){ if (fluid_mod_test_identity(mod,mod_list[i])){ mod_list[i] = NULL; }; }; /* Finally add the new modulator to the list. */ mod_list[mod_list_count++] = mod; mod = mod->next; }; /* Add preset modulators (global / local) to the voice. */ for (i = 0; i < mod_list_count; i++){ mod = mod_list[i]; if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */ /* Preset modulators -add- to existing instrument / * default modulators. SF2.01 page 70 first bullet on * page */ fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); }; }; /* add the synthesis process to the synthesis loop. */ fluid_synth_start_voice(synth, voice); /* Store the ID of the first voice that was created by this noteon event. * Exclusive class may only terminate older voices. * That avoids killing voices, which have just been created. * (a noteon event can create several voice processes with the same exclusive * class - for example when using stereo samples) */ } inst_zone = fluid_inst_zone_next(inst_zone); } } preset_zone = fluid_preset_zone_next(preset_zone); } return FLUID_OK; } /*************************************************************** * * SAMPLE */ /** * Set the name of a RAM SoundFont sample. * @param sample RAM SoundFont sample * @param name Name to assign to sample (20 chars in length, 0 terminated) * @return #FLUID_OK */ int fluid_sample_set_name(fluid_sample_t* sample, const char *name) { FLUID_MEMCPY(sample->name, name, 20); return FLUID_OK; } /** * Assign sample data to a RAM SoundFont sample. * @param sample RAM SoundFont sample * @param data Buffer containing 16 bit audio sample data * @param nbframes Number of samples in \a data * @param copy_data TRUE to copy the data, FALSE to use it directly * @param rootkey Root MIDI note of sample (0-127) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * WARNING: If \a copy_data is FALSE, data should have 8 unused frames at start * and 8 unused frames at the end. */ int fluid_sample_set_sound_data (fluid_sample_t* sample, short *data, unsigned int nbframes, short copy_data, int rootkey) { /* 16 bit mono 44.1KHz data in */ /* in all cases, the sample has ownership of the data : it will release it in the end */ unsigned int storedNbFrames; /* in case we already have some data */ if (sample->data != NULL) { FLUID_FREE(sample->data); } if (copy_data) { /* nbframes should be >= 48 (SoundFont specs) */ storedNbFrames = nbframes; if (storedNbFrames < 48) storedNbFrames = 48; sample->data = FLUID_MALLOC(storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); if (sample->data == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } FLUID_MEMSET(sample->data, 0, storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN, data, nbframes*2); #if 0 /* this would do the fill of the margins */ FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN + storedNbFrames*2, (char*)data, 2*SAMPLE_LOOP_MARGIN); FLUID_MEMCPY((char*)(sample->data), (char*)data + nbframes*2 - 2*SAMPLE_LOOP_MARGIN, 2*SAMPLE_LOOP_MARGIN); #endif /* pointers */ /* all from the start of data */ sample->start = SAMPLE_LOOP_MARGIN; sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames; } else { /* we cannot assure the SAMPLE_LOOP_MARGIN */ sample->data = data; sample->start = 0; sample->end = nbframes; } /* only used as markers for the LOOP generators : set them on the first real frame */ sample->loopstart = sample->start; sample->loopend = sample->end; sample->samplerate = 44100; sample->origpitch = rootkey; sample->pitchadj = 0; sample->sampletype = FLUID_SAMPLETYPE_MONO; sample->valid = 1; return FLUID_OK; } /** * Create new RAM SoundFont sample. * @return New RAM SoundFont sample or NULL if out of memory */ fluid_sample_t * new_fluid_ramsample (void) { /* same as new_fluid_sample. Only here so that it is exported */ fluid_sample_t* sample = NULL; sample = FLUID_NEW(fluid_sample_t); if (sample == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } memset(sample, 0, sizeof(fluid_sample_t)); return sample; } /** * Delete a RAM SoundFont sample. * @param sample Sample to delete * @return #FLUID_OK */ int delete_fluid_ramsample (fluid_sample_t* sample) { /* same as delete_fluid_sample, plus frees the data */ if (sample->data != NULL) { FLUID_FREE(sample->data); } sample->data = NULL; FLUID_FREE(sample); return FLUID_OK; } fluidsynth-1.1.9/src/sfloader/fluid_ramsfont.h000066400000000000000000000040711322272076000214710ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RAMSFONT_H #define _FLUID_RAMSFONT_H #include "fluidsynth.h" #include "fluidsynth_priv.h" #include "fluid_defsfont.h" #ifdef __cplusplus extern "C" { #endif /* * fluid_ramsfont_t */ struct _fluid_ramsfont_t { char name[21]; /* the name of the soundfont */ fluid_list_t* sample; /* the samples in this soundfont */ fluid_rampreset_t* preset; /* the presets of this soundfont */ fluid_preset_t iter_preset; /* preset interface used in the iteration */ fluid_rampreset_t* iter_cur; /* the current preset in the iteration */ }; /* * fluid_preset_t */ struct _fluid_rampreset_t { fluid_rampreset_t* next; fluid_ramsfont_t* sfont; /* the soundfont this preset belongs to */ char name[21]; /* the name of the preset */ unsigned int bank; /* the bank number */ unsigned int num; /* the preset number */ fluid_preset_zone_t* global_zone; /* the global zone of the preset */ fluid_preset_zone_t* zone; /* the chained list of preset zones */ fluid_list_t *presetvoices; /* chained list of used voices */ }; #ifdef __cplusplus } #endif #endif /* _FLUID_SFONT_H */ fluidsynth-1.1.9/src/sfloader/fluid_sfont.h000066400000000000000000000051571322272076000207770ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _PRIV_FLUID_SFONT_H #define _PRIV_FLUID_SFONT_H /* * Utility macros to access soundfonts, presets, and samples */ #define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); } #define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) #define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) #define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf) #define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum) #define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf) #define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr) #define fluid_sfont_get_data(_sf) (_sf)->data #define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); } #define delete_fluid_preset(_preset) \ { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} #define fluid_preset_get_data(_preset) (_preset)->data #define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); } #define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset) #define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset) #define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset) #define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) #define fluid_preset_notify(_preset,_reason,_chan) \ { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }} #define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } #define fluid_sample_decr_ref(_sample) \ (_sample)->refcount--; \ if (((_sample)->refcount == 0) && ((_sample)->notify)) \ (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE); #endif /* _PRIV_FLUID_SFONT_H */ fluidsynth-1.1.9/src/synth/000077500000000000000000000000001322272076000156505ustar00rootroot00000000000000fluidsynth-1.1.9/src/synth/fluid_chan.c000066400000000000000000000204511322272076000201120ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_chan.h" #include "fluid_mod.h" #include "fluid_synth.h" #include "fluid_sfont.h" /* Field shift amounts for sfont_bank_prog bit field integer */ #define PROG_SHIFTVAL 0 #define BANK_SHIFTVAL 8 #define SFONT_SHIFTVAL 22 /* Field mask values for sfont_bank_prog bit field integer */ #define PROG_MASKVAL 0x000000FF /* Bit 7 is used to indicate unset state */ #define BANK_MASKVAL 0x003FFF00 #define BANKLSB_MASKVAL 0x00007F00 #define BANKMSB_MASKVAL 0x003F8000 #define SFONT_MASKVAL 0xFFC00000 static void fluid_channel_init(fluid_channel_t* chan); fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num) { fluid_channel_t* chan; chan = FLUID_NEW(fluid_channel_t); if (chan == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } chan->synth = synth; chan->channum = num; chan->preset = NULL; chan->tuning = NULL; fluid_channel_init(chan); fluid_channel_init_ctrl(chan, 0); return chan; } static void fluid_channel_init(fluid_channel_t* chan) { fluid_preset_t *newpreset; int prognum, banknum; chan->sostenuto_orderid = 0; chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; prognum = 0; banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL | prognum << PROG_SHIFTVAL; newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); fluid_channel_set_preset(chan, newpreset); chan->interp_method = FLUID_INTERP_DEFAULT; chan->tuning_bank = 0; chan->tuning_prog = 0; chan->nrpn_select = 0; chan->nrpn_active = 0; if (chan->tuning) { fluid_tuning_unref (chan->tuning, 1); chan->tuning = NULL; } } /* @param is_all_ctrl_off if nonzero, only resets some controllers, according to http://www.midi.org/techspecs/rp15.php */ void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) { int i; chan->key_pressure = 0; chan->channel_pressure = 0; chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ for (i = 0; i < GEN_LAST; i++) { chan->gen[i] = 0.0f; chan->gen_abs[i] = 0; } if (is_all_ctrl_off) { for (i = 0; i < ALL_SOUND_OFF; i++) { if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) { continue; } if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) { continue; } if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) { continue; } fluid_channel_set_cc (chan, i, 0); } } else { for (i = 0; i < 128; i++) { fluid_channel_set_cc (chan, i, 0); } } /* Set RPN controllers to NULL state */ fluid_channel_set_cc (chan, RPN_LSB, 127); fluid_channel_set_cc (chan, RPN_MSB, 127); /* Set NRPN controllers to NULL state */ fluid_channel_set_cc (chan, NRPN_LSB, 127); fluid_channel_set_cc (chan, NRPN_MSB, 127); /* Expression (MSB & LSB) */ fluid_channel_set_cc (chan, EXPRESSION_MSB, 127); fluid_channel_set_cc (chan, EXPRESSION_LSB, 127); if (!is_all_ctrl_off) { chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ /* Just like panning, a value of 64 indicates no change for sound ctrls */ for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) { fluid_channel_set_cc (chan, i, 64); } /* Volume / initial attenuation (MSB & LSB) */ fluid_channel_set_cc (chan, VOLUME_MSB, 100); fluid_channel_set_cc (chan, VOLUME_LSB, 0); /* Pan (MSB & LSB) */ fluid_channel_set_cc (chan, PAN_MSB, 64); fluid_channel_set_cc (chan, PAN_LSB, 0); /* Reverb */ /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ /* Note: although XG standard specifies the default amount of reverb to be 40, most people preferred having it at zero. See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ } } /* Only called by delete_fluid_synth(), so no need to queue a preset free event */ int delete_fluid_channel(fluid_channel_t* chan) { if (chan->preset) delete_fluid_preset (chan->preset); FLUID_FREE(chan); return FLUID_OK; } /* FIXME - Calls fluid_channel_init() potentially in synthesis context */ void fluid_channel_reset(fluid_channel_t* chan) { fluid_channel_init(chan); fluid_channel_init_ctrl(chan, 0); } /* Should only be called from synthesis context */ int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) { fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); if (chan->preset) { fluid_sfont_t *sfont; sfont = chan->preset->sfont; delete_fluid_preset (chan->preset); fluid_synth_sfont_unref (chan->synth, sfont); /* -- unref preset's SoundFont */ } chan->preset = preset; fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum); return FLUID_OK; } /* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */ void fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfontnum, int banknum, int prognum) { int oldval, newval, oldmask; newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) | ((banknum != -1) ? 0 : BANK_MASKVAL) | ((prognum != -1) ? 0 : PROG_MASKVAL); oldval = chan->sfont_bank_prog; newval = (newval & ~oldmask) | (oldval & oldmask); chan->sfont_bank_prog = newval; } /* Set bank LSB 7 bits */ void fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb) { int oldval, newval, style; style = chan->synth->bank_select; if (style == FLUID_BANK_STYLE_GM || style == FLUID_BANK_STYLE_GS) return; /* ignored */ oldval = chan->sfont_bank_prog; if (style == FLUID_BANK_STYLE_XG) newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL); else /* style == FLUID_BANK_STYLE_MMA */ newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); chan->sfont_bank_prog = newval; } /* Set bank MSB 7 bits */ void fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb) { int oldval, newval, style; style = chan->synth->bank_select; if (style == FLUID_BANK_STYLE_XG) { /* XG bank, do drum-channel auto-switch */ /* The number "120" was based on several keyboards having drums at 120 - 127, reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; return; } if (style == FLUID_BANK_STYLE_GM || chan->channel_type == CHANNEL_TYPE_DRUM) return; /* ignored */ oldval = chan->sfont_bank_prog; if (style == FLUID_BANK_STYLE_GS) newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL); else /* style == FLUID_BANK_STYLE_MMA */ newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); chan->sfont_bank_prog = newval; } /* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */ void fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, int *bank, int *prog) { int sfont_bank_prog; sfont_bank_prog = chan->sfont_bank_prog; if (sfont) *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; } fluidsynth-1.1.9/src/synth/fluid_chan.h000066400000000000000000000151061322272076000201200ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_CHAN_H #define _FLUID_CHAN_H #include "fluidsynth_priv.h" #include "fluid_midi.h" #include "fluid_tuning.h" /* * fluid_channel_t * * Mutual exclusion notes (as of 1.1.2): * None - everything should have been synchronized by the synth. */ struct _fluid_channel_t { fluid_mutex_t mutex; /* Lock for thread sensitive parameters */ fluid_synth_t* synth; /**< Parent synthesizer instance */ int channum; /**< MIDI channel number */ int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ fluid_preset_t* preset; /**< Selected preset */ int key_pressure; /**< MIDI key pressure */ int channel_pressure; /**< MIDI channel pressure */ int pitch_bend; /**< Current pitch bend value */ int pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ int cc[128]; /**< MIDI controller values */ /* Sostenuto order id gives the order of SostenutoOn event. This value is useful to known when the sostenuto pedal is depressed (before or after a key note). We need to compare SostenutoOrderId with voice id. */ unsigned int sostenuto_orderid; int interp_method; /**< Interpolation method (enum fluid_interp) */ fluid_tuning_t* tuning; /**< Micro tuning */ int tuning_bank; /**< Current tuning bank number */ int tuning_prog; /**< Current tuning program number */ /* NRPN system */ int nrpn_select; /* Generator ID of SoundFont NRPN message */ int nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ /* The values of the generators, set by NRPN messages, or by * fluid_synth_set_gen(), are cached in the channel so they can be * applied to future notes. They are copied to a voice's generators * in fluid_voice_init(), which calls fluid_gen_init(). */ fluid_real_t gen[GEN_LAST]; /* By default, the NRPN values are relative to the values of the * generators set in the SoundFont. For example, if the NRPN * specifies an attack of 100 msec then 100 msec will be added to the * combined attack time of the sound font and the modulators. * * However, it is useful to be able to specify the generator value * absolutely, completely ignoring the generators of the SoundFont * and the values of modulators. The gen_abs field, is a boolean * flag indicating whether the NRPN value is absolute or not. */ char gen_abs[GEN_LAST]; /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ int channel_type; }; fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off); int delete_fluid_channel(fluid_channel_t* chan); void fluid_channel_reset(fluid_channel_t* chan); int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset); fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan); void fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfont, int bank, int prog); void fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb); void fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb); void fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, int *bank, int *prog); int fluid_channel_get_num(fluid_channel_t* chan); void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method); int fluid_channel_get_interp_method(fluid_channel_t* chan); #define fluid_channel_get_preset(chan) ((chan)->preset) #define fluid_channel_set_cc(chan, num, val) \ ((chan)->cc[num] = (val)) #define fluid_channel_get_cc(chan, num) \ ((chan)->cc[num]) #define fluid_channel_get_key_pressure(chan) \ ((chan)->key_pressure) #define fluid_channel_set_key_pressure(chan, val) \ ((chan)->key_pressure = (val)) #define fluid_channel_get_channel_pressure(chan) \ ((chan)->channel_pressure) #define fluid_channel_set_channel_pressure(chan, val) \ ((chan)->channel_pressure = (val)) #define fluid_channel_get_pitch_bend(chan) \ ((chan)->pitch_bend) #define fluid_channel_set_pitch_bend(chan, val) \ ((chan)->pitch_bend = (val)) #define fluid_channel_get_pitch_wheel_sensitivity(chan) \ ((chan)->pitch_wheel_sensitivity) #define fluid_channel_set_pitch_wheel_sensitivity(chan, val) \ ((chan)->pitch_wheel_sensitivity = (val)) #define fluid_channel_get_num(chan) ((chan)->channum) #define fluid_channel_set_interp_method(chan, new_method) \ ((chan)->interp_method = (new_method)) #define fluid_channel_get_interp_method(chan) \ ((chan)->interp_method); #define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } #define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) #define fluid_channel_get_tuning(_c) ((_c)->tuning) #define fluid_channel_get_tuning_bank(chan) \ ((chan)->tuning_bank) #define fluid_channel_set_tuning_bank(chan, bank) \ ((chan)->tuning_bank = (bank)) #define fluid_channel_get_tuning_prog(chan) \ ((chan)->tuning_prog) #define fluid_channel_set_tuning_prog(chan, prog) \ ((chan)->tuning_prog = (prog)) #define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) #define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64) #define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } #define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) #define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n]) #define fluid_channel_get_min_note_length_ticks(chan) \ ((chan)->synth->min_note_length_ticks) #endif /* _FLUID_CHAN_H */ fluidsynth-1.1.9/src/synth/fluid_event.c000066400000000000000000000431051322272076000203230ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* 2002 : API design by Peter Hanappe and Antoine Schmitt August 2002 : Implementation by Antoine Schmitt as@gratin.org as part of the infiniteCD author project http://www.infiniteCD.org/ Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free. */ #include "fluid_event_priv.h" #include "fluidsynth_priv.h" /*************************************************************** * * SEQUENCER EVENTS */ /* Event alloc/free */ void fluid_event_clear(fluid_event_t* evt) { FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); // by default, no type evt->dest = -1; evt->src = -1; evt->type = -1; } /** * Create a new sequencer event structure. * @return New sequencer event structure or NULL if out of memory */ fluid_event_t* new_fluid_event() { fluid_event_t* evt; evt = FLUID_NEW(fluid_event_t); if (evt == NULL) { fluid_log(FLUID_PANIC, "event: Out of memory\n"); return NULL; } fluid_event_clear(evt); return(evt); } /** * Delete a sequencer event structure. * @param evt Sequencer event structure created by new_fluid_event(). */ void delete_fluid_event(fluid_event_t* evt) { if (evt == NULL) { return; } FLUID_FREE(evt); } /** * Set the time field of a sequencer event. * @internal * @param evt Sequencer event structure * @param time Time value to assign */ void fluid_event_set_time(fluid_event_t* evt, unsigned int time) { evt->time = time; } /** * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set. * @param evt Sequencer event structure * @param src Unique sequencer ID */ void fluid_event_set_source(fluid_event_t* evt, fluid_seq_id_t src) { evt->src = src; } /** * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID. * @param evt Sequencer event structure * @param dest The destination unique sequencer ID */ void fluid_event_set_dest(fluid_event_t* evt, fluid_seq_id_t dest) { evt->dest = dest; } /** * Set a sequencer event to be a timer event. * @param evt Sequencer event structure * @param data User supplied data pointer */ void fluid_event_timer(fluid_event_t* evt, void* data) { evt->type = FLUID_SEQ_TIMER; evt->data = data; } /** * Set a sequencer event to be a note on event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param vel MIDI velocity value (0-127) */ void fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel) { evt->type = FLUID_SEQ_NOTEON; evt->channel = channel; evt->key = key; evt->vel = vel; } /** * Set a sequencer event to be a note off event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) */ void fluid_event_noteoff(fluid_event_t* evt, int channel, short key) { evt->type = FLUID_SEQ_NOTEOFF; evt->channel = channel; evt->key = key; } /** * Set a sequencer event to be a note duration event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param vel MIDI velocity value (0-127) * @param duration Duration of note in the time scale used by the sequencer (by default milliseconds) */ void fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned int duration) { evt->type = FLUID_SEQ_NOTE; evt->channel = channel; evt->key = key; evt->vel = vel; evt->duration = duration; } /** * Set a sequencer event to be an all sounds off event. * @param evt Sequencer event structure * @param channel MIDI channel number */ void fluid_event_all_sounds_off(fluid_event_t* evt, int channel) { evt->type = FLUID_SEQ_ALLSOUNDSOFF; evt->channel = channel; } /** * Set a sequencer event to be a all notes off event. * @param evt Sequencer event structure * @param channel MIDI channel number */ void fluid_event_all_notes_off(fluid_event_t* evt, int channel) { evt->type = FLUID_SEQ_ALLNOTESOFF; evt->channel = channel; } /** * Set a sequencer event to be a bank select event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param bank_num MIDI bank number (0-16383) */ void fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num) { evt->type = FLUID_SEQ_BANKSELECT; evt->channel = channel; evt->control = bank_num; } /** * Set a sequencer event to be a program change event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val MIDI program number (0-127) */ void fluid_event_program_change(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_PROGRAMCHANGE; evt->channel = channel; evt->value = val; } /** * Set a sequencer event to be a program select event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param sfont_id SoundFont ID number * @param bank_num MIDI bank number (0-16383) * @param preset_num MIDI preset number (0-127) */ void fluid_event_program_select(fluid_event_t* evt, int channel, unsigned int sfont_id, short bank_num, short preset_num) { evt->type = FLUID_SEQ_PROGRAMSELECT; evt->channel = channel; evt->duration = sfont_id; evt->value = preset_num; evt->control = bank_num; } /** * Set a sequencer event to be an any control change event (for internal use). * @param evt Sequencer event structure * @param channel MIDI channel number */ void fluid_event_any_control_change(fluid_event_t* evt, int channel) { evt->type = FLUID_SEQ_ANYCONTROLCHANGE; evt->channel = channel; } /** * Set a sequencer event to be a pitch bend event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend) */ void fluid_event_pitch_bend(fluid_event_t* evt, int channel, int pitch) { evt->type = FLUID_SEQ_PITCHBEND; evt->channel = channel; if (pitch < 0) pitch = 0; if (pitch > 16383) pitch = 16383; evt->pitch = pitch; } /** * Set a sequencer event to be a pitch wheel sensitivity event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param value MIDI pitch wheel sensitivity value in semitones */ void fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value) { evt->type = FLUID_SEQ_PITCHWHHELSENS; evt->channel = channel; evt->value = value; } /** * Set a sequencer event to be a modulation event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val MIDI modulation value (0-127) */ void fluid_event_modulation(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_MODULATION; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a MIDI sustain event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val MIDI sustain value (0-127) */ void fluid_event_sustain(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_SUSTAIN; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a MIDI control change event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param control MIDI control number (0-127) * @param val MIDI control value (0-127) */ void fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val) { evt->type = FLUID_SEQ_CONTROLCHANGE; evt->channel = channel; evt->control = control; evt->value = val; } /** * Set a sequencer event to be a stereo pan event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right) */ void fluid_event_pan(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_PAN; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a volume event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val Volume value (0-127) */ void fluid_event_volume(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_VOLUME; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a reverb send event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val Reverb amount (0-127) */ void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_REVERBSEND; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a chorus send event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val Chorus amount (0-127) */ void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_CHORUSSEND; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be an unregistering event. * @param evt Sequencer event structure * @since 1.1.0 */ void fluid_event_unregistering(fluid_event_t* evt) { evt->type = FLUID_SEQ_UNREGISTERING; } /** * Set a sequencer event to be a channel-wide aftertouch event. * @param evt Sequencer event structure * @param channel MIDI channel number * @param val Aftertouch amount (0-127) * @since 1.1.0 */ void fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val) { evt->type = FLUID_SEQ_CHANNELPRESSURE; evt->channel = channel; if (val < 0) val = 0; if (val > 127) val = 127; evt->value = val; } /** * Set a sequencer event to be a midi system reset event. * @param evt Sequencer event structure * @since 1.1.0 */ void fluid_event_system_reset(fluid_event_t* evt) { evt->type = FLUID_SEQ_SYSTEMRESET; } /* * Accessing event data */ /** * Get the event type (#fluid_seq_event_type) field from a sequencer event structure. * @param evt Sequencer event structure * @return Event type (#fluid_seq_event_type). */ int fluid_event_get_type(fluid_event_t* evt) { return evt->type; } /** * @internal * Get the time field from a sequencer event structure. * @param evt Sequencer event structure * @return Time value */ unsigned int fluid_event_get_time(fluid_event_t* evt) { return evt->time; } /** * Get the source sequencer client from a sequencer event structure. * @param evt Sequencer event structure * @return source field of the sequencer event */ fluid_seq_id_t fluid_event_get_source(fluid_event_t* evt) { return evt->src; } /** * Get the dest sequencer client from a sequencer event structure. * @param evt Sequencer event structure * @return dest field of the sequencer event */ fluid_seq_id_t fluid_event_get_dest(fluid_event_t* evt) { return evt->dest; } /** * Get the MIDI channel field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI zero-based channel number */ int fluid_event_get_channel(fluid_event_t* evt) { return evt->channel; } /** * Get the MIDI note field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI note number (0-127) */ short fluid_event_get_key(fluid_event_t* evt) { return evt->key; } /** * Get the MIDI velocity field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI velocity value (0-127) */ short fluid_event_get_velocity(fluid_event_t* evt) { return evt->vel; } /** * Get the MIDI control number field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI control number (0-127) */ short fluid_event_get_control(fluid_event_t* evt) { return evt->control; } /** * Get the value field from a sequencer event structure. * @param evt Sequencer event structure * @return Value field of event. * * The Value field is used by the following event types: * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num), * #FLUID_SEQ_PITCHWHHELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. */ short fluid_event_get_value(fluid_event_t* evt) { return evt->value; } /** * Get the data field from a sequencer event structure. * @param evt Sequencer event structure * @return Data field of event. * * Used by the #FLUID_SEQ_TIMER event type. */ void* fluid_event_get_data(fluid_event_t* evt) { return evt->data; } /** * Get the duration field from a sequencer event structure. * @param evt Sequencer event structure * @return Note duration value in the time scale used by the sequencer (by default milliseconds) * * Used by the #FLUID_SEQ_NOTE event type. */ unsigned int fluid_event_get_duration(fluid_event_t* evt) { return evt->duration; } /** * Get the MIDI bank field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI bank number (0-16383) * * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT * event types. */ short fluid_event_get_bank(fluid_event_t* evt) { return evt->control; } /** * Get the pitch field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI pitch bend pitch value (0-16383, 8192 = no bend) * * Used by the #FLUID_SEQ_PITCHBEND event type. */ int fluid_event_get_pitch(fluid_event_t* evt) { return evt->pitch; } /** * Get the MIDI program field from a sequencer event structure. * @param evt Sequencer event structure * @return MIDI program number (0-127) * * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT * event types. */ short fluid_event_get_program(fluid_event_t* evt) { return evt->value; } /** * Get the SoundFont ID field from a sequencer event structure. * @param evt Sequencer event structure * @return SoundFont identifier value. * * Used by the #FLUID_SEQ_PROGRAMSELECT event type. */ unsigned int fluid_event_get_sfont_id(fluid_event_t* evt) { return evt->duration; } /********************/ /* heap management */ /********************/ fluid_evt_heap_t* _fluid_evt_heap_init(int nbEvents) { #ifdef HEAP_WITH_DYNALLOC int i; fluid_evt_heap_t* heap; fluid_evt_entry *tmp; heap = FLUID_NEW(fluid_evt_heap_t); if (heap == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } heap->freelist = NULL; fluid_mutex_init(heap->mutex); /* LOCK */ fluid_mutex_lock(heap->mutex); /* Allocate the event entries */ for (i = 0; i < nbEvents; i++) { tmp = FLUID_NEW(fluid_evt_entry); tmp->next = heap->freelist; heap->freelist = tmp; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); #else int i; fluid_evt_heap_t* heap; int siz = 2*sizeof(fluid_evt_entry *) + sizeof(fluid_evt_entry)*nbEvents; heap = (fluid_evt_heap_t *)FLUID_MALLOC(siz); if (heap == NULL) { fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } FLUID_MEMSET(heap, 0, siz); /* link all heap events */ { fluid_evt_entry *tmp = &(heap->pool); for (i = 0 ; i < nbEvents - 1 ; i++) tmp[i].next = &(tmp[i+1]); tmp[nbEvents-1].next = NULL; /* set head & tail */ heap->tail = &(tmp[nbEvents-1]); heap->head = &(heap->pool); } #endif return (heap); } void _fluid_evt_heap_free(fluid_evt_heap_t* heap) { #ifdef HEAP_WITH_DYNALLOC fluid_evt_entry *tmp, *next; /* LOCK */ fluid_mutex_lock(heap->mutex); tmp = heap->freelist; while (tmp) { next = tmp->next; FLUID_FREE(tmp); tmp = next; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); fluid_mutex_destroy(heap->mutex); FLUID_FREE(heap); #else FLUID_FREE(heap); #endif } fluid_evt_entry* _fluid_seq_heap_get_free(fluid_evt_heap_t* heap) { #ifdef HEAP_WITH_DYNALLOC fluid_evt_entry* evt = NULL; /* LOCK */ fluid_mutex_lock(heap->mutex); #if !defined(MACOS9) if (heap->freelist == NULL) { heap->freelist = FLUID_NEW(fluid_evt_entry); if (heap->freelist != NULL) { heap->freelist->next = NULL; } } #endif evt = heap->freelist; if (evt != NULL) { heap->freelist = heap->freelist->next; evt->next = NULL; } /* UNLOCK */ fluid_mutex_unlock(heap->mutex); return evt; #else fluid_evt_entry* evt; if (heap->head == NULL) return NULL; /* take from head of the heap */ /* critical - should threadlock ? */ evt = heap->head; heap->head = heap->head->next; return evt; #endif } void _fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt) { #ifdef HEAP_WITH_DYNALLOC /* LOCK */ fluid_mutex_lock(heap->mutex); evt->next = heap->freelist; heap->freelist = evt; /* UNLOCK */ fluid_mutex_unlock(heap->mutex); #else /* append to the end of the heap */ /* critical - should threadlock ? */ heap->tail->next = evt; heap->tail = evt; evt->next = NULL; #endif } fluidsynth-1.1.9/src/synth/fluid_event_priv.h000066400000000000000000000043331322272076000213700ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_EVENT_PRIV_H #define _FLUID_EVENT_PRIV_H #include "fluidsynth.h" #include "fluid_sys.h" /* Private data for event */ /* ?? should be optimized in size, using unions */ struct _fluid_event_t { unsigned int time; int type; fluid_seq_id_t src; fluid_seq_id_t dest; int channel; short key; short vel; short control; short value; short id; //?? unused ? int pitch; unsigned int duration; void* data; }; unsigned int fluid_event_get_time(fluid_event_t* evt); void fluid_event_set_time(fluid_event_t* evt, unsigned int time); void fluid_event_clear(fluid_event_t* evt); /* private data for sorter + heap */ enum fluid_evt_entry_type { FLUID_EVT_ENTRY_INSERT = 0, FLUID_EVT_ENTRY_REMOVE }; typedef struct _fluid_evt_entry fluid_evt_entry; struct _fluid_evt_entry { fluid_evt_entry *next; short entryType; fluid_event_t evt; }; #define HEAP_WITH_DYNALLOC 1 /* #undef HEAP_WITH_DYNALLOC */ typedef struct _fluid_evt_heap_t { #ifdef HEAP_WITH_DYNALLOC fluid_evt_entry* freelist; fluid_mutex_t mutex; #else fluid_evt_entry* head; fluid_evt_entry* tail; fluid_evt_entry pool; #endif } fluid_evt_heap_t; fluid_evt_heap_t* _fluid_evt_heap_init(int nbEvents); void _fluid_evt_heap_free(fluid_evt_heap_t* heap); fluid_evt_entry* _fluid_seq_heap_get_free(fluid_evt_heap_t* heap); void _fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt); #endif /* _FLUID_EVENT_PRIV_H */ fluidsynth-1.1.9/src/synth/fluid_gen.c000066400000000000000000000165431322272076000177610ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_gen.h" #include "fluid_chan.h" /* See SFSpec21 $8.1.3 */ fluid_gen_info_t fluid_gen_info[] = { /* number/name init scale min max def */ { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f }, { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f }, { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f }, { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }, { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f }, { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f }, { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f }, { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f }, { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f }, { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f }, { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f }, { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f }, { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f }, { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f }, { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f }, { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f }, { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f }, { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f } }; /** * Set an array of generators to their default values. * @param gen Array of generators (should be #GEN_LAST in size). * @return Always returns #FLUID_OK * @deprecated Will be removed from the public API in a future release, because there is no current usecase for it. */ int fluid_gen_set_default_values(fluid_gen_t* gen) { int i; for (i = 0; i < GEN_LAST; i++) { gen[i].flags = GEN_UNUSED; gen[i].mod = 0.0; gen[i].nrpn = 0.0; gen[i].val = fluid_gen_info[i].def; } return FLUID_OK; } /* fluid_gen_init * * Set an array of generators to their initial value */ int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel) { int i; fluid_gen_set_default_values(gen); for (i = 0; i < GEN_LAST; i++) { gen[i].nrpn = fluid_channel_get_gen(channel, i); /* This is an extension to the SoundFont standard. More * documentation is available at the fluid_synth_set_gen2() * function. */ if (fluid_channel_get_gen_abs(channel, i)) { gen[i].flags = GEN_ABS_NRPN; } } return FLUID_OK; } fluid_real_t fluid_gen_scale(int gen, float value) { return (fluid_gen_info[gen].min + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); } 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; } fluidsynth-1.1.9/src/synth/fluid_gen.h000066400000000000000000000032121322272076000177530ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_GEN_H #define _FLUID_GEN_H #include "fluidsynth_priv.h" typedef struct _fluid_gen_info_t { char num; /* Generator number */ char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ float min; /* The minimum value */ float max; /* The maximum value */ float def; /* The default value (cfr. fluid_gen_set_default_values()) */ } fluid_gen_info_t; #define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } #define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } fluid_real_t fluid_gen_scale(int gen, float value); fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel); #endif /* _FLUID_GEN_H */ fluidsynth-1.1.9/src/synth/fluid_mod.c000066400000000000000000000334401322272076000177620ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_mod.h" #include "fluid_chan.h" #include "fluid_voice.h" /* * fluid_mod_clone */ void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src) { mod->dest = src->dest; mod->src1 = src->src1; mod->flags1 = src->flags1; mod->src2 = src->src2; mod->flags2 = src->flags2; mod->amount = src->amount; } /** * Set a modulator's primary source controller and flags. * @param mod Modulator * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller * (#FLUID_MOD_CC), see #fluid_mod_flags. */ void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags) { mod->src1 = src; mod->flags1 = flags; } /** * Set a modulator's secondary source controller and flags. * @param mod Modulator * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller * (#FLUID_MOD_CC), see #fluid_mod_flags. */ void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags) { mod->src2 = src; mod->flags2 = flags; } /** * Set the destination effect of a modulator. * @param mod Modulator * @param dest Destination generator (#fluid_gen_type) */ void fluid_mod_set_dest(fluid_mod_t* mod, int dest) { mod->dest = dest; } /** * Set the scale amount of a modulator. * @param mod Modulator * @param amount Scale amount to assign */ void fluid_mod_set_amount(fluid_mod_t* mod, double amount) { mod->amount = (double) amount; } /** * Get the primary source value from a modulator. * @param mod Modulator * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). */ int fluid_mod_get_source1(fluid_mod_t* mod) { return mod->src1; } /** * Get primary source flags from a modulator. * @param mod Modulator * @return The primary source flags (#fluid_mod_flags). */ int fluid_mod_get_flags1(fluid_mod_t* mod) { return mod->flags1; } /** * Get the secondary source value from a modulator. * @param mod Modulator * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). */ int fluid_mod_get_source2(fluid_mod_t* mod) { return mod->src2; } /** * Get secondary source flags from a modulator. * @param mod Modulator * @return The secondary source flags (#fluid_mod_flags). */ int fluid_mod_get_flags2(fluid_mod_t* mod) { return mod->flags2; } /** * Get destination effect from a modulator. * @param mod Modulator * @return Destination generator (#fluid_gen_type) */ int fluid_mod_get_dest(fluid_mod_t* mod) { return mod->dest; } /** * Get the scale amount from a modulator. * @param mod Modulator * @return Scale amount */ double fluid_mod_get_amount(fluid_mod_t* mod) { return (double) mod->amount; } /* * retrieves the initial value from the given source of the modulator */ static fluid_real_t fluid_mod_get_source_value(const unsigned char mod_src, const unsigned char mod_flags, fluid_real_t* range, const fluid_channel_t* chan, const fluid_voice_t* voice ) { fluid_real_t val; if (mod_flags & FLUID_MOD_CC) { val = fluid_channel_get_cc(chan, mod_src); } else { switch (mod_src) { case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ val = *range; break; case FLUID_MOD_VELOCITY: val = fluid_voice_get_actual_velocity(voice); break; case FLUID_MOD_KEY: val = fluid_voice_get_actual_key(voice); break; case FLUID_MOD_KEYPRESSURE: val = fluid_channel_get_key_pressure (chan); break; case FLUID_MOD_CHANNELPRESSURE: val = fluid_channel_get_channel_pressure (chan); break; case FLUID_MOD_PITCHWHEEL: val = fluid_channel_get_pitch_bend (chan); *range = 0x4000; break; case FLUID_MOD_PITCHWHEELSENS: val = fluid_channel_get_pitch_wheel_sensitivity (chan); break; default: FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src); val = 0.0; } } return val; } /** * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0] */ 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] */ const fluid_real_t val_norm = val / range; /* we could also only switch case the lower nibble of mod_flags, however * this would keep us from adding further mod types in the future * * instead just remove the flag(s) we already took care of */ mod_flags &= ~FLUID_MOD_CC; switch (mod_flags/* & 0x0f*/) { case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */ val = val_norm; break; case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */ val = 1.0f - val_norm; break; case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */ val = -1.0f + 2.0f * val_norm; break; case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */ val = 1.0f - 2.0f * val_norm; break; case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */ val = fluid_concave(127 * (val_norm)); break; case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */ val = fluid_concave(127 * (1.0f - val_norm)); break; case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */ val = (val_norm > 0.5f) ? fluid_concave(127 * 2 * (val_norm - 0.5f)) : -fluid_concave(127 * 2 * (0.5f - val_norm)); break; case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */ val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f)) : fluid_concave(127 * 2 * (0.5f - val_norm)); break; case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */ val = fluid_convex(127 * (val_norm)); break; case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */ val = fluid_convex(127 * (1.0f - val_norm)); break; case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */ val = (val_norm > 0.5f) ? fluid_convex(127 * 2 * (val_norm - 0.5f)) : -fluid_convex(127 * 2 * (0.5f - val_norm)); break; case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */ val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f)) : fluid_convex(127 * 2 * (0.5f - val_norm)); break; case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */ val = (val_norm >= 0.5f)? 1.0f : 0.0f; break; case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */ val = (val_norm >= 0.5f)? 0.0f : 1.0f; break; case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */ val = (val_norm >= 0.5f)? 1.0f : -1.0f; break; case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */ val = (val_norm >= 0.5f)? -1.0f : 1.0f; break; default: FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags); val = 0.0f; break; } return val; } /* * fluid_mod_get_value */ fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice) { fluid_real_t v1 = 0.0, v2 = 1.0; fluid_real_t range1 = 127.0, range2 = 127.0; if (chan == NULL) { return 0.0f; } /* 'special treatment' for default controller * * Reference: SF2.01 section 8.4.2 * * The GM default controller 'vel-to-filter cut off' is not clearly * defined: If implemented according to the specs, the filter * frequency jumps between vel=63 and vel=64. To maintain * compatibility with existing sound fonts, the implementation is * 'hardcoded', it is impossible to implement using only one * modulator otherwise. * * I assume here, that the 'intention' of the paragraph is one * octave (1200 cents) filter frequency shift between vel=127 and * vel=64. 'amount' is (-2400), at least as long as the controller * is set to default. * * Further, the 'appearance' of the modulator (source enumerator, * destination enumerator, flags etc) is different from that * described in section 8.4.2, but it matches the definition used in * several SF2.1 sound fonts (where it is used only to turn it off). * */ if ((mod->src2 == FLUID_MOD_VELOCITY) && (mod->src1 == FLUID_MOD_VELOCITY) && (mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) && (mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) && (mod->dest == GEN_FILTERFC)) { // S. Christian Collins' mod, to stop forcing velocity based filtering /* if (voice->vel < 64){ return (fluid_real_t) mod->amount / 2.0; } else { return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; } */ return 0; // (fluid_real_t) mod->amount / 2.0; } // end S. Christian Collins' mod /* get the initial value of the first source */ if (mod->src1 > 0) { v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, chan, voice); /* transform the input value */ v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); } else { return 0.0; } /* no need to go further */ if (v1 == 0.0f) { return 0.0f; } /* get the second input source */ if (mod->src2 > 0) { v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, chan, voice); /* transform the second input value */ v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); } else { v2 = 1.0f; } /* it's as simple as that: */ return (fluid_real_t) mod->amount * v1 * v2; } /** * Create a new uninitialized modulator structure. * @return New allocated modulator or NULL if out of memory */ fluid_mod_t* fluid_mod_new() { fluid_mod_t* mod = FLUID_NEW (fluid_mod_t); if (mod == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } return mod; } /** * Free a modulator structure. * @param mod Modulator to free */ void fluid_mod_delete (fluid_mod_t *mod) { FLUID_FREE(mod); } /** * Checks if two modulators are identical in sources, flags and destination. * @param mod1 First modulator * @param mod2 Second modulator * @return TRUE if identical, FALSE otherwise * * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'. */ int fluid_mod_test_identity (fluid_mod_t *mod1, fluid_mod_t *mod2) { return mod1->dest == mod2->dest && mod1->src1 == mod2->src1 && mod1->src2 == mod2->src2 && mod1->flags1 == mod2->flags1 && mod1->flags2 == mod2->flags2; } /* debug function: Prints the contents of a modulator */ void fluid_dump_modulator(fluid_mod_t * mod){ int src1=mod->src1; int dest=mod->dest; int src2=mod->src2; int flags1=mod->flags1; int flags2=mod->flags2; fluid_real_t amount=(fluid_real_t)mod->amount; printf("Src: "); if (flags1 & FLUID_MOD_CC){ printf("MIDI CC=%i",src1); } else { switch(src1){ case FLUID_MOD_NONE: printf("None"); break; case FLUID_MOD_VELOCITY: printf("note-on velocity"); break; case FLUID_MOD_KEY: printf("Key nr"); break; case FLUID_MOD_KEYPRESSURE: printf("Poly pressure"); break; case FLUID_MOD_CHANNELPRESSURE: printf("Chan pressure"); break; case FLUID_MOD_PITCHWHEEL: printf("Pitch Wheel"); break; case FLUID_MOD_PITCHWHEELSENS: printf("Pitch Wheel sens"); break; default: printf("(unknown: %i)", src1); }; /* switch src1 */ }; /* if not CC */ if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");}; if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");}; printf("-> "); switch(dest){ case GEN_FILTERQ: printf("Q"); break; case GEN_FILTERFC: printf("fc"); break; case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break; case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break; case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break; case GEN_CHORUSSEND: printf("Chorus send"); break; case GEN_REVERBSEND: printf("Reverb send"); break; case GEN_PAN: printf("pan"); break; case GEN_ATTENUATION: printf("att"); break; default: printf("dest %i",dest); }; /* switch dest */ printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2); }; fluidsynth-1.1.9/src/synth/fluid_mod.h000066400000000000000000000031221322272076000177610ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_MOD_H #define _FLUID_MOD_H #include "fluidsynth_priv.h" #include "fluid_conv.h" void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src); fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice); void fluid_dump_modulator(fluid_mod_t * mod); #define fluid_mod_has_source(mod,cc,ctrl) \ ( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \ || ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \ || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \ || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0))))) #define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen) #endif /* _FLUID_MOD_H */ fluidsynth-1.1.9/src/synth/fluid_synth.c000066400000000000000000005127671322272076000203660ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_chan.h" #include "fluid_tuning.h" #include "fluid_settings.h" #include "fluid_sfont.h" #include "fluid_hash.h" #include "fluid_defsfont.h" #ifdef TRAP_ON_FPE #define _GNU_SOURCE #include /* seems to not be declared in fenv.h */ extern int feenableexcept (int excepts); #endif static void fluid_synth_init(void); static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel); static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key); static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num); static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value); static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name, fluid_real_t value); static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun); static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl); static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int channum); static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset); static fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, unsigned int banknum, unsigned int prognum); static fluid_preset_t* fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, unsigned int banknum, unsigned int prognum); static void fluid_synth_update_presets(fluid_synth_t* synth); static int fluid_synth_update_sample_rate(fluid_synth_t* synth, char* name, double value); static int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value); static void fluid_synth_update_gain_LOCAL(fluid_synth_t* synth); static int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value); static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony); static void init_dither(void); static inline int roundi (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 fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont); static int fluid_synth_sfunload_callback(void* data, unsigned int msec); static 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, fluid_tuning_t *tuning, int bank, int prog, int apply); static void fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, fluid_tuning_t *old_tuning, fluid_tuning_t *new_tuning, int apply, int unref_new); static void fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *channel); static int fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, fluid_tuning_t *tuning, int apply); static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value, int absolute); static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); /*************************************************************** * * GLOBAL */ /* has the synth module been initialized? */ static int fluid_synth_initialized = 0; static void fluid_synth_init(void); static void init_dither(void); /* default modulators * SF2.01 page 52 ff: * * There is a set of predefined default modulators. They have to be * explicitly overridden by the sound font in order to turn them off. */ fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ /* reverb presets */ static fluid_revmodel_presets_t revmodel_preset[] = { /* name */ /* roomsize */ /* damp */ /* width */ /* level */ { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f }, { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f }, { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f }, { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f }, { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f }, { NULL, 0.0f, 0.0f, 0.0f, 0.0f } }; /*************************************************************** * * INITIALIZATION & UTILITIES */ static void fluid_synth_register_overflow(fluid_settings_t* settings, fluid_num_update_t update_func, void* update_data) { fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0, update_func, update_data); fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0, update_func, update_data); fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0, update_func, update_data); fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0, update_func, update_data); fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0, update_func, update_data); } void fluid_synth_settings(fluid_settings_t* settings) { fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.dump", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL); fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0, NULL, NULL); fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0, NULL, NULL); fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_settings_register_int(settings, "synth.parallel-render", 1, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL); fluid_synth_register_overflow(settings, NULL, NULL); fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0, NULL, NULL); fluid_settings_add_option(settings, "synth.midi-bank-select", "gm"); fluid_settings_add_option(settings, "synth.midi-bank-select", "gs"); fluid_settings_add_option(settings, "synth.midi-bank-select", "xg"); fluid_settings_add_option(settings, "synth.midi-bank-select", "mma"); } /** * Get FluidSynth runtime version. * @param major Location to store major number * @param minor Location to store minor number * @param micro Location to store micro number */ void fluid_version(int *major, int *minor, int *micro) { *major = FLUIDSYNTH_VERSION_MAJOR; *minor = FLUIDSYNTH_VERSION_MINOR; *micro = FLUIDSYNTH_VERSION_MICRO; } /** * Get FluidSynth runtime version as a string. * @return FluidSynth version string, which is internal and should not be * modified or freed. */ char * fluid_version_str (void) { return FLUIDSYNTH_VERSION; } #define FLUID_API_ENTRY_CHAN(fail_value) \ fluid_return_val_if_fail (synth != NULL, fail_value); \ fluid_return_val_if_fail (chan >= 0, fail_value); \ fluid_synth_api_enter(synth); \ if (chan >= synth->midi_channels) { \ fluid_synth_api_exit(synth); \ return fail_value; \ } \ #define FLUID_API_RETURN(return_value) \ do { fluid_synth_api_exit(synth); \ return return_value; } while (0) /* * void fluid_synth_init * * Does all the initialization for this module. */ static void fluid_synth_init(void) { fluid_synth_initialized++; #ifdef TRAP_ON_FPE /* Turn on floating point exception traps */ feenableexcept (FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); #endif fluid_conversion_config(); fluid_rvoice_dsp_config(); fluid_sys_config(); init_dither(); /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */ FLUID_MOD_GC /* Not a MIDI continuous controller */ | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ ); fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */ fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ fluid_mod_set_amount(&default_vel2att_mod, 960.0); /* Modulation amount: 960 */ /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff * Have to make a design decision here. The specs don't make any sense this way or another. * One sound font, 'Kingston Piano', which has been praised for its quality, tries to * override this modulator with an amount of 0 and positive polarity (instead of what * the specs say, D=1) for the secondary source. * So if we change the polarity to 'positive', one of the best free sound fonts works... */ fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ FLUID_MOD_GC /* CC=0 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_NEGATIVE /* D=1 */ ); fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ FLUID_MOD_GC /* CC=0 */ | FLUID_MOD_SWITCH /* type=3 */ | FLUID_MOD_UNIPOLAR /* P=0 */ // do not remove | FLUID_MOD_NEGATIVE /* D=1 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */ fluid_mod_set_amount(&default_vel2filter_mod, -2400); /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */ fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */ FLUID_MOD_GC /* CC=0 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_at2viblfo_mod, 0,0); /* no second source */ fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ fluid_mod_set_amount(&default_at2viblfo_mod, 50); /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */ fluid_mod_set_source1(&default_mod2viblfo_mod, 1, /* Index=1 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_mod2viblfo_mod, 0,0); /* no second source */ fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ fluid_mod_set_amount(&default_mod2viblfo_mod, 50); /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/ fluid_mod_set_source1(&default_att_mod, 7, /* index=7 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_CONCAVE /* type=1 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_NEGATIVE /* D=1 */ ); fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ fluid_mod_set_amount(&default_att_mod, 960.0); /* Amount: 960 */ /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ fluid_mod_set_source1(&default_pan_mod, 10, /* index=10 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_BIPOLAR /* P=1 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ /* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000 tenths of a percent". The center value (64) corresponds to 50%, so it follows that amount = 50% x 1000/% = 500. */ fluid_mod_set_amount(&default_pan_mod, 500.0); /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/ fluid_mod_set_source1(&default_expr_mod, 11, /* index=11 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_CONCAVE /* type=1 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_NEGATIVE /* D=1 */ ); fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ fluid_mod_set_amount(&default_expr_mod, 960.0); /* Amount: 960 */ /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ fluid_mod_set_source1(&default_reverb_mod, 91, /* index=91 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */ fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */ /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */ fluid_mod_set_source1(&default_chorus_mod, 93, /* index=93 */ FLUID_MOD_CC /* CC=1 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */ fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */ /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ FLUID_MOD_GC /* CC =0 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_BIPOLAR /* P=1 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */ FLUID_MOD_GC /* CC=0 */ | FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_POSITIVE /* D=0 */ ); fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */ fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ } static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t* synth) { if (synth->eventhandler->is_threadsafe) return fluid_atomic_int_get(&synth->ticks_since_start); else return synth->ticks_since_start; } static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t* synth, int val) { if (synth->eventhandler->is_threadsafe) fluid_atomic_int_add((int*) &synth->ticks_since_start, val); else synth->ticks_since_start += val; } /*************************************************************** * FLUID SAMPLE TIMERS * Timers that use written audio data as timing reference */ struct _fluid_sample_timer_t { fluid_sample_timer_t* next; /* Single linked list of timers */ unsigned long starttick; fluid_timer_callback_t callback; void* data; int isfinished; }; /* * fluid_sample_timer_process - called when synth->ticks is updated */ void fluid_sample_timer_process(fluid_synth_t* synth) { fluid_sample_timer_t* st; long msec; int cont; unsigned int ticks = fluid_synth_get_ticks(synth); for (st=synth->sample_timers; st; st=st->next) { if (st->isfinished) { continue; } msec = (long) (1000.0*((double) (ticks - st->starttick))/synth->sample_rate); cont = (*st->callback)(st->data, msec); if (cont == 0) { st->isfinished = 1; } } } fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data) { fluid_sample_timer_t* result = FLUID_NEW(fluid_sample_timer_t); if (result == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } result->starttick = fluid_synth_get_ticks(synth); result->isfinished = 0; result->data = data; result->callback = callback; result->next = synth->sample_timers; synth->sample_timers = result; return result; } int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer) { fluid_sample_timer_t** ptr = &synth->sample_timers; while (*ptr) { if (*ptr == timer) { *ptr = timer->next; FLUID_FREE(timer); return FLUID_OK; } ptr = &((*ptr)->next); } FLUID_LOG(FLUID_ERR,"delete_fluid_sample_timer failed, no timer found"); return FLUID_FAILED; } /*************************************************************** * * FLUID SYNTH */ static FLUID_INLINE void fluid_synth_update_mixer(fluid_synth_t* synth, void* method, int intparam, fluid_real_t realparam) { fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL); fluid_return_if_fail(synth->eventhandler->mixer != NULL); fluid_rvoice_eventhandler_push(synth->eventhandler, method, synth->eventhandler->mixer, intparam, realparam); } /** * Create new FluidSynth instance. * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * * @note The settings parameter is used directly and should not be modified * or freed independently. */ fluid_synth_t* new_fluid_synth(fluid_settings_t *settings) { fluid_synth_t* synth; fluid_sfloader_t* loader; double gain; int i, nbuf; /* initialize all the conversion tables and other stuff */ if (fluid_synth_initialized == 0) { fluid_synth_init(); } /* allocate a new synthesizer object */ synth = FLUID_NEW(fluid_synth_t); if (synth == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); fluid_rec_mutex_init(synth->mutex); fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); synth->public_api_count = 0; synth->settings = settings; fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb); fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus); fluid_settings_getint(settings, "synth.verbose", &synth->verbose); fluid_settings_getint(settings, "synth.dump", &synth->dump); fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); fluid_settings_getnum(settings, "synth.gain", &gain); synth->gain = gain; fluid_settings_getint(settings, "synth.device-id", &synth->device_id); fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); /* register the callbacks */ fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0, (fluid_num_update_t) fluid_synth_update_sample_rate, synth); fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0, (fluid_num_update_t) fluid_synth_update_gain, synth); fluid_settings_register_int(settings, "synth.polyphony", synth->polyphony, 1, 65535, 0, (fluid_int_update_t) fluid_synth_update_polyphony, synth); fluid_settings_register_int(settings, "synth.device-id", synth->device_id, 126, 0, 0, (fluid_int_update_t) fluid_synth_update_device_id, synth); fluid_synth_register_overflow(settings, (fluid_num_update_t) fluid_synth_update_overflow, synth); /* do some basic sanity checking on the settings */ if (synth->midi_channels % 16 != 0) { int n = synth->midi_channels / 16; synth->midi_channels = (n + 1) * 16; fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels); FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. " "I'll increase the number of channels to the next multiple."); } if (synth->audio_channels < 1) { FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. " "Changing this setting to 1."); synth->audio_channels = 1; } else if (synth->audio_channels > 128) { FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). " "Limiting this setting to 128.", synth->audio_channels); synth->audio_channels = 128; } if (synth->audio_groups < 1) { FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. " "Changing this setting to 1."); synth->audio_groups = 1; } else if (synth->audio_groups > 128) { FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). " "Limiting this setting to 128.", synth->audio_groups); synth->audio_groups = 128; } if (synth->effects_channels < 2) { FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)." "Setting effects channels to 2.", synth->effects_channels); synth->effects_channels = 2; } /* The number of buffers is determined by the higher number of nr * groups / nr audio channels. If LADSPA is unused, they should be * the same. */ nbuf = synth->audio_channels; if (synth->audio_groups > nbuf) { nbuf = synth->audio_groups; } /* as soon as the synth is created it starts playing. */ synth->state = FLUID_SYNTH_PLAYING; synth->sfont_info = NULL; synth->sfont_hash = new_fluid_hashtable (NULL, NULL); synth->noteid = 0; synth->ticks_since_start = 0; synth->tuning = NULL; fluid_private_init(synth->tuning_iter); /* Allocate event queue for rvoice mixer */ fluid_settings_getint(settings, "synth.parallel-render", &i); /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ synth->eventhandler = new_fluid_rvoice_eventhandler(i, synth->polyphony*64, synth->polyphony, nbuf, synth->effects_channels, synth->sample_rate); if (synth->eventhandler == NULL) goto error_recovery; #ifdef LADSPA /* Create and initialize the Fx unit.*/ synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth); fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->LADSPA_FxUnit); #endif /* allocate and add the default sfont loader */ loader = new_fluid_defsfloader(settings); if (loader == NULL) { FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader"); } else { fluid_synth_add_sfloader(synth, loader); } /* allocate all channel objects */ synth->channel = FLUID_ARRAY(fluid_channel_t*, synth->midi_channels); if (synth->channel == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } for (i = 0; i < synth->midi_channels; i++) { synth->channel[i] = new_fluid_channel(synth, i); if (synth->channel[i] == NULL) { goto error_recovery; } } /* allocate all synthesis processes */ synth->nvoice = synth->polyphony; synth->voice = FLUID_ARRAY(fluid_voice_t*, synth->nvoice); if (synth->voice == NULL) { goto error_recovery; } for (i = 0; i < synth->nvoice; i++) { synth->voice[i] = new_fluid_voice(synth->sample_rate); if (synth->voice[i] == NULL) { goto error_recovery; } } fluid_synth_set_sample_rate(synth, synth->sample_rate); fluid_synth_update_overflow(synth, "", 0.0f); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, synth->polyphony, 0.0f); fluid_synth_set_reverb_on(synth, synth->with_reverb); fluid_synth_set_chorus_on(synth, synth->with_chorus); synth->cur = FLUID_BUFSIZE; synth->curmax = 0; synth->dither_index = 0; synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE; synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP; synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH; synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL; fluid_rvoice_eventhandler_push5(synth->eventhandler, fluid_rvoice_mixer_set_reverb_params, synth->eventhandler->mixer, FLUID_REVMODEL_SET_ALL, synth->reverb_roomsize, synth->reverb_damping, synth->reverb_width, synth->reverb_level, 0.0f); /* Initialize multi-core variables if multiple cores enabled */ if (synth->cores > 1) { int prio_level = 0; fluid_settings_getint (synth->settings, "audio.realtime-prio", &prio_level); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_threads, synth->cores-1, prio_level); } synth->bank_select = FLUID_BANK_STYLE_GS; if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gm") == 1) synth->bank_select = FLUID_BANK_STYLE_GM; else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gs") == 1) synth->bank_select = FLUID_BANK_STYLE_GS; else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "xg") == 1) synth->bank_select = FLUID_BANK_STYLE_XG; else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "mma") == 1) synth->bank_select = FLUID_BANK_STYLE_MMA; fluid_synth_process_event_queue(synth); /* FIXME */ synth->start = fluid_curtime(); return synth; error_recovery: delete_fluid_synth(synth); return NULL; } /** * Delete a FluidSynth instance. * @param synth FluidSynth instance to delete * @return FLUID_OK * * @note Other users of a synthesizer instance, such as audio and MIDI drivers, * should be deleted prior to freeing the FluidSynth instance. */ int delete_fluid_synth(fluid_synth_t* synth) { int i, k; fluid_list_t *list; fluid_sfont_info_t* sfont_info; fluid_sfloader_t* loader; if (synth == NULL) { return FLUID_OK; } fluid_profiling_print(); /* turn off all voices, needed to unload SoundFont data */ if (synth->voice != NULL) { for (i = 0; i < synth->nvoice; i++) { fluid_voice_t* voice = synth->voice[i]; if (!voice) continue; fluid_voice_unlock_rvoice(voice); fluid_voice_overflow_rvoice_finished(voice); if (fluid_voice_is_playing(voice)) { fluid_voice_off(voice); /* If we only use fluid_voice_off(voice) it will trigger a delayed * fluid_voice_stop(voice) via fluid_synth_check_finished_voices(). * But here, we are deleting the fluid_synth_t instance so * fluid_voice_stop() will be never triggered resulting in * SoundFont data never unloaded (i.e a serious memory leak). * So, fluid_voice_stop() must be explicitly called to insure * unloading SoundFont data */ fluid_voice_stop(voice); } } } /* also unset all presets for clean SoundFont unload */ if (synth->channel != NULL) for (i = 0; i < synth->midi_channels; i++) if (synth->channel[i] != NULL) fluid_channel_set_preset(synth->channel[i], NULL); if (synth->eventhandler) delete_fluid_rvoice_eventhandler(synth->eventhandler); /* delete all the SoundFonts */ for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); delete_fluid_sfont (sfont_info->sfont); FLUID_FREE (sfont_info); } delete_fluid_list(synth->sfont_info); /* Delete the SoundFont info hash */ if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash); /* delete all the SoundFont loaders */ for (list = synth->loaders; list; list = fluid_list_next(list)) { loader = (fluid_sfloader_t*) fluid_list_get(list); fluid_sfloader_delete(loader); } delete_fluid_list(synth->loaders); if (synth->channel != NULL) { for (i = 0; i < synth->midi_channels; i++) { if (synth->channel[i] != NULL) { delete_fluid_channel(synth->channel[i]); } } FLUID_FREE(synth->channel); } if (synth->voice != NULL) { for (i = 0; i < synth->nvoice; i++) { if (synth->voice[i] != NULL) { delete_fluid_voice(synth->voice[i]); } } FLUID_FREE(synth->voice); } /* free the tunings, if any */ if (synth->tuning != NULL) { for (i = 0; i < 128; i++) { if (synth->tuning[i] != NULL) { for (k = 0; k < 128; k++) { if (synth->tuning[i][k] != NULL) { delete_fluid_tuning(synth->tuning[i][k]); } } FLUID_FREE(synth->tuning[i]); } } FLUID_FREE(synth->tuning); } fluid_private_free (synth->tuning_iter); #ifdef LADSPA /* Release the LADSPA Fx unit */ fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); FLUID_FREE(synth->LADSPA_FxUnit); #endif fluid_rec_mutex_destroy(synth->mutex); FLUID_FREE(synth); return FLUID_OK; } /** * Get a textual representation of the last error * @param synth FluidSynth instance * @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. */ /* FIXME - The error messages are not thread-safe, yet. They are still stored * in a global message buffer (see fluid_sys.c). */ char* fluid_synth_error(fluid_synth_t* synth) { return fluid_error(); } /** * Send a note-on event to a FluidSynth object. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @param vel MIDI velocity (0-127, 0=noteoff) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) { int result; fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); result = fluid_synth_noteon_LOCAL (synth, chan, key, vel); FLUID_API_RETURN(result); } /* Local synthesis thread variant of fluid_synth_noteon */ static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) { fluid_channel_t* channel; /* notes with velocity zero go to noteoff */ if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key); channel = synth->channel[chan]; /* make sure this channel has a preset */ if (channel->preset == NULL) { if (synth->verbose) { FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s", chan, key, vel, 0, fluid_synth_get_ticks(synth) / 44100.0f, (fluid_curtime() - synth->start) / 1000.0f, 0.0f, 0, "channel has no preset"); } return FLUID_FAILED; } /* If there is another voice process on the same channel and key, advance it to the release phase. */ fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); return fluid_preset_noteon(channel->preset, synth, chan, key, vel); } /** * Send a note-off event to a FluidSynth object. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no * voices matched the note off event) */ int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) { int result; fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); result = fluid_synth_noteoff_LOCAL (synth, chan, key); FLUID_API_RETURN(result); } /* Local synthesis thread variant of fluid_synth_noteoff */ static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) { fluid_voice_t* voice; int status = FLUID_FAILED; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_on(voice) && (fluid_voice_get_channel(voice) == chan) && (fluid_voice_get_key(voice) == key)) { if (synth->verbose) { int used_voices = 0; int k; for (k = 0; k < synth->polyphony; k++) { if (!_AVAILABLE(synth->voice[k])) { used_voices++; } } FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, fluid_voice_get_id(voice), (fluid_curtime() - synth->start) / 1000.0f, used_voices); } /* if verbose */ fluid_voice_noteoff(voice); status = FLUID_OK; } /* if voice on */ } /* for all voices */ return status; } /* Damp voices on a channel (turn notes off), if they're sustained by sustain pedal */ static int fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) fluid_voice_release(voice); } return FLUID_OK; } /* Damp voices on a channel (turn notes off), if they're sustained by sostenuto pedal */ static int fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if ((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) fluid_voice_release(voice); } return FLUID_OK; } /** * Send a MIDI controller event on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param num MIDI controller number (0-127) * @param val MIDI controller value (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) { int result; fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED); fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); if (synth->verbose) FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); fluid_channel_set_cc (synth->channel[chan], num, val); result = fluid_synth_cc_LOCAL (synth, chan, num); FLUID_API_RETURN(result); } /* Local synthesis thread variant of MIDI CC set function. */ static int fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num) { fluid_channel_t* chan = synth->channel[channum]; int nrpn_select; int value; value = fluid_channel_get_cc (chan, num); switch (num) { case SUSTAIN_SWITCH: /* Release voices if Sustain switch is released */ if (value < 64) /* Sustain is released */ fluid_synth_damp_voices_by_sustain_LOCAL (synth, channum); break; case SOSTENUTO_SWITCH: /* Release voices if Sostetuno switch is released */ if (value < 64) /* Sostenuto is released */ fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); else /* Sostenuto is depressed */ /* Update sostenuto order id when pedaling on Sostenuto */ chan->sostenuto_orderid = synth->noteid; /* future voice id value */ break; case BANK_SELECT_MSB: fluid_channel_set_bank_msb (chan, value & 0x7F); break; case BANK_SELECT_LSB: fluid_channel_set_bank_lsb (chan, value & 0x7F); break; case ALL_NOTES_OFF: fluid_synth_all_notes_off_LOCAL (synth, channum); break; case ALL_SOUND_OFF: fluid_synth_all_sounds_off_LOCAL (synth, channum); break; case ALL_CTRL_OFF: fluid_channel_init_ctrl (chan, 1); fluid_synth_modulate_voices_all_LOCAL (synth, channum); break; case DATA_ENTRY_MSB: { int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB); if (chan->nrpn_active) /* NRPN is active? */ { /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ if ((fluid_channel_get_cc (chan, NRPN_MSB) == 120) && (fluid_channel_get_cc (chan, NRPN_LSB) < 100)) { nrpn_select = chan->nrpn_select; if (nrpn_select < GEN_LAST) { float val = fluid_gen_scale_nrpn (nrpn_select, data); fluid_synth_set_gen_LOCAL (synth, channum, nrpn_select, val, FALSE); } chan->nrpn_select = 0; /* Reset to 0 */ } } else if (fluid_channel_get_cc (chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ { switch (fluid_channel_get_cc (chan, RPN_LSB)) { case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */ fluid_channel_set_pitch_wheel_sensitivity (synth->channel[channum], value); fluid_synth_update_pitch_wheel_sens_LOCAL (synth, channum); /* Update bend range */ /* FIXME - Handle LSB? (Fine bend range in cents) */ break; case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */ fluid_synth_set_gen_LOCAL (synth, channum, GEN_FINETUNE, (data - 8192) / 8192.0 * 100.0, FALSE); break; case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ fluid_synth_set_gen_LOCAL (synth, channum, GEN_COARSETUNE, value - 64, FALSE); break; case RPN_TUNING_PROGRAM_CHANGE: fluid_channel_set_tuning_prog (chan, value); fluid_synth_activate_tuning (synth, channum, fluid_channel_get_tuning_bank (chan), value, TRUE); break; case RPN_TUNING_BANK_SELECT: fluid_channel_set_tuning_bank (chan, value); break; case RPN_MODULATION_DEPTH_RANGE: break; } } break; } case NRPN_MSB: fluid_channel_set_cc (chan, NRPN_LSB, 0); chan->nrpn_select = 0; chan->nrpn_active = 1; break; case NRPN_LSB: /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ if (fluid_channel_get_cc (chan, NRPN_MSB) == 120) { if (value == 100) { chan->nrpn_select += 100; } else if (value == 101) { chan->nrpn_select += 1000; } else if (value == 102) { chan->nrpn_select += 10000; } else if (value < 100) { chan->nrpn_select += value; } } chan->nrpn_active = 1; break; case RPN_MSB: case RPN_LSB: chan->nrpn_active = 0; break; default: return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num); } return FLUID_OK; } /** * Get current MIDI controller value on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param num MIDI controller number (0-127) * @param pval Location to store MIDI controller value (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) { fluid_return_val_if_fail (num >= 0 && num < 128, FLUID_FAILED); fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); *pval = fluid_channel_get_cc (synth->channel[chan], num); FLUID_API_RETURN(FLUID_OK); } /* * Handler for synth.device-id setting. */ static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value) { fluid_synth_api_enter(synth); synth->device_id = value; fluid_synth_api_exit(synth); return 0; } /** * Process a MIDI SYSEX (system exclusive) message. * @param synth FluidSynth instance * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7) * @param len Length of data in buffer * @param response Buffer to store response to or NULL to ignore * @param response_len IN/OUT parameter, in: size of response buffer, out: * amount of data written to response buffer (if FLUID_FAILED is returned and * this value is non-zero, it indicates the response buffer is too small) * @param handled Optional location to store boolean value if message was * recognized and handled or not (set to TRUE if it was handled) * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX * command (useful for checking if a SYSEX message would be handled) * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 */ /* SYSEX format (0xF0 and 0xF7 not passed to this function): * Non-realtime: 0xF0 0x7E [BODY] 0xF7 * Realtime: 0xF0 0x7F [BODY] 0xF7 * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7 */ int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int *handled, int dryrun) { int avail_response = 0; if (handled) *handled = FALSE; if (response_len) { avail_response = *response_len; *response_len = 0; } fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (data != NULL, FLUID_FAILED); fluid_return_val_if_fail (len > 0, FLUID_FAILED); fluid_return_val_if_fail (!response || response_len, FLUID_FAILED); if (len < 4) return FLUID_OK; /* MIDI tuning SYSEX message? */ if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) { int result; fluid_synth_api_enter(synth); result = fluid_synth_sysex_midi_tuning (synth, data, len, response, response_len, avail_response, handled, dryrun); FLUID_API_RETURN(result); } return FLUID_OK; } /* Handler for MIDI tuning SYSEX messages */ static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun) { int realtime, msgid; int bank = 0, prog, channels; double tunedata[128]; int keys[128]; char name[17]; int note, frac, frac2; uint8 chksum; int i, count, index; const char *dataptr; char *resptr;; realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; msgid = data[3]; switch (msgid) { case MIDI_SYSEX_TUNING_BULK_DUMP_REQ: case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK: if (data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) { if (len != 5 || data[4] & 0x80 || !response) return FLUID_OK; *response_len = 406; prog = data[4]; } else { if (len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) return FLUID_OK; *response_len = 407; bank = data[4]; prog = data[5]; } if (dryrun) { if (handled) *handled = TRUE; return FLUID_OK; } if (avail_response < *response_len) return FLUID_FAILED; /* Get tuning data, return if tuning not found */ if (fluid_synth_tuning_dump (synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) { *response_len = 0; return FLUID_OK; } resptr = response; *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; *resptr++ = synth->device_id; *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) *resptr++ = bank; *resptr++ = prog; FLUID_STRNCPY (resptr, name, 16); resptr += 16; for (i = 0; i < 128; i++) { note = tunedata[i] / 100.0; fluid_clip (note, 0, 127); frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; fluid_clip (frac, 0, 16383); *resptr++ = note; *resptr++ = frac >> 7; *resptr++ = frac & 0x7F; } if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) { /* NOTE: Checksum is not as straight forward as the bank based messages */ chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; for (i = 21; i < 128 * 3 + 21; i++) chksum ^= response[i]; } else { for (i = 1, chksum = 0; i < 406; i++) chksum ^= response[i]; } *resptr++ = chksum & 0x7F; if (handled) *handled = TRUE; break; case MIDI_SYSEX_TUNING_NOTE_TUNE: case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK: dataptr = data + 4; if (msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) { if (len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) return FLUID_OK; } else { if (len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 || len != data[6] * 4 + 7) return FLUID_OK; bank = *dataptr++; } if (dryrun) { if (handled) *handled = TRUE; return FLUID_OK; } prog = *dataptr++; count = *dataptr++; for (i = 0, index = 0; i < count; i++) { note = *dataptr++; if (note & 0x80) return FLUID_OK; keys[index] = note; note = *dataptr++; frac = *dataptr++; frac2 = *dataptr++; if (note & 0x80 || frac & 0x80 || frac2 & 0x80) return FLUID_OK; frac = frac << 7 | frac2; /* No change pitch value? Doesn't really make sense to send that, but.. */ if (note == 0x7F && frac == 16383) continue; tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); index++; } if (index > 0) { if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata, realtime) == FLUID_FAILED) return FLUID_FAILED; } if (handled) *handled = TRUE; break; case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE: case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE: if ((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) return FLUID_OK; if (data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) return FLUID_OK; if (dryrun) { if (handled) *handled = TRUE; return FLUID_OK; } channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) { for (i = 0; i < 12; i++) { frac = data[i + 7]; if (frac & 0x80) return FLUID_OK; tunedata[i] = (int)frac - 64; } } else { for (i = 0; i < 12; i++) { frac = data[i * 2 + 7]; frac2 = data[i * 2 + 8]; if (frac & 0x80 || frac2 & 0x80) return FLUID_OK; tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); } } if (fluid_synth_activate_octave_tuning (synth, 0, 0, "SYSEX", tunedata, realtime) == FLUID_FAILED) return FLUID_FAILED; if (channels) { for (i = 0; i < 16; i++) { if (channels & (1 << i)) fluid_synth_activate_tuning (synth, i, 0, 0, realtime); } } if (handled) *handled = TRUE; break; } return FLUID_OK; } /** * Turn off all notes on a MIDI channel (put them into release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.4 */ int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (chan >= -1, FLUID_FAILED); fluid_synth_api_enter(synth); if (chan >= synth->midi_channels) result = FLUID_FAILED; else result = fluid_synth_all_notes_off_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) fluid_voice_noteoff(voice); } return FLUID_OK; } /** * Immediately stop all notes on a MIDI channel (skips release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.4 */ int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (chan >= -1, FLUID_FAILED); fluid_synth_api_enter(synth); if (chan >= synth->midi_channels) result = FLUID_FAILED; else result = fluid_synth_all_sounds_off_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */ static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) fluid_voice_off(voice); } return FLUID_OK; } /** * Reset reverb engine * @param synth FluidSynth instance * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_reset_reverb(fluid_synth_t* synth) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); FLUID_API_RETURN(FLUID_OK); } /** * Reset chorus engine * @param synth FluidSynth instance * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_reset_chorus(fluid_synth_t* synth) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); FLUID_API_RETURN(FLUID_OK); } /** * Send MIDI system reset command (big red 'panic' button), turns off notes and * resets controllers. * @param synth FluidSynth instance * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_system_reset(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); result = fluid_synth_system_reset_LOCAL (synth); FLUID_API_RETURN(result); } /* Local variant of the system reset command */ static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice)) fluid_voice_off(voice); } for (i = 0; i < synth->midi_channels; i++) fluid_channel_reset(synth->channel[i]); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f); return FLUID_OK; } /** * Update voices on a MIDI channel after a MIDI control change. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param is_cc Boolean value indicating if ctrl is a CC controller or not * @param ctrl MIDI controller value * @return FLUID_OK on success, FLUID_FAILED otherwise */ static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_get_channel(voice) == chan) fluid_voice_modulate(voice, is_cc, ctrl); } return FLUID_OK; } /** * Update voices on a MIDI channel after all MIDI controllers have been changed. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @return FLUID_OK on success, FLUID_FAILED otherwise */ static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_get_channel(voice) == chan) fluid_voice_modulate_all(voice); } return FLUID_OK; } /** * Set the MIDI channel pressure controller value. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI channel pressure value (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) { int result; fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); if (synth->verbose) FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); fluid_channel_set_channel_pressure (synth->channel[chan], val); result = fluid_synth_update_channel_pressure_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Updates channel pressure from within synthesis thread */ static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan) { return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); } /** * Set the MIDI pitch bend controller value on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val MIDI pitch bend value (0-16383 with 8192 being center) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) { int result; fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); if (synth->verbose) FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); fluid_channel_set_pitch_bend (synth->channel[chan], val); result = fluid_synth_update_pitch_bend_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Local synthesis thread variant of pitch bend */ static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan) { return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL); } /** * Get the MIDI pitch bend controller value on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with * 8192 being center) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) { fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); FLUID_API_RETURN(FLUID_OK); } /** * Set MIDI pitch wheel sensitivity on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param val Pitch wheel sensitivity value in semitones * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) { int result; fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ FLUID_API_ENTRY_CHAN(FLUID_FAILED); if (synth->verbose) FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val); result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Local synthesis thread variant of set pitch wheel sensitivity */ static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan) { return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); } /** * Get MIDI pitch wheel sensitivity on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param pval Location to store pitch wheel sensitivity value in semitones * @return FLUID_OK on success, FLUID_FAILED otherwise * @since Sometime AFTER v1.0 API freeze. */ int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) { fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); FLUID_API_RETURN(FLUID_OK); } /** * Assign a preset to a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) * @return FLUID_OK on success, FLUID_FAILED otherwise */ static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset) { fluid_channel_t *channel; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); channel = synth->channel[chan]; return fluid_channel_set_preset (channel, preset); } /* Get a preset by SoundFont, bank and program numbers. * Returns preset pointer or NULL. * * @note The returned preset has been allocated, caller owns it and should * free it when finished using it. */ static fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, unsigned int banknum, unsigned int prognum) { fluid_preset_t *preset = NULL; fluid_sfont_info_t *sfont_info; fluid_list_t *list; /* 128 indicates an "unset" operation" */ if (prognum == FLUID_UNSET_PROGRAM) return NULL; for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum) { preset = fluid_sfont_get_preset (sfont_info->sfont, banknum - sfont_info->bankofs, prognum); if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ break; } } return preset; } /* Get a preset by SoundFont name, bank and program. * Returns preset pointer or NULL. * * @note The returned preset has been allocated, caller owns it and should * free it when finished using it. */ static fluid_preset_t* fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, unsigned int banknum, unsigned int prognum) { fluid_preset_t *preset = NULL; fluid_sfont_info_t *sfont_info; fluid_list_t *list; for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0) { preset = fluid_sfont_get_preset (sfont_info->sfont, banknum - sfont_info->bankofs, prognum); if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ break; } } return preset; } /* Find a preset by bank and program numbers. * Returns preset pointer or NULL. * * @note The returned preset has been allocated, caller owns it and should * free it when finished using it. */ fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, unsigned int prognum) { fluid_preset_t *preset = NULL; fluid_sfont_info_t *sfont_info; fluid_list_t *list; for (list = synth->sfont_info; list; list = fluid_list_next (list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); preset = fluid_sfont_get_preset (sfont_info->sfont, banknum - sfont_info->bankofs, prognum); if (preset) { sfont_info->refcount++; /* Add reference to SoundFont */ break; } } return preset; } /** * Send a program change event on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param prognum MIDI program number (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise */ /* FIXME - Currently not real-time safe, due to preset allocation and mutex lock, * and may be called from within synthesis context. */ /* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented * since fluid_synth_unset_program() should be used instead. */ int fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; int subst_bank, subst_prog, banknum = 0, result; fluid_return_val_if_fail (prognum >= 0 && prognum <= 128, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; if (channel->channel_type == CHANNEL_TYPE_DRUM) banknum = DRUM_INST_BANK; else fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); if (synth->verbose) FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); /* I think this is a hack for MIDI files that do bank changes in GM mode. * Proper way to handle this would probably be to ignore bank changes when in * GM mode. - JG * This is now possible by setting synth.midi-bank-select=gm, but let the hack * stay for the time being. - DH */ if (prognum != FLUID_UNSET_PROGRAM) { subst_bank = banknum; subst_prog = prognum; preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); /* Fallback to another preset if not found */ if (!preset) { /* Percussion: Fallback to preset 0 in percussion bank */ if (channel->channel_type == CHANNEL_TYPE_DRUM) { subst_prog = 0; subst_bank = DRUM_INST_BANK; preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); } /* Melodic instrument */ else { /* Fallback first to bank 0:prognum */ subst_bank = 0; preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); /* Fallback to first preset in bank 0 (usually piano...) */ if (!preset) { subst_prog = 0; preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); } } if (preset) FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", chan, banknum, prognum, subst_bank, subst_prog); else FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum); } } /* Assign the SoundFont ID and program number to the channel */ fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, -1, prognum); result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } /** * Set instrument bank number on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param bank MIDI bank number * @return FLUID_OK on success, FLUID_FAILED otherwise * @note This function does not change the instrument currently assigned to \c chan, * as it is usually called prior to fluid_synth_program_change(). If you still want * instrument changes to take effect immediately, call fluid_synth_program_reset() * after having set up the bank configuration. * */ int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) { fluid_return_val_if_fail (bank <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); FLUID_API_RETURN(FLUID_OK); } /** * Set SoundFont ID on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_id ID of a loaded SoundFont * @return FLUID_OK on success, FLUID_FAILED otherwise * @note This function does not change the instrument currently assigned to \c chan, * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change(). * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset() * after having selected the soundfont. */ int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) { FLUID_API_ENTRY_CHAN(FLUID_FAILED); fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); FLUID_API_RETURN(FLUID_OK); } /** * Set the preset of a MIDI channel to an unassigned state. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @since 1.1.1 * * Note: Channel retains its SoundFont ID and bank numbers, while the program * number is set to an "unset" state. MIDI program changes may re-assign a * preset if one matches. */ int fluid_synth_unset_program (fluid_synth_t *synth, int chan) { int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM); FLUID_API_RETURN(result); } /** * Get current SoundFont ID, bank number and program number for a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_id Location to store SoundFont ID * @param bank_num Location to store MIDI bank number * @param preset_num Location to store MIDI program number * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) { fluid_channel_t* channel; fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED); fluid_return_val_if_fail (bank_num != NULL, FLUID_FAILED); fluid_return_val_if_fail (preset_num != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, (int *)preset_num); /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; FLUID_API_RETURN(FLUID_OK); } /** * Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_id ID of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, unsigned int bank_num, unsigned int preset_num) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; /* ++ Allocate preset */ preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "There is no preset with bank number %d and preset number %d in SoundFont %d", bank_num, preset_num, sfont_id); FLUID_API_RETURN(FLUID_FAILED); } /* Assign the new SoundFont ID, bank and program number to the channel */ fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } /** * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param sfont_name Name of a loaded SoundFont * @param bank_num MIDI bank number * @param preset_num MIDI program number * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan, const char *sfont_name, unsigned int bank_num, unsigned int preset_num) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; int result; fluid_return_val_if_fail (sfont_name != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; /* ++ Allocate preset */ preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, preset_num); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "There is no preset with bank number %d and preset number %d in SoundFont %s", bank_num, preset_num, sfont_name); FLUID_API_RETURN(FLUID_FAILED); } /* Assign the new SoundFont ID, bank and program number to the channel */ fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), bank_num, preset_num); result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } /* * This function assures that every MIDI channel has a valid preset * (NULL is okay). This function is called after a SoundFont is * unloaded or reloaded. */ static void fluid_synth_update_presets(fluid_synth_t* synth) { fluid_channel_t *channel; fluid_preset_t *preset; int sfont, bank, prog; int chan; for (chan = 0; chan < synth->midi_channels; chan++) { channel = synth->channel[chan]; fluid_channel_get_sfont_bank_prog (channel, &sfont, &bank, &prog); preset = fluid_synth_get_preset (synth, sfont, bank, prog); fluid_synth_set_preset (synth, chan, preset); } } /* Handler for synth.gain setting. */ static int fluid_synth_update_sample_rate(fluid_synth_t* synth, char* name, double value) { fluid_synth_set_sample_rate(synth, (float) value); return 0; } /** * Set sample rate of the synth. * @note This function is currently experimental and should only be * used when no voices or notes are active, and before any rendering calls. * @param synth FluidSynth instance * @param sample_rate New sample rate (Hz) * @since 1.1.2 */ void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate) { int i; fluid_return_if_fail (synth != NULL); fluid_synth_api_enter(synth); fluid_clip (sample_rate, 8000.0f, 96000.0f); synth->sample_rate = sample_rate; fluid_settings_getint(synth->settings, "synth.min-note-length", &i); synth->min_note_length_ticks = (unsigned int) (i*synth->sample_rate/1000.0f); for (i=0; i < synth->polyphony; i++) fluid_voice_set_output_rate(synth->voice[i], sample_rate); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, 0, sample_rate); fluid_synth_api_exit(synth); } /* Handler for synth.gain setting. */ static int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) { fluid_synth_set_gain(synth, (float) value); return 0; } /** * Set synth output gain value. * @param synth FluidSynth instance * @param gain Gain value (function clamps value to the range 0.0 to 10.0) */ void fluid_synth_set_gain(fluid_synth_t* synth, float gain) { fluid_return_if_fail (synth != NULL); fluid_synth_api_enter(synth); fluid_clip (gain, 0.0f, 10.0f); synth->gain = gain; fluid_synth_update_gain_LOCAL (synth); fluid_synth_api_exit(synth); } /* Called by synthesis thread to update the gain in all voices */ static void fluid_synth_update_gain_LOCAL(fluid_synth_t* synth) { fluid_voice_t *voice; float gain; int i; gain = synth->gain; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice)) fluid_voice_set_gain (voice, gain); } } /** * Get synth output gain value. * @param synth FluidSynth instance * @return Synth gain value (0.0 to 10.0) */ float fluid_synth_get_gain(fluid_synth_t* synth) { float result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = synth->gain; FLUID_API_RETURN(result); } /* * Handler for synth.polyphony setting. */ static int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) { fluid_synth_set_polyphony(synth, value); return 0; } /** * Set synthesizer polyphony (max number of voices). * @param synth FluidSynth instance * @param polyphony Polyphony to assign * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.0.6 */ int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (polyphony >= 1 && polyphony <= 65535, FLUID_FAILED); fluid_synth_api_enter(synth); result = fluid_synth_update_polyphony_LOCAL(synth, polyphony); FLUID_API_RETURN(result); } /* Called by synthesis thread to update the polyphony value */ static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony) { fluid_voice_t *voice; int i; if (new_polyphony > synth->nvoice) { /* Create more voices */ fluid_voice_t** new_voices = FLUID_REALLOC(synth->voice, sizeof(fluid_voice_t*) * new_polyphony); if (new_voices == NULL) return FLUID_FAILED; synth->voice = new_voices; for (i = synth->nvoice; i < new_polyphony; i++) { synth->voice[i] = new_fluid_voice(synth->sample_rate); if (synth->voice[i] == NULL) return FLUID_FAILED; } synth->nvoice = new_polyphony; } synth->polyphony = new_polyphony; /* turn off any voices above the new limit */ for (i = synth->polyphony; i < synth->nvoice; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice)) fluid_voice_off (voice); } fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, synth->polyphony, 0.0f); return FLUID_OK; } /** * Get current synthesizer polyphony (max number of voices). * @param synth FluidSynth instance * @return Synth polyphony value. * @since 1.0.6 */ int fluid_synth_get_polyphony(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); result = synth->polyphony; FLUID_API_RETURN(result); } /** * Get current number of active voices. * @param synth FluidSynth instance * @return Number of currently active voices. * @since 1.1.0 * * Note: To generate accurate continuous statistics of the voice count, caller * should ensure this function is called synchronously with the audio synthesis * process. This can be done in the new_fluid_audio_driver2() audio callback * function for example. */ int fluid_synth_get_active_voice_count(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); result = synth->active_voice_count; FLUID_API_RETURN(result); } /** * Get the internal synthesis buffer size value. * @param synth FluidSynth instance * @return Internal buffer size in audio frames. * * Audio is synthesized this number of frames at a time. Defaults to 64 frames. */ int fluid_synth_get_internal_bufsize(fluid_synth_t* synth) { return FLUID_BUFSIZE; } /** * Resend a bank select and a program change for every channel and assign corresponding instruments. * @param synth FluidSynth instance * @return FLUID_OK on success, FLUID_FAILED otherwise * * This function is called mainly after a SoundFont has been loaded, * unloaded or reloaded. */ int fluid_synth_program_reset(fluid_synth_t* synth) { int i, prog; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* try to set the correct presets */ for (i = 0; i < synth->midi_channels; i++){ fluid_channel_get_sfont_bank_prog (synth->channel[i], NULL, NULL, &prog); fluid_synth_program_change(synth, i, prog); } FLUID_API_RETURN(FLUID_OK); } /** * Synthesize a block of floating point audio to separate audio buffers (multichannel rendering). First effect channel used by reverb, second for chorus. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size) * @param right Array of float buffers to store right channel of planar audio (size: dito) * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size) * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) * @return FLUID_OK on success, FLUID_FAIL otherwise * * @note Should only be called from synthesis thread. * * Usage example: * @code const int FramesToRender = 64; int channels; // retrieve number of stereo audio channels fluid_settings_getint(settings, "synth.audio-channels", &channels); // we need twice as many (mono-)buffers channels *= 2; // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM float** mix_buf = new float*[channels]; for(int i = 0; i < channels; i++) { mix_buf[i] = new float[FramesToRender]; } // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan)) fluid_settings_getint(settings, "synth.effects-channels", &channels); channels *= 2; float** fx_buf = new float*[channels]; for(int i = 0; i < channels; i++) { fx_buf[i] = new float[FramesToRender]; } float** mix_buf_l = mix_buf; float** mix_buf_r = &mix_buf[channels/2]; float** fx_buf_l = fx_buf; float** fx_buf_r = &fx_buf[channels/2]; fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r) * @endcode */ int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, float** left, float** right, float** fx_left, float** fx_right) { fluid_real_t** left_in, **fx_left_in; fluid_real_t** right_in, **fx_right_in; double time = fluid_utime(); int i, num, available, count; #ifdef WITH_FLOAT int bytes; #endif float cpu_load; if (!synth->eventhandler->is_threadsafe) fluid_synth_api_enter(synth); /* First, take what's still available in the buffer */ count = 0; num = synth->cur; if (synth->cur < FLUID_BUFSIZE) { available = FLUID_BUFSIZE - synth->cur; fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); num = (available > len)? len : available; #ifdef WITH_FLOAT bytes = num * sizeof(float); #endif for (i = 0; i < synth->audio_channels; i++) { #ifdef WITH_FLOAT FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes); FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes); #else //WITH_FLOAT int j; for (j = 0; j < num; j++) { left[i][j] = (float) left_in[i][j + synth->cur]; right[i][j] = (float) right_in[i][j + synth->cur]; } #endif //WITH_FLOAT } for (i = 0; i < synth->effects_channels; i++) { #ifdef WITH_FLOAT if(fx_left != NULL) FLUID_MEMCPY(fx_left[i], fx_left_in[i] + synth->cur, bytes); if(fx_right != NULL) FLUID_MEMCPY(fx_right[i], fx_right_in[i] + synth->cur, bytes); #else //WITH_FLOAT int j; if(fx_left != NULL) { for (j = 0; j < num; j++) fx_left[i][j] = (float) fx_left_in[i][j + synth->cur]; } if(fx_right != NULL) { for (j = 0; j < num; j++) fx_right[i][j] = (float) fx_right_in[i][j + synth->cur]; } #endif //WITH_FLOAT } count += num; num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ } /* Then, run one_block() and copy till we have 'len' samples */ while (count < len) { fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0); fluid_synth_render_blocks(synth, 1); // TODO: fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE; #ifdef WITH_FLOAT bytes = num * sizeof(float); #endif for (i = 0; i < synth->audio_channels; i++) { #ifdef WITH_FLOAT FLUID_MEMCPY(left[i] + count, left_in[i], bytes); FLUID_MEMCPY(right[i] + count, right_in[i], bytes); #else //WITH_FLOAT int j; for (j = 0; j < num; j++) { left[i][j + count] = (float) left_in[i][j]; right[i][j + count] = (float) right_in[i][j]; } #endif //WITH_FLOAT } for (i = 0; i < synth->effects_channels; i++) { #ifdef WITH_FLOAT if(fx_left != NULL) FLUID_MEMCPY(fx_left[i] + count, fx_left_in[i], bytes); if(fx_right != NULL) FLUID_MEMCPY(fx_right[i] + count, fx_right_in[i], bytes); #else //WITH_FLOAT int j; if(fx_left != NULL) { for (j = 0; j < num; j++) fx_left[i][j + count] = (float) fx_left_in[i][j]; } if(fx_right != NULL) { for (j = 0; j < num; j++) fx_right[i][j + count] = (float) fx_right_in[i][j]; } #endif //WITH_FLOAT } count += num; } synth->cur = num; time = fluid_utime() - time; cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set (&synth->cpu_load, cpu_load); if (!synth->eventhandler->is_threadsafe) fluid_synth_api_exit(synth); return FLUID_OK; } /** * Synthesize floating point audio to audio buffers. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param nin Ignored * @param in Ignored * @param nout Count of arrays in 'out' * @param out Array of arrays to store audio to * @return FLUID_OK on success, FLUID_FAIL otherwise * * This function implements the default interface defined in fluidsynth/audio.h. * * @note Should only be called from synthesis thread. */ /* * FIXME: Currently if nout != 2 memory allocation will occur! */ int fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, int nout, float** out) { if (nout==2) { return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1); } else { float **left, **right; int i; left = FLUID_ARRAY(float*, nout/2); right = FLUID_ARRAY(float*, nout/2); if ((left == NULL) || (right == NULL)) { FLUID_LOG(FLUID_ERR, "Out of memory."); FLUID_FREE(left); FLUID_FREE(right); return FLUID_FAILED; } for(i=0; ieventhandler->is_threadsafe) fluid_synth_api_enter(synth); fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); l = synth->cur; fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) { /* fill up the buffers as needed */ if (l >= synth->curmax) { int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); l = 0; } left_out[j] = (float) left_in[0][l]; right_out[k] = (float) right_in[0][l]; } synth->cur = l; time = fluid_utime() - time; cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set (&synth->cpu_load, cpu_load); if (!synth->eventhandler->is_threadsafe) fluid_synth_api_exit(synth); fluid_profile(FLUID_PROF_WRITE, prof_ref); return FLUID_OK; } #define DITHER_SIZE 48000 #define DITHER_CHANNELS 2 static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; /* Init dither table */ static void init_dither(void) { float d, dp; int c, i; for (c = 0; c < DITHER_CHANNELS; c++) { dp = 0; for (i = 0; i < DITHER_SIZE-1; i++) { d = rand() / (float)RAND_MAX - 0.5f; rand_table[c][i] = d - dp; dp = d; } rand_table[c][DITHER_SIZE-1] = 0 - dp; } } /* A portable replacement for roundf(), seems it may actually be faster too! */ static inline int roundi (float x) { if (x >= 0.0f) return (int)(x+0.5f); else return (int)(x-0.5f); } /** * Synthesize a block of 16 bit audio samples to audio buffers. * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param lout Array of 16 bit words to store left channel of audio * @param loff Offset index in 'lout' for first sample * @param lincr Increment between samples stored to 'lout' * @param rout Array of 16 bit words to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' * @return FLUID_OK on success, FLUID_FAIL otherwise * * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, * lincr = 2, rincr = 2). * * @note Should only be called from synthesis thread. * @note Reverb and Chorus are mixed to \c lout resp. \c rout. * @note Dithering is performed when converting from internal floating point to * 16 bit audio. */ int fluid_synth_write_s16(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, 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; 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; //double prof_ref_on_block; float cpu_load; fluid_profile_ref_var (prof_ref); if (!synth->eventhandler->is_threadsafe) fluid_synth_api_enter(synth); fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = synth->cur; di = synth->dither_index; for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) { /* fill up the buffers as needed */ if (cur >= synth->curmax) { int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE; //prof_ref_on_block = fluid_profile_ref(); synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; //fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block); } left_sample = roundi (left_in[0][cur] * 32766.0f + rand_table[0][di]); right_sample = roundi (right_in[0][cur] * 32766.0f + rand_table[1][di]); di++; 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; synth->dither_index = di; /* keep dither buffer continous */ fluid_profile(FLUID_PROF_WRITE, prof_ref); time = fluid_utime() - time; cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set (&synth->cpu_load, cpu_load); if (!synth->eventhandler->is_threadsafe) fluid_synth_api_exit(synth); return 0; } /** * Converts stereo floating point sample data to signed 16 bit data with dithering. * @param dither_index Pointer to an integer which should be initialized to 0 * before the first call and passed unmodified to additional calls which are * part of the same synthesis output. * @param len Length in frames to convert * @param lin Buffer of left audio samples to convert from * @param rin Buffer of right audio samples to convert from * @param lout Array of 16 bit words to store left channel of audio * @param loff Offset index in 'lout' for first sample * @param lincr Increment between samples stored to 'lout' * @param rout Array of 16 bit words to store right channel of audio * @param roff Offset index in 'rout' for first sample * @param rincr Increment between samples stored to 'rout' * * @note Currently private to libfluidsynth. */ void fluid_synth_dither_s16(int *dither_index, int len, float* lin, 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; 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_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) 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 */ fluid_profile(FLUID_PROF_WRITE, prof_ref); } static void fluid_synth_check_finished_voices(fluid_synth_t* synth) { int j; fluid_rvoice_t* fv; while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) { for (j=0; j < synth->polyphony; j++) { if (synth->voice[j]->rvoice == fv) { fluid_voice_unlock_rvoice(synth->voice[j]); fluid_voice_stop(synth->voice[j]); break; } else if (synth->voice[j]->overflow_rvoice == fv) { fluid_voice_overflow_rvoice_finished(synth->voice[j]); break; } } } } /** * Process all waiting events in the rvoice queue. * Make sure no (other) rendering is running in parallel when * you call this function! */ void fluid_synth_process_event_queue(fluid_synth_t* synth) { fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); } /** * Process blocks (FLUID_BUFSIZE) of audio. * Must be called from renderer thread only! * @return number of blocks rendered. Might (often) return less than requested */ static int fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount) { int i, maxblocks; fluid_profile_ref_var (prof_ref); /* Assign ID of synthesis thread */ // synth->synth_thread_id = fluid_thread_get_id (); fluid_check_fpe("??? Just starting up ???"); fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); /* do not render more blocks than we can store internally */ maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer); if (blockcount > maxblocks) blockcount = maxblocks; for (i=0; i < blockcount; i++) { fluid_sample_timer_process(synth); fluid_synth_add_ticks(synth, FLUID_BUFSIZE); /* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all() * (should only happen with parallel render) stop processing and go for rendering */ if (fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) { // Something has happened, we can't process more blockcount = i+1; break; } } fluid_check_fpe("fluid_sample_timer_process"); blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount); /* Testcase, that provokes a denormal floating point error */ #if 0 {float num=1;while (num != 0){num*=0.5;};}; #endif fluid_check_fpe("??? Remainder of synth_one_block ???"); fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref); return blockcount; } static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name, fluid_real_t value) { double d; fluid_synth_api_enter(synth); fluid_settings_getnum(synth->settings, "synth.overflow.percussion", &d); synth->overflow.percussion = d; fluid_settings_getnum(synth->settings, "synth.overflow.released", &d); synth->overflow.released = d; fluid_settings_getnum(synth->settings, "synth.overflow.sustained", &d); synth->overflow.sustained = d; fluid_settings_getnum(synth->settings, "synth.overflow.volume", &d); synth->overflow.volume = d; fluid_settings_getnum(synth->settings, "synth.overflow.age", &d); synth->overflow.age = d; FLUID_API_RETURN(0); } /* Selects a voice for killing. */ static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) { int i; fluid_real_t best_prio = OVERFLOW_PRIO_CANNOT_KILL-1; fluid_real_t this_voice_prio; fluid_voice_t* voice; int best_voice_index=-1; unsigned int ticks = fluid_synth_get_ticks(synth); for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; /* safeguard against an available voice. */ if (_AVAILABLE(voice)) { return voice; } this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow, ticks); /* check if this voice has less priority than the previous candidate. */ if (this_voice_prio < best_prio) { best_voice_index = i; best_prio = this_voice_prio; } } if (best_voice_index < 0) { return NULL; } voice = synth->voice[best_voice_index]; FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ", fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice)); fluid_voice_off(voice); return voice; } /** * Allocate a synthesis voice. * @param synth FluidSynth instance * @param sample Sample to assign to the voice * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number for the voice (0-127) * @param vel MIDI velocity for the voice (0-127) * @return Allocated synthesis voice or NULL on error * * This function is called by a SoundFont's preset in response to a noteon event. * The returned voice comes with default modulators and generators. * A single noteon event may create any number of voices, when the preset is layered. * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. */ fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) { int i, k; fluid_voice_t* voice = NULL; fluid_channel_t* channel = NULL; unsigned int ticks; fluid_return_val_if_fail (sample != NULL, NULL); FLUID_API_ENTRY_CHAN(NULL); /* check if there's an available synthesis process */ for (i = 0; i < synth->polyphony; i++) { if (_AVAILABLE(synth->voice[i])) { voice = synth->voice[i]; break; } } /* No success yet? Then stop a running voice. */ if (voice == NULL) { FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice"); voice = fluid_synth_free_voice_by_kill_LOCAL(synth); } if (voice == NULL) { FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); FLUID_API_RETURN(NULL); } ticks = fluid_synth_get_ticks(synth); if (synth->verbose) { k = 0; for (i = 0; i < synth->polyphony; i++) { if (!_AVAILABLE(synth->voice[i])) { k++; } } FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", chan, key, vel, synth->storeid, (float) ticks / 44100.0f, (fluid_curtime() - synth->start) / 1000.0f, 0.0f, k); } if (chan >= 0) { channel = synth->channel[chan]; } if (fluid_voice_init (voice, sample, channel, key, vel, synth->storeid, ticks, synth->gain) != FLUID_OK) { FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); FLUID_API_RETURN(NULL); } /* add the default modulators to the synthesis process. */ fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */ fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */ fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */ fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */ fluid_voice_add_mod(voice, &default_att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.5 */ fluid_voice_add_mod(voice, &default_pan_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.6 */ fluid_voice_add_mod(voice, &default_expr_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.7 */ fluid_voice_add_mod(voice, &default_reverb_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.8 */ fluid_voice_add_mod(voice, &default_chorus_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.9 */ fluid_voice_add_mod(voice, &default_pitch_bend_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.10 */ FLUID_API_RETURN(voice); } /* Kill all voices on a given channel, which have the same exclusive class * generator as new_voice. */ static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, fluid_voice_t* new_voice) { int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS); fluid_voice_t* existing_voice; int i; /* Excl. class 0: No exclusive class */ if (excl_class == 0) return; /* Kill all notes on the same channel with the same exclusive class */ for (i = 0; i < synth->polyphony; i++) { existing_voice = synth->voice[i]; /* If voice is playing, on the same channel, has same exclusive * class and is not part of the same noteon event (voice group), then kill it */ if (fluid_voice_is_playing(existing_voice) && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice) && (int)_GEN (existing_voice, GEN_EXCLUSIVECLASS) == excl_class && fluid_voice_get_id (existing_voice) != fluid_voice_get_id(new_voice)) fluid_voice_kill_excl(existing_voice); } } /** * Activate a voice previously allocated with fluid_synth_alloc_voice(). * @param synth FluidSynth instance * @param voice Voice to activate * * This function is called by a SoundFont's preset in response to a noteon * event. Exclusive classes are processed here. * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. */ void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) { fluid_return_if_fail (synth != NULL); fluid_return_if_fail (voice != NULL); // fluid_return_if_fail (fluid_synth_is_synth_thread (synth)); fluid_synth_api_enter(synth); /* Find the exclusive class of this voice. If set, kill all voices * that match the exclusive class and are younger than the first * voice process created by this noteon event. */ fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice); fluid_voice_start(voice); /* Start the new voice */ if (synth->eventhandler->is_threadsafe) fluid_voice_lock_rvoice(voice); fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice); fluid_synth_api_exit(synth); } /** * Add a SoundFont loader to the synth. This function takes ownership of \c loader * and frees it automatically upon \c synth destruction. * @param synth FluidSynth instance * @param loader Loader API structure * * SoundFont loaders are used to add custom instrument loading to FluidSynth. * The caller supplied functions for loading files, allocating presets, * retrieving information on them and synthesizing note-on events. Using this * method even non SoundFont instruments can be synthesized, although limited * to the SoundFont synthesis model. * * @note Should only be called before any SoundFont files are loaded. */ void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) { gboolean sfont_already_loaded; fluid_return_if_fail (synth != NULL); fluid_return_if_fail (loader != NULL); fluid_synth_api_enter(synth); sfont_already_loaded = synth->sfont_info != NULL; if (!sfont_already_loaded) synth->loaders = fluid_list_prepend(synth->loaders, loader); fluid_synth_api_exit(synth); } /** * Load a SoundFont file (filename is interpreted by SoundFont loaders). * The newly loaded SoundFont will be put on top of the SoundFont * stack. Presets are searched starting from the SoundFont on the * top of the stack, working the way down the stack until a preset is found. * * @param synth FluidSynth instance * @param filename File to load * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset()) * @return SoundFont ID on success, FLUID_FAILED on error */ int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets) { fluid_sfont_info_t *sfont_info; fluid_sfont_t *sfont; fluid_list_t *list; fluid_sfloader_t *loader; unsigned int sfont_id; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (filename != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* MT NOTE: Loaders list should not change. */ for (list = synth->loaders; list; list = fluid_list_next(list)) { loader = (fluid_sfloader_t*) fluid_list_get(list); sfont = fluid_sfloader_load(loader, filename); if (sfont != NULL) { sfont_info = new_fluid_sfont_info (synth, sfont); if (!sfont_info) { delete_fluid_sfont (sfont); FLUID_API_RETURN(FLUID_FAILED); } sfont->id = sfont_id = ++synth->sfont_id; synth->sfont_info = fluid_list_prepend(synth->sfont_info, sfont_info); /* prepend to list */ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ /* reset the presets for all channels if requested */ if (reset_presets) fluid_synth_program_reset(synth); FLUID_API_RETURN((int)sfont_id); } } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); FLUID_API_RETURN(FLUID_FAILED); } /* Create a new SoundFont info structure, free with FLUID_FREE */ static fluid_sfont_info_t * new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont) { fluid_sfont_info_t *sfont_info; sfont_info = FLUID_NEW (fluid_sfont_info_t); if (!sfont_info) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } sfont_info->sfont = sfont; sfont_info->synth = synth; sfont_info->refcount = 1; /* Start with refcount of 1 for owning synth */ sfont_info->bankofs = 0; return (sfont_info); } /** * Unload a SoundFont. * @param synth FluidSynth instance * @param id ID of SoundFont to unload * @param reset_presets TRUE to re-assign presets for all MIDI channels * @return FLUID_OK on success, FLUID_FAILED on error */ int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) { fluid_sfont_info_t *sfont_info = NULL; fluid_list_t *list; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* remove the SoundFont from the list */ for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); if (fluid_sfont_get_id (sfont_info->sfont) == id) { synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); break; } } if (!list) { FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); FLUID_API_RETURN(FLUID_FAILED); } /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ if (reset_presets) fluid_synth_program_reset (synth); else fluid_synth_update_presets (synth); /* -- Remove synth->sfont_info list's reference to SoundFont */ fluid_synth_sfont_unref (synth, sfont_info->sfont); FLUID_API_RETURN(FLUID_OK); } /* Unref a SoundFont and destroy if no more references */ void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont) { fluid_sfont_info_t *sfont_info; int refcount = 0; sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont); if (sfont_info) { sfont_info->refcount--; /* -- Remove the sfont_info list's reference */ refcount = sfont_info->refcount; if (refcount == 0) /* Remove SoundFont from hash if no more references */ fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); } fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */ if (refcount == 0) /* No more references? - Attempt delete */ { if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */ { FLUID_FREE (sfont_info); FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ else new_fluid_timer (100, fluid_synth_sfunload_callback, sfont_info, TRUE, TRUE, FALSE); } } /* Callback to continually attempt to unload a SoundFont, * only if a SoundFont loader blocked the unload operation */ static int fluid_synth_sfunload_callback(void* data, unsigned int msec) { fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data; if (delete_fluid_sfont (sfont_info->sfont) == 0) { FLUID_FREE (sfont_info); FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); return FALSE; } else return TRUE; } /** * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack. * @param synth FluidSynth instance * @param id ID of SoundFont to reload * @return SoundFont ID on success, FLUID_FAILED on error */ int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) { char filename[1024]; fluid_sfont_info_t *sfont_info, *old_sfont_info; fluid_sfont_t* sfont; fluid_sfloader_t* loader; fluid_list_t *list; int index; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); /* Search for SoundFont and get its index */ for (list = synth->sfont_info, index = 0; list; list = fluid_list_next (list), index++) { old_sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); if (fluid_sfont_get_id (old_sfont_info->sfont) == id) break; } if (!list) { FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); FLUID_API_RETURN(FLUID_FAILED); } /* keep a copy of the SoundFont's filename */ FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont)); if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK) FLUID_API_RETURN(FLUID_FAILED); /* MT Note: SoundFont loader list will not change */ for (list = synth->loaders; list; list = fluid_list_next(list)) { loader = (fluid_sfloader_t*) fluid_list_get(list); sfont = fluid_sfloader_load(loader, filename); if (sfont != NULL) { sfont->id = id; sfont_info = new_fluid_sfont_info (synth, sfont); if (!sfont_info) { delete_fluid_sfont (sfont); FLUID_API_RETURN(FLUID_FAILED); } synth->sfont_info = fluid_list_insert_at(synth->sfont_info, index, sfont_info); /* insert the sfont at the same index */ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ /* reset the presets for all channels */ fluid_synth_update_presets(synth); FLUID_API_RETURN(sfont->id); } } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); FLUID_API_RETURN(FLUID_FAILED); } /** * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack. * @param synth FluidSynth instance * @param sfont SoundFont to add * @return New assigned SoundFont ID or FLUID_FAILED on error */ int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) { fluid_sfont_info_t *sfont_info; unsigned int sfont_id; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (sfont != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); sfont_info = new_fluid_sfont_info (synth, sfont); if (!sfont_info) FLUID_API_RETURN(FLUID_FAILED); sfont->id = sfont_id = ++synth->sfont_id; synth->sfont_info = fluid_list_prepend (synth->sfont_info, sfont_info); /* prepend to list */ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ /* reset the presets for all channels */ fluid_synth_program_reset (synth); FLUID_API_RETURN(sfont_id); } /** * Remove a SoundFont from the SoundFont stack without deleting it. * @param synth FluidSynth instance * @param sfont SoundFont to remove * * SoundFont is not freed and is left as the responsibility of the caller. * * @note The SoundFont should only be freed after there are no presets * referencing it. This can only be ensured by the SoundFont loader and * therefore this function should not normally be used. */ void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) { fluid_sfont_info_t *sfont_info; fluid_list_t *list; fluid_return_if_fail (synth != NULL); fluid_return_if_fail (sfont != NULL); fluid_synth_api_enter(synth); /* remove the SoundFont from the list */ for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); if (sfont_info->sfont == sfont) { synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); /* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */ fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); break; } } /* reset the presets for all channels */ fluid_synth_program_reset (synth); fluid_synth_api_exit(synth); } /** * Count number of loaded SoundFont files. * @param synth FluidSynth instance * @return Count of loaded SoundFont files. */ int fluid_synth_sfcount(fluid_synth_t* synth) { int count; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); count = fluid_list_size (synth->sfont_info); FLUID_API_RETURN(count); } /** * Get SoundFont by index. * @param synth FluidSynth instance * @param num SoundFont index on the stack (starting from 0 for top of stack). * @return SoundFont instance or NULL if invalid index * * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) { fluid_sfont_t *sfont = NULL; fluid_list_t *list; fluid_return_val_if_fail (synth != NULL, NULL); fluid_synth_api_enter(synth); list = fluid_list_nth (synth->sfont_info, num); if (list) sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; FLUID_API_RETURN(sfont); } /** * Get SoundFont by ID. * @param synth FluidSynth instance * @param id SoundFont ID * @return SoundFont instance or NULL if invalid ID * * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) { fluid_sfont_t* sfont = NULL; fluid_list_t* list; fluid_return_val_if_fail (synth != NULL, NULL); fluid_synth_api_enter(synth); for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; if (fluid_sfont_get_id (sfont) == id) break; } FLUID_API_RETURN(list ? sfont : NULL); } /** * Get SoundFont by name. * @param synth FluidSynth instance * @param name Name of SoundFont * @return SoundFont instance or NULL if invalid name * @since 1.1.0 * * @note Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. */ fluid_sfont_t * fluid_synth_get_sfont_by_name(fluid_synth_t* synth, const char *name) { fluid_sfont_t* sfont = NULL; fluid_list_t* list; fluid_return_val_if_fail (synth != NULL, NULL); fluid_return_val_if_fail (name != NULL, NULL); fluid_synth_api_enter(synth); for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) break; } FLUID_API_RETURN(list ? sfont : NULL); } /** * Get active preset on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @return Preset or NULL if no preset active on \c chan * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon methods. Not thread safe otherwise. */ fluid_preset_t * fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan) { fluid_preset_t* result; fluid_channel_t *channel; FLUID_API_ENTRY_CHAN(NULL); channel = synth->channel[chan]; result = channel->preset; fluid_synth_api_exit(synth); return result; } /** * Get information on the currently selected preset on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param info Caller supplied structure to fill with preset information * @return #FLUID_OK on success, #FLUID_FAILED otherwise * @deprecated Provides redundant functionality that can be achieved with * fluid_synth_get_channel_preset() or fluid_synth_get_program(). * @since 1.1.1 */ int fluid_synth_get_channel_info (fluid_synth_t *synth, int chan, fluid_synth_channel_info_t *info) { fluid_channel_t *channel; fluid_preset_t *preset; char *name; if (info) { info->assigned = FALSE; info->name[0] = '\0'; } fluid_return_val_if_fail (info != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; preset = channel->preset; if (preset) { info->assigned = TRUE; name = fluid_preset_get_name (preset); if (name) { strncpy (info->name, name, FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE); info->name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE - 1] = '\0'; } else info->name[0] = '\0'; info->sfont_id = preset->sfont->id; info->bank = fluid_preset_get_banknum (preset); info->program = fluid_preset_get_num (preset); } else { info->assigned = FALSE; fluid_channel_get_sfont_bank_prog (channel, &info->sfont_id, &info->bank, &info->program); info->name[0] = '\0'; } fluid_synth_api_exit(synth); return FLUID_OK; } /** * Get list of currently playing voices. * @param synth FluidSynth instance * @param buf Array to store voices to (NULL terminated if not filled completely) * @param bufsize Count of indexes in buf * @param id Voice ID to search for or < 0 to return list of all playing voices * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon methods. Voices are only guaranteed to remain * unchanged until next synthesis process iteration. */ void fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int id) { int count = 0; int i; fluid_return_if_fail (synth != NULL); fluid_return_if_fail (buf != NULL); fluid_synth_api_enter(synth); for (i = 0; i < synth->polyphony && count < bufsize; i++) { fluid_voice_t* voice = synth->voice[i]; if (fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id)) buf[count++] = voice; } if (count < bufsize) buf[count] = NULL; fluid_synth_api_exit(synth); } /** * Enable or disable reverb effect. * @param synth FluidSynth instance * @param on TRUE to enable reverb, FALSE to disable */ void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) { fluid_return_if_fail (synth != NULL); fluid_atomic_int_set (&synth->with_reverb, on != 0); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, on != 0, 0.0f); } /** * Activate a reverb preset. * @param synth FluidSynth instance * @param num Reverb preset number * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Currently private to libfluidsynth. */ int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) { int i = 0; while (revmodel_preset[i].name != NULL) { if (i == num) { fluid_synth_set_reverb (synth, revmodel_preset[i].roomsize, revmodel_preset[i].damp, revmodel_preset[i].width, revmodel_preset[i].level); return FLUID_OK; } i++; } return FLUID_FAILED; } /** * Set reverb parameters. * @param synth FluidSynth instance * @param roomsize Reverb room size value (0.0-1.2) * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) * * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. */ void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, double width, double level) { fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ALL, roomsize, damping, width, level); } /** * Set one or more reverb parameters. * @param synth FluidSynth instance * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) * @param roomsize Reverb room size value (0.0-1.2) * @param damping Reverb damping value (0.0-1.0) * @param width Reverb width value (0.0-100.0) * @param level Reverb level value (0.0-1.0) * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Not realtime safe and therefore should not be called from synthesis * context at the risk of stalling audio output. */ int fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, double damping, double width, double level) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); if (!(set & FLUID_REVMODEL_SET_ALL)) set = FLUID_REVMODEL_SET_ALL; /* Synth shadow values are set here so that they will be returned if querried */ fluid_synth_api_enter(synth); if (set & FLUID_REVMODEL_SET_ROOMSIZE) fluid_atomic_float_set (&synth->reverb_roomsize, roomsize); if (set & FLUID_REVMODEL_SET_DAMPING) fluid_atomic_float_set (&synth->reverb_damping, damping); if (set & FLUID_REVMODEL_SET_WIDTH) fluid_atomic_float_set (&synth->reverb_width, width); if (set & FLUID_REVMODEL_SET_LEVEL) fluid_atomic_float_set (&synth->reverb_level, level); fluid_rvoice_eventhandler_push5(synth->eventhandler, fluid_rvoice_mixer_set_reverb_params, synth->eventhandler->mixer, set, roomsize, damping, width, level, 0.0f); FLUID_API_RETURN(FLUID_OK); } /** * Get reverb room size. * @param synth FluidSynth instance * @return Reverb room size (0.0-1.2) */ double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->reverb_roomsize); FLUID_API_RETURN(result); } /** * Get reverb damping. * @param synth FluidSynth instance * @return Reverb damping value (0.0-1.0) */ double fluid_synth_get_reverb_damp(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->reverb_damping); FLUID_API_RETURN(result); } /** * Get reverb level. * @param synth FluidSynth instance * @return Reverb level value (0.0-1.0) */ double fluid_synth_get_reverb_level(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->reverb_level); FLUID_API_RETURN(result); } /** * Get reverb width. * @param synth FluidSynth instance * @return Reverb width value (0.0-100.0) */ double fluid_synth_get_reverb_width(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->reverb_width); FLUID_API_RETURN(result); } /** * Enable or disable chorus effect. * @param synth FluidSynth instance * @param on TRUE to enable chorus, FALSE to disable */ void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) { fluid_return_if_fail (synth != NULL); fluid_synth_api_enter(synth); fluid_atomic_int_set (&synth->with_chorus, on != 0); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled, on != 0, 0.0f); fluid_synth_api_exit(synth); } /** * Set chorus parameters. * @param synth FluidSynth instance * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) * @param level Chorus level (0.0-10.0) * @param speed Chorus speed in Hz (0.29-5.0) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) */ void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, double speed, double depth_ms, int type) { fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed, depth_ms, type); } /** * Set one or more chorus parameters. * @param synth FluidSynth instance * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t) * @param nr Chorus voice count (0-99, CPU time consumption proportional to * this value) * @param level Chorus level (0.0-10.0) * @param speed Chorus speed in Hz (0.29-5.0) * @param depth_ms Chorus depth (max value depends on synth sample rate, * 0.0-21.0 is safe for sample rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) */ int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, double speed, double depth_ms, int type) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); if (!(set & FLUID_CHORUS_SET_ALL)) set = FLUID_CHORUS_SET_ALL; /* Synth shadow values are set here so that they will be returned if queried */ fluid_synth_api_enter(synth); if (set & FLUID_CHORUS_SET_NR) fluid_atomic_int_set (&synth->chorus_nr, nr); if (set & FLUID_CHORUS_SET_LEVEL) fluid_atomic_float_set (&synth->chorus_level, level); if (set & FLUID_CHORUS_SET_SPEED) fluid_atomic_float_set (&synth->chorus_speed, speed); if (set & FLUID_CHORUS_SET_DEPTH) fluid_atomic_float_set (&synth->chorus_depth, depth_ms); if (set & FLUID_CHORUS_SET_TYPE) fluid_atomic_int_set (&synth->chorus_type, type); fluid_rvoice_eventhandler_push5(synth->eventhandler, fluid_rvoice_mixer_set_chorus_params, synth->eventhandler->mixer, set, nr, level, speed, depth_ms, type); FLUID_API_RETURN(FLUID_OK); } /** * Get chorus voice number (delay line count) value. * @param synth FluidSynth instance * @return Chorus voice count (0-99) */ int fluid_synth_get_chorus_nr(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_int_get (&synth->chorus_nr); FLUID_API_RETURN(result); } /** * Get chorus level. * @param synth FluidSynth instance * @return Chorus level value (0.0-10.0) */ double fluid_synth_get_chorus_level(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->chorus_level); FLUID_API_RETURN(result); } /** * Get chorus speed in Hz. * @param synth FluidSynth instance * @return Chorus speed in Hz (0.29-5.0) */ double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->chorus_speed); FLUID_API_RETURN(result); } /** * Get chorus depth. * @param synth FluidSynth instance * @return Chorus depth */ double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_float_get (&synth->chorus_depth); FLUID_API_RETURN(result); } /** * Get chorus waveform type. * @param synth FluidSynth instance * @return Chorus waveform type (#fluid_chorus_mod) */ int fluid_synth_get_chorus_type(fluid_synth_t* synth) { double result; fluid_return_val_if_fail (synth != NULL, 0.0); fluid_synth_api_enter(synth); result = fluid_atomic_int_get (&synth->chorus_type); FLUID_API_RETURN(result); } /* * If the same note is hit twice on the same channel, then the older * voice process is advanced to the release stage. Using a mechanical * MIDI controller, the only way this can happen is when the sustain * pedal is held. In this case the behaviour implemented here is * natural for many instruments. Note: One noteon event can trigger * several voice processes, for example a stereo sample. Don't * release those... */ static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key) { int i; fluid_voice_t* voice; synth->storeid = synth->noteid++; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_playing(voice) && (fluid_voice_get_channel(voice) == chan) && (fluid_voice_get_key(voice) == key) && (fluid_voice_get_id(voice) != synth->noteid)) { /* Id of voices that was sustained by sostenuto */ if(fluid_voice_is_sostenuto(voice)) synth->storeid = fluid_voice_get_id(voice); /* Force the voice into release stage (pedaling is ignored) */ fluid_voice_release(voice); } } } /** * Set synthesis interpolation method on one or all MIDI channels. * @param synth FluidSynth instance * @param chan MIDI channel to set interpolation method on or -1 for all channels * @param interp_method Interpolation method (#fluid_interp) * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method) { int i; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); if (chan < -1 || chan >= synth->midi_channels) FLUID_API_RETURN(FLUID_FAILED); if (synth->channel[0] == NULL) { FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!"); FLUID_API_RETURN(FLUID_FAILED); } for (i = 0; i < synth->midi_channels; i++) { if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) fluid_channel_set_interp_method(synth->channel[i], interp_method); } FLUID_API_RETURN(FLUID_OK); }; /** * Get the total count of MIDI channels. * @param synth FluidSynth instance * @return Count of MIDI channels */ int fluid_synth_count_midi_channels(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); result = synth->midi_channels; FLUID_API_RETURN(result); } /** * Get the total count of audio channels. * @param synth FluidSynth instance * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc) */ int fluid_synth_count_audio_channels(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); result = synth->audio_channels; FLUID_API_RETURN(result); } /** * Get the total number of allocated audio channels. Usually identical to the * number of audio channels. Can be employed by LADSPA effects subsystem. * * @param synth FluidSynth instance * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc) */ int fluid_synth_count_audio_groups(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); result = synth->audio_groups; FLUID_API_RETURN(result); } /** * Get the total number of allocated effects channels. * @param synth FluidSynth instance * @return Count of allocated effects channels */ int fluid_synth_count_effects_channels(fluid_synth_t* synth) { int result; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); result = synth->effects_channels; FLUID_API_RETURN(result); } /** * Get the synth CPU load value. * @param synth FluidSynth instance * @return Estimated CPU load value in percent (0-100) */ double fluid_synth_get_cpu_load(fluid_synth_t* synth) { fluid_return_val_if_fail (synth != NULL, 0); return fluid_atomic_float_get (&synth->cpu_load); } /* Get tuning for a given bank:program */ static fluid_tuning_t * fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) { if ((synth->tuning == NULL) || (synth->tuning[bank] == NULL) || (synth->tuning[bank][prog] == NULL)) return NULL; return synth->tuning[bank][prog]; } /* Replace tuning on a given bank:program (need not already exist). * Synth mutex should already be locked by caller. */ static int fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, fluid_tuning_t *tuning, int bank, int prog, int apply) { fluid_tuning_t *old_tuning; if (synth->tuning == NULL) { synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); if (synth->tuning == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**)); } if (synth->tuning[bank] == NULL) { synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128); if (synth->tuning[bank] == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*)); } old_tuning = synth->tuning[bank][prog]; synth->tuning[bank][prog] = tuning; if (old_tuning) { if (!fluid_tuning_unref (old_tuning, 1)) /* -- unref old tuning */ { /* Replace old tuning if present */ fluid_synth_replace_tuning_LOCAL (synth, old_tuning, tuning, apply, FALSE); } } return FLUID_OK; } /* Replace a tuning with a new one in all MIDI channels. new_tuning can be * NULL, in which case channels are reset to default equal tempered scale. */ static void fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, fluid_tuning_t *old_tuning, fluid_tuning_t *new_tuning, int apply, int unref_new) { fluid_channel_t *channel; int old_tuning_unref = 0; int i; for (i = 0; i < synth->midi_channels; i++) { channel = synth->channel[i]; if (fluid_channel_get_tuning (channel) == old_tuning) { old_tuning_unref++; if (new_tuning) fluid_tuning_ref (new_tuning); /* ++ ref new tuning for channel */ fluid_channel_set_tuning (channel, new_tuning); if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); } } /* Send unref old tuning event if any unrefs */ if (old_tuning && old_tuning_unref) fluid_tuning_unref (old_tuning, old_tuning_unref); if (!unref_new || !new_tuning) return; fluid_tuning_unref (new_tuning, 1); } /* Update voice tunings in realtime */ static void fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *channel) { fluid_voice_t *voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_on(voice) && (voice->channel == channel)) { fluid_voice_calculate_gen_pitch (voice); fluid_voice_update_param (voice, GEN_PITCH); } } } /** * Set the tuning of the entire MIDI note scale. * @param synth FluidSynth instance * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 128, each value is number of * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). * Pass NULL to create a equal tempered (normal) scale. * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Tuning is not applied in realtime to existing notes of the replaced * tuning (if any), use fluid_synth_activate_key_tuning() instead to specify * this behavior. * * @deprecated Use fluid_synth_activate_key_tuning(synth, bank, prog, name, pitch, FALSE) instead. */ int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch) { return fluid_synth_activate_key_tuning (synth, bank, prog, name, pitch, FALSE); } /** * Set the tuning of the entire MIDI note scale. * @param synth FluidSynth instance * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 128, each value is number of * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). * Pass NULL to create a equal tempered (normal) scale. * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch, int apply) { fluid_tuning_t* tuning; int retval = FLUID_OK; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); tuning = new_fluid_tuning (name, bank, prog); if (tuning) { if (pitch) fluid_tuning_set_all (tuning, pitch); retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply); if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); } else retval = FLUID_FAILED; FLUID_API_RETURN(retval); } /** * Apply an octave tuning to every octave in the MIDI note scale. * @param synth FluidSynth instance * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 12 for each note of an octave * starting at note C, values are number of offset cents to add to the normal * tuning amount) * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Tuning is not applied in realtime to existing notes of the replaced * tuning (if any), use fluid_synth_activate_octave_tuning() instead to specify * this behavior. * * @deprecated Use fluid_synth_activate_octave_tuning(synth, bank, prog, name, pitch, FALSE) instead. */ int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch) { return fluid_synth_activate_octave_tuning (synth, bank, prog, name, pitch, FALSE); } /** * Activate an octave tuning on every octave in the MIDI note scale. * @param synth FluidSynth instance * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param name Label name for this tuning * @param pitch Array of pitch values (length of 12 for each note of an octave * starting at note C, values are number of offset cents to add to the normal * tuning amount) * @param apply TRUE to apply new tuning in realtime to existing notes which * are using the replaced tuning (if any), FALSE otherwise * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog, const char* name, const double* pitch, int apply) { fluid_tuning_t* tuning; int retval = FLUID_OK; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); tuning = new_fluid_tuning (name, bank, prog); if (tuning) { fluid_tuning_set_octave (tuning, pitch); retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply); if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1); } else retval = FLUID_FAILED; FLUID_API_RETURN(retval); } /** * Set tuning values for one or more MIDI notes for an existing tuning. * @param synth FluidSynth instance * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param len Number of MIDI notes to assign * @param key Array of MIDI key numbers (length of 'len', values 0-127) * @param pitch Array of pitch values (length of 'len', values are number of * cents from MIDI note 0) * @param apply TRUE to apply tuning change in realtime to existing notes using * the specified tuning, FALSE otherwise * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Prior to version 1.1.0 it was an error to specify a tuning that didn't * already exist. Starting with 1.1.0, the default equal tempered scale will be * used as a basis, if no tuning exists for the given bank and prog. */ int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, int len, const int *key, const double* pitch, int apply) { fluid_tuning_t* old_tuning, *new_tuning; int retval = FLUID_OK; int i; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); fluid_return_val_if_fail (len > 0, FLUID_FAILED); fluid_return_val_if_fail (key != NULL, FLUID_FAILED); fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); old_tuning = fluid_synth_get_tuning (synth, bank, prog); if (old_tuning) new_tuning = fluid_tuning_duplicate (old_tuning); else new_tuning = new_fluid_tuning ("Unnamed", bank, prog); if (new_tuning) { for (i = 0; i < len; i++) fluid_tuning_set_pitch (new_tuning, key[i], pitch[i]); retval = fluid_synth_replace_tuning_LOCK (synth, new_tuning, bank, prog, apply); if (retval == FLUID_FAILED) fluid_tuning_unref (new_tuning, 1); } else retval = FLUID_FAILED; FLUID_API_RETURN(retval); } /** * Select a tuning scale on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note This function does NOT activate tuning in realtime, use * fluid_synth_activate_tuning() instead to specify whether tuning change * should cause existing notes to update. * * @note Prior to version 1.1.0 it was an error to select a tuning that didn't * already exist. Starting with 1.1.0, a default equal tempered scale will be * created, if no tuning exists for the given bank and prog. * * @deprecated Use fluid_synth_activate_tuning(synth, chan, bank, prog, FALSE) instead. */ int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) { return fluid_synth_activate_tuning (synth, chan, bank, prog, FALSE); } /** * Activate a tuning scale on a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param bank Tuning bank number (0-127), not related to MIDI instrument bank * @param prog Tuning preset number (0-127), not related to MIDI instrument program * @param apply TRUE to apply tuning change to active notes, FALSE otherwise * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 * * @note A default equal tempered scale will be created, if no tuning exists * on the given bank and prog. */ int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, int apply) { fluid_tuning_t* tuning; int retval = FLUID_OK; //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED); fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); tuning = fluid_synth_get_tuning (synth, bank, prog); /* If no tuning exists, create a new default tuning. We do this, so that * it can be replaced later, if any changes are made. */ if (!tuning) { tuning = new_fluid_tuning ("Unnamed", bank, prog); if (tuning) fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, FALSE); } if (tuning) fluid_tuning_ref (tuning); /* ++ ref for outside of lock */ if (!tuning) FLUID_API_RETURN(FLUID_FAILED); fluid_tuning_ref (tuning); /* ++ ref new tuning for following function */ retval = fluid_synth_set_tuning_LOCAL (synth, chan, tuning, apply); fluid_tuning_unref (tuning, 1); /* -- unref for outside of lock */ FLUID_API_RETURN(retval); } /* Local synthesis thread set tuning function (takes over tuning reference) */ static int fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan, fluid_tuning_t *tuning, int apply) { fluid_tuning_t *old_tuning; fluid_channel_t *channel; channel = synth->channel[chan]; old_tuning = fluid_channel_get_tuning (channel); fluid_channel_set_tuning (channel, tuning); /* !! Takes over callers reference */ if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel); /* Send unref old tuning event */ if (old_tuning) { fluid_tuning_unref (old_tuning, 1); } return FLUID_OK; } /** * Clear tuning scale on a MIDI channel (set it to the default equal tempered scale). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note This function does NOT activate tuning change in realtime, use * fluid_synth_deactivate_tuning() instead to specify whether tuning change * should cause existing notes to update. * * @deprecated Use fluid_synth_deactivate_tuning(synth, chan, FALSE) instead. */ int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) { return fluid_synth_deactivate_tuning (synth, chan, FALSE); } /** * Clear tuning scale on a MIDI channel (use default equal tempered scale). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param apply TRUE to apply tuning change to active notes, FALSE otherwise * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 */ int fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply) { int retval = FLUID_OK; FLUID_API_ENTRY_CHAN(FLUID_FAILED); retval = fluid_synth_set_tuning_LOCAL (synth, chan, NULL, apply); FLUID_API_RETURN(retval); } /** * Start tuning iteration. * @param synth FluidSynth instance */ void fluid_synth_tuning_iteration_start(fluid_synth_t* synth) { fluid_return_if_fail (synth != NULL); fluid_synth_api_enter(synth); fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER (0)); fluid_synth_api_exit(synth); } /** * Advance to next tuning. * @param synth FluidSynth instance * @param bank Location to store MIDI bank number of next tuning scale * @param prog Location to store MIDI program number of next tuning scale * @return 1 if tuning iteration advanced, 0 if no more tunings */ int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) { void *pval; int b = 0, p = 0; fluid_return_val_if_fail (synth != NULL, 0); fluid_return_val_if_fail (bank != NULL, 0); fluid_return_val_if_fail (prog != NULL, 0); fluid_synth_api_enter(synth); /* Current tuning iteration stored as: bank << 8 | program */ pval = fluid_private_get (synth->tuning_iter); p = FLUID_POINTER_TO_INT (pval); b = (p >> 8) & 0xFF; p &= 0xFF; if (!synth->tuning) { FLUID_API_RETURN(0); } for (; b < 128; b++, p = 0) { if (synth->tuning[b] == NULL) continue; for (; p < 128; p++) { if (synth->tuning[b][p] == NULL) continue; *bank = b; *prog = p; if (p < 127) fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER (b << 8 | (p + 1))); else fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER ((b + 1) << 8)); FLUID_API_RETURN(1); } } FLUID_API_RETURN(0); } /** * Get the entire note tuning for a given MIDI bank and program. * @param synth FluidSynth instance * @param bank MIDI bank number of tuning * @param prog MIDI program number of tuning * @param name Location to store tuning name or NULL to ignore * @param len Maximum number of chars to store to 'name' (including NULL byte) * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) * @return FLUID_OK if matching tuning was found, FLUID_FAILED otherwise */ int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, char* name, int len, double* pitch) { fluid_tuning_t* tuning; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); tuning = fluid_synth_get_tuning (synth, bank, prog); if (tuning) { if (name) { snprintf (name, len - 1, "%s", fluid_tuning_get_name (tuning)); name[len - 1] = 0; /* make sure the string is null terminated */ } if (pitch) FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double)); } FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED); } /** * Get settings assigned to a synth. * @param synth FluidSynth instance * @return FluidSynth settings which are assigned to the synth */ fluid_settings_t * fluid_synth_get_settings(fluid_synth_t* synth) { fluid_return_val_if_fail (synth != NULL, NULL); return synth->settings; } /** * Convenience function to set a string setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param str Value to assign to the setting * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_setstr() in combination with fluid_synth_get_settings() instead. */ int fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); return fluid_settings_setstr(synth->settings, name, str); } /** * Convenience function to duplicate a string setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param str Location to store a pointer to the newly allocated string value * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_dupstr() in combination with fluid_synth_get_settings() instead. * * The returned string is owned by the caller and should be freed with free() * when finished with it. */ int fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); fluid_return_val_if_fail (str != NULL, FLUID_FAILED); return fluid_settings_dupstr(synth->settings, name, str); } /** * Convenience function to set a floating point setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param val Value to assign to the setting * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_setnum() in combination with fluid_synth_get_settings() instead. */ int fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); return fluid_settings_setnum(synth->settings, name, val); } /** * Convenience function to get a floating point setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param val Location to store the current value of the setting * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_getnum() in combination with fluid_synth_get_settings() instead. */ int fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); return fluid_settings_getnum(synth->settings, name, val); } /** * Convenience function to set an integer setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param val Value to assign to the setting * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_setint() in combination with fluid_synth_get_settings() instead. */ int fluid_synth_setint(fluid_synth_t* synth, const char* name, int val) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); return fluid_settings_setint(synth->settings, name, val); } /** * Convenience function to get an integer setting of a synth. * @param synth FluidSynth instance * @param name Name of setting parameter * @param val Location to store the current value of the setting * @return FLUID_OK on success, FLUID_FAILED otherwise * @deprecated Use fluid_settings_getint() in combination with fluid_synth_get_settings() instead. */ int fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val) { fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_return_val_if_fail (name != NULL, FLUID_FAILED); return fluid_settings_getint(synth->settings, name, val); } /** * Set a SoundFont generator (effect) value on a MIDI channel in real-time. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param param SoundFont generator ID (#fluid_gen_type) * @param value Offset generator value to assign to the MIDI channel * @return FLUID_OK on success, FLUID_FAILED otherwise * * Parameter numbers and ranges are described in the SoundFont 2.01 * specification PDF, paragraph 8.1.3, page 48. See #fluid_gen_type. */ int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) { fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE); FLUID_API_RETURN(FLUID_OK); } /* Synthesis thread local set gen function */ static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value, int absolute) { fluid_voice_t* voice; int i; fluid_channel_set_gen (synth->channel[chan], param, value, absolute); for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_get_channel(voice) == chan) fluid_voice_set_param (voice, param, value, absolute); } } /** * Set a SoundFont generator (effect) value on a MIDI channel in real-time. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param param SoundFont generator ID (#fluid_gen_type) * @param value Offset or absolute generator value to assign to the MIDI channel * @param absolute 0 to assign a relative value, non-zero to assign an absolute value * @param normalized 0 if value is specified in the native units of the generator, * non-zero to take the value as a 0.0-1.0 range and apply it to the valid * generator effect range (scaled and shifted as necessary). * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.0 * * @deprecated As of 1.1.9 this function is deprecated, because there doesn't seem * to be a use-case for it. * * This function allows for setting all effect parameters in real time on a * MIDI channel. Setting absolute to non-zero will cause the value to override * any generator values set in the instruments played on the MIDI channel. * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont * generator parameters and valid ranges. */ int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, float value, int absolute, int normalized) { float v; fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); v = normalized ? fluid_gen_scale(param, value) : value; fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute); FLUID_API_RETURN(FLUID_OK); } /** * Get generator value assigned to a MIDI channel. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param param SoundFont generator ID (#fluid_gen_type) * @return Current generator value assigned to MIDI channel */ float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) { float result; fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); result = fluid_channel_get_gen(synth->channel[chan], param); FLUID_API_RETURN(result); } /** * Assign a MIDI router to a synth. * @param synth FluidSynth instance * @param router MIDI router to assign to the synth * * @note This should only be done once and prior to using the synth. * @deprecated This function is only used by shell command handler, which will be refactored in a future release. */ void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router) { fluid_return_if_fail (synth != NULL); fluid_synth_api_enter(synth); synth->midi_router = router; fluid_synth_api_exit(synth); }; /** * Handle MIDI event from MIDI router, used as a callback function. * @param data FluidSynth instance * @param event MIDI event to handle * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) { fluid_synth_t* synth = (fluid_synth_t*) data; int type = fluid_midi_event_get_type(event); int chan = fluid_midi_event_get_channel(event); switch(type) { case NOTE_ON: return fluid_synth_noteon(synth, chan, fluid_midi_event_get_key(event), fluid_midi_event_get_velocity(event)); case NOTE_OFF: return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event)); case CONTROL_CHANGE: return fluid_synth_cc(synth, chan, fluid_midi_event_get_control(event), fluid_midi_event_get_value(event)); case PROGRAM_CHANGE: return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event)); case CHANNEL_PRESSURE: return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); case PITCH_BEND: return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event)); case MIDI_SYSTEM_RESET: return fluid_synth_system_reset(synth); case MIDI_SYSEX: return fluid_synth_sysex (synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE); } return FLUID_FAILED; } /** * Create and start voices using a preset and a MIDI note on event. * @param synth FluidSynth instance * @param id Voice group ID to use (can be used with fluid_synth_stop()). * @param preset Preset to synthesize * @param audio_chan Unused currently, set to 0 * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number (0-127) * @param vel MIDI velocity number (1-127) * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note Should only be called from within synthesis thread, which includes * SoundFont loader preset noteon method. */ int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, int audio_chan, int chan, int key, int vel) { int result; fluid_return_val_if_fail (preset != NULL, FLUID_FAILED); fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); fluid_return_val_if_fail (vel >= 1 && vel <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); synth->storeid = id; result = fluid_preset_noteon (preset, synth, chan, key, vel); FLUID_API_RETURN(result); } /** * Stop notes for a given note event voice ID. * @param synth FluidSynth instance * @param id Voice note event ID * @return FLUID_OK on success, FLUID_FAILED otherwise * * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned * if no matching voice note event ID was found. Versions after 1.1.0 only * return #FLUID_FAILED if an error occurs. */ int fluid_synth_stop(fluid_synth_t* synth, unsigned int id) { int result; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); fluid_synth_stop_LOCAL (synth, id); result = FLUID_OK; FLUID_API_RETURN(result); } /* Local synthesis thread variant of fluid_synth_stop */ static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id) { fluid_voice_t* voice; int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (fluid_voice_is_on(voice) && (fluid_voice_get_id (voice) == id)) fluid_voice_noteoff(voice); } } /** * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract * \c offset from any bank number when assigning instruments. * * @param synth FluidSynth instance * @param sfont_id ID of a loaded SoundFont * @param offset Bank offset value to apply to all instruments */ int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) { fluid_sfont_info_t *sfont_info; fluid_list_t *list; fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) { sfont_info->bankofs = offset; break; } } if (!list) { FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); FLUID_API_RETURN(FLUID_FAILED); } FLUID_API_RETURN(FLUID_OK); } /** * Get bank offset of a loaded SoundFont. * @param synth FluidSynth instance * @param sfont_id ID of a loaded SoundFont * @return SoundFont bank offset value */ int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) { fluid_sfont_info_t *sfont_info; fluid_list_t *list; int offset = 0; fluid_return_val_if_fail (synth != NULL, 0); fluid_synth_api_enter(synth); for (list = synth->sfont_info; list; list = fluid_list_next(list)) { sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) { offset = sfont_info->bankofs; break; } } if (!list) { FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); FLUID_API_RETURN(0); } FLUID_API_RETURN(offset); } void fluid_synth_api_enter(fluid_synth_t* synth) { if (synth->use_mutex) { fluid_rec_mutex_lock(synth->mutex); } if (!synth->public_api_count) { fluid_synth_check_finished_voices(synth); } synth->public_api_count++; } void fluid_synth_api_exit(fluid_synth_t* synth) { synth->public_api_count--; if (!synth->public_api_count) { fluid_rvoice_eventhandler_flush(synth->eventhandler); } if (synth->use_mutex) { fluid_rec_mutex_unlock(synth->mutex); } } /** * Set midi channel type * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param type MIDI channel type (#fluid_midi_channel_type) * @return FLUID_OK on success, FLUID_FAILED otherwise * @since 1.1.4 */ int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type) { fluid_return_val_if_fail ((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); synth->channel[chan]->channel_type = type; FLUID_API_RETURN(FLUID_OK); } fluidsynth-1.1.9/src/synth/fluid_synth.h000066400000000000000000000225251322272076000203570ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_SYNTH_H #define _FLUID_SYNTH_H /*************************************************************** * * INCLUDES */ #if HAVE_CONFIG_H #include "config.h" #endif #include "fluidsynth_priv.h" #include "fluid_list.h" #include "fluid_rev.h" #include "fluid_voice.h" #include "fluid_chorus.h" #include "fluid_ladspa.h" #include "fluid_midi_router.h" #include "fluid_sys.h" #include "fluid_rvoice_event.h" /*************************************************************** * * DEFINES */ #define FLUID_NUM_PROGRAMS 128 #define DRUM_INST_BANK 128 #define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */ /*************************************************************** * * ENUM */ /** * Bank Select MIDI message styles. Default style is GS. */ enum fluid_midi_bank_select { FLUID_BANK_STYLE_GM, /**< GM style, bank = 0 always (CC0/MSB and CC32/LSB ignored) */ FLUID_BANK_STYLE_GS, /**< GS style, bank = CC0/MSB (CC32/LSB ignored) */ FLUID_BANK_STYLE_XG, /**< XG style, bank = CC32/LSB (CC0/MSB ignored) */ FLUID_BANK_STYLE_MMA /**< MMA style bank = 128*MSB+LSB */ }; enum fluid_synth_status { FLUID_SYNTH_CLEAN, FLUID_SYNTH_PLAYING, FLUID_SYNTH_QUIET, FLUID_SYNTH_STOPPED }; #define SYNTH_REVERB_CHANNEL 0 #define SYNTH_CHORUS_CHANNEL 1 /* * Structure used for sfont_info field in #fluid_synth_t for each loaded * SoundFont with the SoundFont instance and additional fields. */ typedef struct _fluid_sfont_info_t { fluid_sfont_t *sfont; /**< Loaded SoundFont */ fluid_synth_t *synth; /**< Parent synth */ int refcount; /**< SoundFont reference count (0 if no presets referencing it) */ int bankofs; /**< Bank offset */ } fluid_sfont_info_t; /* * fluid_synth_t * * Mutual exclusion notes (as of 1.1.2): * * All variables are considered belongning to the "public API" thread, * which processes all MIDI, except for: * * ticks_since_start - atomic, set by rendering thread only * cpu_load - atomic, set by rendering thread only * cur, curmax, dither_index - used by rendering thread only * LADSPA_FxUnit - same instance copied in rendering thread. Synchronising handled internally (I think...?). * */ struct _fluid_synth_t { fluid_rec_mutex_t mutex; /**< Lock for public API */ int use_mutex; /**< Use mutex for all public API functions? */ int public_api_count; /**< How many times the mutex is currently locked */ fluid_settings_t* settings; /**< the synthesizer settings */ int device_id; /**< Device ID used for SYSEX messages */ int polyphony; /**< Maximum polyphony */ int with_reverb; /**< Should the synth use the built-in reverb unit? */ int with_chorus; /**< Should the synth use the built-in chorus unit? */ int verbose; /**< Turn verbose mode on? */ int dump; /**< Dump events to stdout to hook up a user interface? */ double sample_rate; /**< The sample rate */ int midi_channels; /**< the number of MIDI channels (>= 16) */ int bank_select; /**< the style of Bank Select MIDI messages */ int audio_channels; /**< the number of audio channels (1 channel=left+right) */ int audio_groups; /**< the number of (stereo) 'sub'groups from the synth. Typically equal to audio_channels. */ int effects_channels; /**< the number of effects channels (>= 2) */ int state; /**< the synthesizer state */ unsigned int ticks_since_start; /**< the number of audio samples since the start */ unsigned int start; /**< the start in msec, as returned by system clock */ fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */ fluid_list_t *loaders; /**< the SoundFont loaders */ fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ fluid_hashtable_t *sfont_hash; /**< Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */ unsigned int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ float gain; /**< master gain */ fluid_channel_t** channel; /**< the channels */ int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ fluid_voice_t** voice; /**< the synthesis voices */ int active_voice_count; /**< count of active voices */ unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ unsigned int storeid; fluid_rvoice_eventhandler_t* eventhandler; float reverb_roomsize; /**< Shadow of reverb roomsize */ float reverb_damping; /**< Shadow of reverb damping */ float reverb_width; /**< Shadow of reverb width */ float reverb_level; /**< Shadow of reverb level */ int chorus_nr; /**< Shadow of chorus number */ float chorus_level; /**< Shadow of chorus level */ float chorus_speed; /**< Shadow of chorus speed */ float chorus_depth; /**< Shadow of chorus depth */ int chorus_type; /**< Shadow of chorus type */ int cur; /**< the current sample in the audio buffers to be output */ int curmax; /**< current amount of samples present in the audio buffers */ int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ float cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ fluid_tuning_t*** tuning; /**< 128 banks of 128 programs for the tunings */ fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ fluid_midi_router_t* midi_router; /**< The midi router. Could be done nicer. */ fluid_sample_timer_t* sample_timers; /**< List of timers triggered before a block is processed */ unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ int cores; /**< Number of CPU cores (1 by default) */ #ifdef LADSPA fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */ #endif }; int fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str); int fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str); int fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val); int fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val); int fluid_synth_setint(fluid_synth_t* synth, const char* name, int val); int fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val); fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, unsigned int prognum); void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont); int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); void fluid_synth_print_voice(fluid_synth_t* synth); void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, void* lout, int loff, int lincr, void* rout, int roff, int rincr); int fluid_synth_reset_reverb(fluid_synth_t* synth); int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); int fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, double damping, double width, double level); int fluid_synth_reset_chorus(fluid_synth_t* synth); int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, double speed, double depth_ms, int type); fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data); int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer); void fluid_synth_api_enter(fluid_synth_t* synth); void fluid_synth_api_exit(fluid_synth_t* synth); void fluid_synth_process_event_queue(fluid_synth_t* synth); /* * misc */ void fluid_synth_settings(fluid_settings_t* settings); #endif /* _FLUID_SYNTH_H */ fluidsynth-1.1.9/src/synth/fluid_tuning.c000066400000000000000000000077011322272076000205100ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_tuning.h" #include "fluidsynth_priv.h" #include "fluid_sys.h" fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog) { fluid_tuning_t* tuning; int i; tuning = FLUID_NEW(fluid_tuning_t); if (tuning == NULL) { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } tuning->name = NULL; if (name != NULL) { tuning->name = FLUID_STRDUP(name); } tuning->bank = bank; tuning->prog = prog; for (i = 0; i < 128; i++) { tuning->pitch[i] = i * 100.0; } tuning->refcount = 1; /* Start with a refcount of 1 */ return tuning; } /* Duplicate a tuning */ fluid_tuning_t * fluid_tuning_duplicate (fluid_tuning_t *tuning) { fluid_tuning_t *new_tuning; int i; new_tuning = FLUID_NEW (fluid_tuning_t); if (!new_tuning) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } if (tuning->name) { new_tuning->name = FLUID_STRDUP (tuning->name); if (!new_tuning->name) { FLUID_FREE (new_tuning); FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } } else new_tuning->name = NULL; new_tuning->bank = tuning->bank; new_tuning->prog = tuning->prog; for (i = 0; i < 128; i++) new_tuning->pitch[i] = tuning->pitch[i]; new_tuning->refcount = 1; /* Start with a refcount of 1 */ return new_tuning; } void delete_fluid_tuning (fluid_tuning_t *tuning) { if (tuning->name) FLUID_FREE (tuning->name); FLUID_FREE (tuning); } /* Add a reference to a tuning object */ void fluid_tuning_ref (fluid_tuning_t *tuning) { fluid_return_if_fail (tuning != NULL); fluid_atomic_int_inc (&tuning->refcount); } /* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */ int fluid_tuning_unref (fluid_tuning_t *tuning, int count) { fluid_return_val_if_fail (tuning != NULL, FALSE); /* Add and compare are separate, but that is OK, since refcount will only * reach 0 when there are no references and therefore no possibility of * another thread adding a reference in between */ fluid_atomic_int_add (&tuning->refcount, -count); /* Delete when refcount reaches 0 */ if (!fluid_atomic_int_get (&tuning->refcount)) { delete_fluid_tuning (tuning); return TRUE; } else return FALSE; } void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name) { if (tuning->name != NULL) { FLUID_FREE(tuning->name); tuning->name = NULL; } if (name != NULL) { tuning->name = FLUID_STRDUP(name); } } char* fluid_tuning_get_name(fluid_tuning_t* tuning) { return tuning->name; } void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch) { tuning->pitch[key] = pitch; } void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv) { int i; for (i = 0; i < 128; i++) { tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; } } void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch) { int i; for (i = 0; i < 128; i++) { tuning->pitch[i] = pitch[i]; } } void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch) { if ((key >= 0) && (key < 128)) { tuning->pitch[key] = pitch; } } fluidsynth-1.1.9/src/synth/fluid_tuning.h000066400000000000000000000042271322272076000205150ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* More information about micro tuning can be found at: http://www.midi.org/about-midi/tuning.htm http://www.midi.org/about-midi/tuning-scale.htm http://www.midi.org/about-midi/tuning_extens.htm */ #ifndef _FLUID_TUNING_H #define _FLUID_TUNING_H #include "fluidsynth_priv.h" struct _fluid_tuning_t { char* name; int bank; int prog; double pitch[128]; /* the pitch of every key, in cents */ int refcount; /* Tuning reference count */ }; fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog); void delete_fluid_tuning (fluid_tuning_t *tuning); fluid_tuning_t *fluid_tuning_duplicate (fluid_tuning_t *tuning); void fluid_tuning_ref (fluid_tuning_t *tuning); int fluid_tuning_unref (fluid_tuning_t *tuning, int count); void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name); char* fluid_tuning_get_name(fluid_tuning_t* tuning); #define fluid_tuning_get_bank(_t) ((_t)->bank) #define fluid_tuning_get_prog(_t) ((_t)->prog) void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch); #define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key]) void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv); void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch); #define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) #endif /* _FLUID_TUNING_H */ fluidsynth-1.1.9/src/synth/fluid_voice.c000066400000000000000000001677331322272076000203250ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 "fluidsynth_priv.h" #include "fluid_voice.h" #include "fluid_mod.h" #include "fluid_chan.h" #include "fluid_conv.h" #include "fluid_synth.h" #include "fluid_sys.h" #include "fluid_sfont.h" #include "fluid_rvoice_event.h" /* used for filter turn off optimization - if filter cutoff is above the specified value and filter q is below the other value, turn filter off */ #define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f #define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f /* min vol envelope release (to stop clicks) in SoundFont timecents */ #define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice); static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, int gen_key2base, int is_decay); static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); #define UPDATE_RVOICE0(proc) \ do { \ if (voice->can_access_rvoice) proc(voice->rvoice); \ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ proc, voice->rvoice, 0, 0.0f); \ } while (0) #define UPDATE_RVOICE_PTR(proc, obj) \ do { \ if (voice->can_access_rvoice) proc(voice->rvoice, obj); \ else fluid_rvoice_eventhandler_push_ptr(voice->channel->synth->eventhandler, \ proc, voice->rvoice, obj); \ } while (0) #define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \ do { \ if (voice->can_access_rvoice) proc(obj, rarg); \ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ proc, obj, 0, rarg); \ } while (0) #define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \ do { \ if (voice->can_access_rvoice) proc(obj, iarg); \ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ proc, obj, iarg, 0.0f); \ } while (0) #define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ do { \ if (voice->can_access_rvoice) proc(obj, iarg, rarg); \ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \ proc, obj, iarg, rarg); \ } while (0) #define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \ do { \ if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \ else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \ proc, obj, iarg, r1, r2, r3, r4, r5); \ } while (0) #define UPDATE_RVOICE_VOLENV(section, arg1, arg2, arg3, arg4, arg5) \ do { \ fluid_adsr_env_set_data(&voice->volenv, section, arg1, arg2, arg3, arg4, arg5) \ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, arg1, arg2, arg3, arg4, arg5) \ } while(0) #define UPDATE_RVOICE_MODENV(section, arg1, arg2, arg3, arg4, arg5) \ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, arg1, arg2, arg3, arg4, arg5) #define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1) #define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1) #define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1) #define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg) #define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg) #define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) #define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) static inline void fluid_voice_update_volenv(fluid_voice_t* voice, fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max) { fluid_adsr_env_set_data(&voice->volenv, section, count, coeff, increment, min, max); UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, count, coeff, increment, min, max); } static inline void fluid_voice_update_modenv(fluid_voice_t* voice, fluid_adsr_env_section_t section, unsigned int count, fluid_real_t coeff, fluid_real_t increment, fluid_real_t min, fluid_real_t max) { UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, count, coeff, increment, min, max); } static inline void fluid_sample_null_ptr(fluid_sample_t** sample) { if (*sample != NULL) { fluid_sample_decr_ref(*sample); *sample = NULL; } } /* * Swaps the current rvoice with the current overflow_rvoice */ static void fluid_voice_swap_rvoice(fluid_voice_t* voice) { fluid_rvoice_t* rtemp = voice->rvoice; int ctemp = voice->can_access_rvoice; voice->rvoice = voice->overflow_rvoice; voice->can_access_rvoice = voice->can_access_overflow_rvoice; voice->overflow_rvoice = rtemp; voice->can_access_overflow_rvoice = ctemp; } static void fluid_voice_initialize_rvoice(fluid_voice_t* voice) { FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); /* The 'sustain' and 'finished' segments of the volume / modulation * envelope are constant. They are never affected by any modulator * or generator. Therefore it is enough to initialize them once * during the lifetime of the synth. */ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); fluid_voice_update_volenv(voice, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVSUSTAIN, 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED, 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); } /* * new_fluid_voice */ fluid_voice_t* new_fluid_voice(fluid_real_t output_rate) { fluid_voice_t* voice; voice = FLUID_NEW(fluid_voice_t); if (voice == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } voice->rvoice = FLUID_NEW(fluid_rvoice_t); voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); if (voice->rvoice == NULL || voice->overflow_rvoice == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(voice->rvoice); FLUID_FREE(voice); return NULL; } voice->status = FLUID_VOICE_CLEAN; voice->chan = NO_CHANNEL; voice->key = 0; voice->vel = 0; voice->channel = NULL; voice->sample = NULL; /* Initialize both the rvoice and overflow_rvoice */ voice->can_access_rvoice = 1; voice->can_access_overflow_rvoice = 1; fluid_voice_initialize_rvoice(voice); fluid_voice_swap_rvoice(voice); fluid_voice_initialize_rvoice(voice); fluid_voice_set_output_rate(voice, output_rate); return voice; } /* * delete_fluid_voice */ int delete_fluid_voice(fluid_voice_t* voice) { if (voice == NULL) { return FLUID_OK; } if (!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) { /* stop rvoice before deleting voice! */ return FLUID_FAILED; } FLUID_FREE(voice->overflow_rvoice); FLUID_FREE(voice->rvoice); FLUID_FREE(voice); return FLUID_OK; } /* fluid_voice_init * * Initialize the synthesis process */ int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int start_time, fluid_real_t gain) { /* Note: The voice parameters will be initialized later, when the * generators have been retrieved from the sound font. Here, only * the 'working memory' of the voice (position in envelopes, history * of IIR filters, position in sample etc) is initialized. */ int i; if (!voice->can_access_rvoice) { if (voice->can_access_overflow_rvoice) fluid_voice_swap_rvoice(voice); else { FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); return FLUID_FAILED; } } /* We are now guaranteed to have access to the rvoice */ if (voice->sample) fluid_voice_off(voice); voice->id = id; voice->chan = fluid_channel_get_num(channel); voice->key = (unsigned char) key; voice->vel = (unsigned char) vel; voice->channel = channel; voice->mod_count = 0; voice->start_time = start_time; voice->debug = 0; voice->has_noteoff = 0; UPDATE_RVOICE0(fluid_rvoice_reset); /* Increment the reference count of the sample to prevent the unloading of the soundfont while this voice is playing, once for us and once for the rvoice. */ fluid_sample_incr_ref(sample); UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample); fluid_sample_incr_ref(sample); voice->sample = sample; i = fluid_channel_get_interp_method(channel); UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); /* Set all the generators to their default value, according to SF * 2.01 section 8.1.3 (page 48). The value of NRPN messages are * copied from the channel to the voice's generators. The sound font * loader overwrites them. The generator values are later converted * into voice parameters in * fluid_voice_calculate_runtime_synthesis_parameters. */ fluid_gen_init(&voice->gen[0], channel); UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice)); voice->synth_gain = gain; /* avoid division by zero later*/ if (voice->synth_gain < 0.0000001){ voice->synth_gain = 0.0000001; } UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); /* Set up buffer mapping, should be done more flexible in the future. */ i = channel->synth->audio_groups; UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL); i = 2 * (voice->chan % i); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1); return FLUID_OK; } /** * Update sample rate. * @note If the voice is active, it will be turned off. */ int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value) { if (fluid_voice_is_playing(voice)) fluid_voice_off(voice); voice->output_rate = value; UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); /* Update the other rvoice as well */ fluid_voice_swap_rvoice(voice); UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); fluid_voice_swap_rvoice(voice); return FLUID_FAILED; } /** * Set the value of a generator. * @param voice Voice instance * @param i Generator ID (#fluid_gen_type) * @param val Generator value */ void fluid_voice_gen_set(fluid_voice_t* voice, int i, float val) { voice->gen[i].val = val; voice->gen[i].flags = GEN_SET; if (i == GEN_SAMPLEMODE) UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val); } /** * Offset the value of a generator. * @param voice Voice instance * @param i Generator ID (#fluid_gen_type) * @param val Value to add to the existing value */ void fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val) { voice->gen[i].val += val; voice->gen[i].flags = GEN_SET; } /** * Get the value of a generator. * @param voice Voice instance * @param gen Generator ID (#fluid_gen_type) * @return Current generator value */ float fluid_voice_gen_get(fluid_voice_t* voice, int gen) { return voice->gen[gen].val; } fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) { /* This is an extension to the SoundFont standard. More * documentation is available at the fluid_synth_set_gen2() * function. */ if (voice->gen[num].flags == GEN_ABS_NRPN) { return (fluid_real_t) voice->gen[num].nrpn; } else { return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); } } /** * Synthesize a voice to a buffer. * * @param voice Voice to synthesize * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) * @return Count of samples written to dsp_buf (can be 0) * * Panning, reverb and chorus are processed separately. The dsp interpolation * routine is in (fluid_dsp_float.c). */ int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf) { int result; if (!voice->can_access_rvoice) return 0; result = fluid_rvoice_write(voice->rvoice, dsp_buf); if (result == -1) return 0; if ((result < FLUID_BUFSIZE) && fluid_voice_is_playing(voice)) /* Voice finished by itself */ fluid_voice_off(voice); return result; } /** * Mix voice data to left/right (panning), reverb and chorus buffers. * @param count Number of samples * @param dsp_buf Source buffer * @param voice Voice to mix * @param left_buf Left audio buffer * @param right_buf Right audio buffer * @param reverb_buf Reverb buffer * @param chorus_buf Chorus buffer * */ void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, fluid_real_t* left_buf, fluid_real_t* right_buf, fluid_real_t* reverb_buf, fluid_real_t* chorus_buf) { fluid_rvoice_buffers_t buffers; fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf}; fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left); fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right); fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb); fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus); fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4); fluid_check_fpe ("voice_mix"); } /* * fluid_voice_start */ void fluid_voice_start(fluid_voice_t* voice) { /* The maximum volume of the loop is calculated and cached once for each * sample with its nominal loop settings. This happens, when the sample is used * for the first time.*/ fluid_voice_calculate_runtime_synthesis_parameters(voice); voice->ref = fluid_profile_ref(); voice->status = FLUID_VOICE_ON; /* Increment voice count */ voice->channel->synth->active_voice_count++; } void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) { fluid_tuning_t* tuning; fluid_real_t x; /* The GEN_PITCH is a hack to fit the pitch bend controller into the * modulator paradigm. Now the nominal pitch of the key is set. * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a * non-realtime parameter. So we don't allow modulation (as opposed * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied, * one key remains fixed. Here C3 (MIDI number 60) is used. */ if (fluid_channel_has_tuning(voice->channel)) { tuning = fluid_channel_get_tuning (voice->channel); x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f)); voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f * (fluid_tuning_get_pitch (tuning, fluid_voice_get_actual_key(voice)) - x) + x; } else { voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val * (fluid_voice_get_actual_key(voice) - voice->root_pitch / 100.0f) + voice->root_pitch; } } /* * fluid_voice_calculate_runtime_synthesis_parameters * * in this function we calculate the values of all the parameters. the * parameters are converted to their most useful unit for the DSP * algorithm, for example, number of samples instead of * timecents. Some parameters keep their "perceptual" unit and * conversion will be done in the DSP function. This is the case, for * example, for the pitch since it is modulated by the controllers in * cents. */ static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) { int i; int list_of_generators_to_initialize[35] = { GEN_STARTADDROFS, /* SF2.01 page 48 #0 */ GEN_ENDADDROFS, /* #1 */ GEN_STARTLOOPADDROFS, /* #2 */ GEN_ENDLOOPADDROFS, /* #3 */ /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */ GEN_MODLFOTOPITCH, /* #5 */ GEN_VIBLFOTOPITCH, /* #6 */ GEN_MODENVTOPITCH, /* #7 */ GEN_FILTERFC, /* #8 */ GEN_FILTERQ, /* #9 */ GEN_MODLFOTOFILTERFC, /* #10 */ GEN_MODENVTOFILTERFC, /* #11 */ /* GEN_ENDADDRCOARSEOFS [1] #12 */ GEN_MODLFOTOVOL, /* #13 */ /* not defined #14 */ GEN_CHORUSSEND, /* #15 */ GEN_REVERBSEND, /* #16 */ GEN_PAN, /* #17 */ /* not defined #18 */ /* not defined #19 */ /* not defined #20 */ GEN_MODLFODELAY, /* #21 */ GEN_MODLFOFREQ, /* #22 */ GEN_VIBLFODELAY, /* #23 */ GEN_VIBLFOFREQ, /* #24 */ GEN_MODENVDELAY, /* #25 */ GEN_MODENVATTACK, /* #26 */ GEN_MODENVHOLD, /* #27 */ GEN_MODENVDECAY, /* #28 */ /* GEN_MODENVSUSTAIN [1] #29 */ GEN_MODENVRELEASE, /* #30 */ /* GEN_KEYTOMODENVHOLD [1] #31 */ /* GEN_KEYTOMODENVDECAY [1] #32 */ GEN_VOLENVDELAY, /* #33 */ GEN_VOLENVATTACK, /* #34 */ GEN_VOLENVHOLD, /* #35 */ GEN_VOLENVDECAY, /* #36 */ /* GEN_VOLENVSUSTAIN [1] #37 */ GEN_VOLENVRELEASE, /* #38 */ /* GEN_KEYTOVOLENVHOLD [1] #39 */ /* GEN_KEYTOVOLENVDECAY [1] #40 */ /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */ GEN_KEYNUM, /* #46 */ GEN_VELOCITY, /* #47 */ GEN_ATTENUATION, /* #48 */ /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */ /* GEN_COARSETUNE [1] #51 */ /* GEN_FINETUNE [1] #52 */ GEN_OVERRIDEROOTKEY, /* #58 */ GEN_PITCH, /* --- */ -1}; /* end-of-list marker */ /* When the voice is made ready for the synthesis process, a lot of * voice-internal parameters have to be calculated. * * At this point, the sound font has already set the -nominal- value * for all generators (excluding GEN_PITCH). Most generators can be * modulated - they include a nominal value and an offset (which * changes with velocity, note number, channel parameters like * aftertouch, mod wheel...) Now this offset will be calculated as * follows: * * - Process each modulator once. * - Calculate its output value. * - Find the target generator. * - Add the output value to the modulation value of the generator. * * Note: The generators have been initialized with * fluid_gen_set_default_values. */ for (i = 0; i < voice->mod_count; i++) { fluid_mod_t* mod = &voice->mod[i]; fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice); int dest_gen_index = mod->dest; fluid_gen_t* dest_gen = &voice->gen[dest_gen_index]; dest_gen->mod += modval; /* fluid_dump_modulator(mod); */ } /* Now the generators are initialized, nominal and modulation value. * The voice parameters (which depend on generators) are calculated * with fluid_voice_update_param. Processing the list of generator * changes will calculate each voice parameter once. * * Note [1]: Some voice parameters depend on several generators. For * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided * by removing all but one generator from the list of voice * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the * initialisation list contains only GEN_XXX. */ /* Calculate the voice parameter(s) dependent on each generator. */ for (i = 0; list_of_generators_to_initialize[i] != -1; i++) { fluid_voice_update_param(voice, list_of_generators_to_initialize[i]); } /* Make an estimate on how loud this voice can get at any time (attenuation). */ UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, fluid_voice_get_lower_boundary_for_attenuation(voice)); return FLUID_OK; } /* * calculate_hold_decay_buffers */ static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base, int gen_key2base, int is_decay) { /* Purpose: * * Returns the number of DSP loops, that correspond to the hold * (is_decay=0) or decay (is_decay=1) time. * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY */ fluid_real_t timecents; fluid_real_t seconds; int buffers; /* SF2.01 section 8.4.3 # 31, 32, 39, 40 * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. * The unit of the generator is timecents per key number. * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) * will cause (60-72)*100=-1200 timecents of time variation. * The time is cut in half. */ timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - fluid_voice_get_actual_key(voice))); /* Range checking */ if (is_decay){ /* SF 2.01 section 8.1.3 # 28, 36 */ if (timecents > 8000.0) { timecents = 8000.0; } } else { /* SF 2.01 section 8.1.3 # 27, 35 */ if (timecents > 5000) { timecents = 5000.0; } /* SF 2.01 section 8.1.2 # 27, 35: * The most negative number indicates no hold time */ if (timecents <= -32768.) { return 0; } } /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ if (timecents < -12000.0) { timecents = -12000.0; } seconds = fluid_tc2sec(timecents); /* Each DSP loop processes FLUID_BUFSIZE samples. */ /* round to next full number of buffers */ buffers = (int)(((fluid_real_t)voice->output_rate * seconds) / (fluid_real_t)FLUID_BUFSIZE +0.5); return buffers; } /* * The value of a generator (gen) has changed. (The different * generators are listed in fluidsynth.h, or in SF2.01 page 48-49) * Now the dependent 'voice' parameters are calculated. * * fluid_voice_update_param can be called during the setup of the * voice (to calculate the initial value for a voice parameter), or * during its operation (a generator has been changed due to * real-time parameter modifications like pitch-bend). * * Note: The generator holds three values: The base value .val, an * offset caused by modulators .mod, and an offset caused by the * NRPN system. _GEN(voice, generator_enumerator) returns the sum * of all three. */ /** * Update all the synthesis parameters, which depend on generator \a gen. * @param voice Voice instance * @param gen Generator id (#fluid_gen_type) * * This is only necessary after changing a generator of an already operating voice. * Most applications will not need this function. */ void fluid_voice_update_param(fluid_voice_t* voice, int gen) { double q_dB; fluid_real_t x; fluid_real_t y; unsigned int count, z; // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank. static const float ALT_ATTENUATION_SCALE = 0.4; switch (gen) { case GEN_PAN: /* range checking is done in the fluid_pan function */ voice->pan = _GEN(voice, GEN_PAN); voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f; voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f; UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right); break; case GEN_ATTENUATION: voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn; /* Range: SF2.01 section 8.1.3 # 48 * Motivation for range checking: * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ fluid_clip(voice->attenuation, 0.0, 1440.0); UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation); break; /* The pitch is calculated from three different generators. * Read comment in fluidsynth.h about GEN_PITCH. */ case GEN_PITCH: case GEN_COARSETUNE: case GEN_FINETUNE: /* The testing for allowed range is done in 'fluid_ct2hz' */ voice->pitch = (_GEN(voice, GEN_PITCH) + 100.0f * _GEN(voice, GEN_COARSETUNE) + _GEN(voice, GEN_FINETUNE)); UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch); break; case GEN_REVERBSEND: /* The generator unit is 'tenths of a percent'. */ voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f; fluid_clip(voice->reverb_send, 0.0, 1.0); voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f; UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb); break; case GEN_CHORUSSEND: /* The generator unit is 'tenths of a percent'. */ voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f; fluid_clip(voice->chorus_send, 0.0, 1.0); voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f; UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus); break; case GEN_OVERRIDEROOTKEY: /* This is a non-realtime parameter. Therefore the .mod part of the generator * can be neglected. * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount * which offsets the original rate. This means that the fine tuning is * inverted with respect to the root note (so subtract it, not add). */ if (voice->sample != NULL) { if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f - voice->sample->pitchadj; else voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate)); } else { if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f; else voice->root_pitch = 0; x = fluid_ct2hz(voice->root_pitch); } /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ fluid_voice_calculate_gen_pitch(voice); UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x); break; case GEN_FILTERFC: /* The resonance frequency is converted from absolute cents to * midicents .val and .mod are both used, this permits real-time * modulation. The allowed range is tested in the 'fluid_ct2hz' * function [PH,20021214] */ x = _GEN(voice, GEN_FILTERFC); UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_fres, x); break; case GEN_FILTERQ: /* The generator contains 'centibels' (1/10 dB) => divide by 10 to * obtain dB */ q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f; /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ fluid_clip(q_dB, 0.0f, 96.0f); /* Short version: Modify the Q definition in a way, that a Q of 0 * dB leads to no resonance hump in the freq. response. * * Long version: From SF2.01, page 39, item 9 (initialFilterQ): * "The gain at the cutoff frequency may be less than zero when * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave * q as it is, then this results in a 3 dB hump slightly below * fc. At fc, the gain is exactly the DC gain (0 dB). What is * (probably) meant here is that the filter does not show a * resonance hump for q_dB=0. In this case, the corresponding * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of * attenuation at fc now. In this case Q_dB is the height of the * resonance peak not over the DC gain, but over the frequency * response of a non-resonant filter. This idea is implemented as * follows: */ q_dB -= 3.01f; UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB); break; case GEN_MODLFOTOPITCH: x = _GEN(voice, GEN_MODLFOTOPITCH); fluid_clip(x, -12000.0, 12000.0); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); break; case GEN_MODLFOTOVOL: x = _GEN(voice, GEN_MODLFOTOVOL); fluid_clip(x, -960.0, 960.0); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); break; case GEN_MODLFOTOFILTERFC: x = _GEN(voice, GEN_MODLFOTOFILTERFC); fluid_clip(x, -12000, 12000); UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); break; case GEN_MODLFODELAY: x = _GEN(voice, GEN_MODLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z); break; case GEN_MODLFOFREQ: /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples * - the delay into a sample delay */ x = _GEN(voice, GEN_MODLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x); break; case GEN_VIBLFOFREQ: /* vib lfo * * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples * - the delay into a sample delay */ x = _GEN(voice, GEN_VIBLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate; UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); break; case GEN_VIBLFODELAY: x = _GEN(voice,GEN_VIBLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); break; case GEN_VIBLFOTOPITCH: x = _GEN(voice, GEN_VIBLFOTOPITCH); fluid_clip(x, -12000.0, 12000.0); UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); break; case GEN_KEYNUM: /* GEN_KEYNUM: SF2.01 page 46, item 46 * * If this generator is active, it forces the key number to its * value. Non-realtime controller. * * There is a flag, which should indicate, whether a generator is * enabled or not. But here we rely on the default value of -1. */ /* 2017-09-02: do not change the voice's key here, otherwise it will * never be released on a noteoff event */ #if 0 x = _GEN(voice, GEN_KEYNUM); if (x >= 0){ voice->key = x; } #endif break; case GEN_VELOCITY: /* GEN_VELOCITY: SF2.01 page 46, item 47 * * If this generator is active, it forces the velocity to its * value. Non-realtime controller. * * There is a flag, which should indicate, whether a generator is * enabled or not. But here we rely on the default value of -1. */ /* 2017-09-02: do not change the voice's velocity here, user * fluid_voice_get_actual_velocity() to get the value of this generator * if active. */ #if 0 x = _GEN(voice, GEN_VELOCITY); if (x > 0) { voice->vel = x; } #endif break; case GEN_MODENVTOPITCH: x = _GEN(voice, GEN_MODENVTOPITCH); fluid_clip(x, -12000.0, 12000.0); UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); break; case GEN_MODENVTOFILTERFC: x = _GEN(voice,GEN_MODENVTOFILTERFC); /* Range: SF2.01 section 8.1.3 # 1 * Motivation for range checking: * Filter is reported to make funny noises now and then */ fluid_clip(x, -12000.0, 12000.0); UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x); break; /* sample start and ends points * * Range checking is initiated via the * voice->check_sample_sanity flag, * because it is impossible to check here: * During the voice setup, all modulators are processed, while * the voice is inactive. Therefore, illegal settings may * occur during the setup (for example: First move the loop * end point ahead of the loop start point => invalid, then * move the loop start point forward => valid again. */ case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ if (voice->sample != NULL) { z = (voice->sample->start + (int) _GEN(voice, GEN_STARTADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); UPDATE_RVOICE_I1(fluid_rvoice_set_start, z); } break; case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ if (voice->sample != NULL) { z = (voice->sample->end + (int) _GEN(voice, GEN_ENDADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); UPDATE_RVOICE_I1(fluid_rvoice_set_end, z); } break; case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ if (voice->sample != NULL) { z = (voice->sample->loopstart + (int) _GEN(voice, GEN_STARTLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z); } break; case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ if (voice->sample != NULL) { z = (voice->sample->loopend + (int) _GEN(voice, GEN_ENDLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z); } break; /* Conversion functions differ in range limit */ #define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE) #define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE) #define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE) /* volume envelope * * - delay and hold times are converted to absolute number of samples * - sustain is converted to its absolute value * - attack, decay and release are converted to their increment per sample */ case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ x = _GEN(voice, GEN_VOLENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); count = NUM_BUFFERS_DELAY(x); fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDELAY, count, 0.0f, 0.0f, -1.0f, 1.0f); break; case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ x = _GEN(voice, GEN_VOLENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); fluid_voice_update_volenv(voice, FLUID_VOICE_ENVATTACK, count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f); break; case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVHOLD, count, 1.0f, 0.0f, -1.0f, 2.0f); break; case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f); break; case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ x = _GEN(voice, GEN_VOLENVRELEASE); fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); fluid_voice_update_volenv(voice, FLUID_VOICE_ENVRELEASE, count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f); break; /* Modulation envelope */ case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ x = _GEN(voice, GEN_MODENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDELAY, NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f); break; case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ x = _GEN(voice, GEN_MODENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_ATTACK(x); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVATTACK, count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f); break; case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVHOLD, count, 1.0f, 0.0f, -1.0f, 2.0f); break; case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDECAY, count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f); break; case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ x = _GEN(voice, GEN_MODENVRELEASE); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_BUFFERS_RELEASE(x); fluid_voice_update_modenv(voice, FLUID_VOICE_ENVRELEASE, count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f); break; } /* switch 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 * * In this implementation, I want to make sure that all controllers * are event based: the parameter values of the DSP algorithm should * only be updates when a controller event arrived and not at every * iteration of the audio cycle (which would probably be feasible if * the synth was made in silicon). * * The update is done in three 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. * * - 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 */ int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl) { int i, k; fluid_mod_t* mod; int gen; fluid_real_t modval; /* 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)) { 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++) { if (fluid_mod_has_dest(&voice->mod[k], gen)) { modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice); } } 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); } } return FLUID_OK; } /** * Update all the modulators. This function is called after a * ALL_CTRL_OFF MIDI message has been received (CC 121). * */ 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->channel, 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; } /* Force the voice into release stage. Useful anywhere a voice needs to be damped even if pedals (sustain sostenuto) are depressed. See fluid_synth_damp_voices_by_sustain_LOCAL(), fluid_synth_damp_voices_by_sostenuto_LOCAL, fluid_voice_noteoff(). */ void fluid_voice_release(fluid_voice_t* voice) { unsigned int at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); voice->has_noteoff = 1; // voice is marked as noteoff occured } /* * fluid_voice_noteoff * * Sending a noteoff event will advance the envelopes to section 5 (release). */ int fluid_voice_noteoff(fluid_voice_t* voice) { fluid_channel_t* channel; fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref); channel = voice->channel; /* Sustain a note under Sostenuto pedal */ if (fluid_channel_sostenuto(channel) && channel->sostenuto_orderid > voice->id) { // Sostenuto depressed after note voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; } /* Or sustain a note under Sustain pedal */ else if (fluid_channel_sustained(channel)) { voice->status = FLUID_VOICE_SUSTAINED; } /* Or force the voice to release stage */ else fluid_voice_release(voice); return FLUID_OK; } /* * fluid_voice_kill_excl * * Percussion sounds can be mutually exclusive: for example, a 'closed * hihat' sound will terminate an 'open hihat' sound ringing at the * same time. This behaviour is modeled using 'exclusive classes', * turning on a voice with an exclusive class other than 0 will kill * all other voices having that exclusive class within the same preset * or channel. fluid_voice_kill_excl gets called, when 'voice' is to * be killed for that reason. */ int fluid_voice_kill_excl(fluid_voice_t* voice){ unsigned int at_tick; if (!fluid_voice_is_playing(voice)) { return FLUID_OK; } /* Turn off the exclusive class information for this voice, so that it doesn't get killed twice */ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); /* Speed up the volume envelope */ /* The value was found through listening tests with hi-hat samples. */ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); fluid_voice_update_param(voice, GEN_VOLENVRELEASE); /* Speed up the modulation envelope */ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); fluid_voice_update_param(voice, GEN_MODENVRELEASE); at_tick = fluid_channel_get_min_note_length_ticks (voice->channel); UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); return FLUID_OK; } /* * Called by fluid_synth when the overflow rvoice can be reclaimed. */ void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice) { voice->can_access_overflow_rvoice = 1; fluid_sample_null_ptr(&voice->overflow_rvoice->dsp.sample); } /* * fluid_voice_off * * Force the voice into finished stage. Useful anywhere a voice * needs to be cancelled from MIDI API. */ void fluid_voice_off(fluid_voice_t* voice) { UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */ } /* * fluid_voice_stop * * Purpose: * Turns off a voice, meaning that it is not processed anymore by the * DSP loop, i.e. contrary part to fluid_voice_start(). */ void fluid_voice_stop(fluid_voice_t* voice) { fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); voice->chan = NO_CHANNEL; if (voice->can_access_rvoice) fluid_sample_null_ptr(&voice->rvoice->dsp.sample); voice->status = FLUID_VOICE_OFF; voice->has_noteoff = 1; /* Decrement the reference count of the sample. */ fluid_sample_null_ptr(&voice->sample); /* Decrement voice count */ voice->channel->synth->active_voice_count--; } /** * 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 * #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 * exist so don't check. */ void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode) { 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 */ { FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); return; } if (mode == FLUID_VOICE_ADD) { /* if identical modulator exists, add them */ for (i = 0; i < voice->mod_count; i++) { if (fluid_mod_test_identity(&voice->mod[i], mod)) { // printf("Adding modulator...\n"); voice->mod[i].amount += mod->amount; return; } } } else if (mode == FLUID_VOICE_OVERWRITE) { /* if identical modulator exists, replace it (only the amount has to be changed) */ for (i = 0; i < voice->mod_count; i++) { if (fluid_mod_test_identity(&voice->mod[i], mod)) { // printf("Replacing modulator...amount is %f\n",mod->amount); voice->mod[i].amount = mod->amount; return; } } } /* Add a new modulator (No existing modulator to add / overwrite). Also, default modulators (FLUID_VOICE_DEFAULT) are added without checking, if the same modulator already exists. */ if (voice->mod_count < FLUID_NUM_MOD) { fluid_mod_clone(&voice->mod[voice->mod_count++], mod); } else { FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id); } } /** * Get the unique ID of the noteon-event. * @param voice Voice instance * @return Note on unique ID * * A SoundFont loader may store the voice processes it has created for * real-time control during the operation of a voice (for example: parameter * changes in SoundFont editor). The synth uses a pool of voices, which are * 'recycled' and never deallocated. * * Before modifying an existing voice, check * - that its state is still 'playing' * - that the ID is still the same * * Otherwise the voice has finished playing. */ unsigned int fluid_voice_get_id(const fluid_voice_t* voice) { return voice->id; } /** * Check if a voice is producing sound. This is also true after a voice received a noteoff as it may be playing in release phase. * @param voice Voice instance * @return TRUE if playing, FALSE otherwise */ int fluid_voice_is_playing(const fluid_voice_t* voice) { return (voice->status == FLUID_VOICE_ON) || fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice); } /** * Check if a voice is ON. A voice is ON, if it has not yet received a noteoff event. * @param voice Voice instance * @return TRUE if on, FALSE otherwise * @since 1.1.7 */ int fluid_voice_is_on(const fluid_voice_t* voice) { return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff); } /** * Check if a voice keeps playing after it has received a noteoff due to being held by sustain. * @param voice Voice instance * @return TRUE if sustained, FALSE otherwise * @since 1.1.7 */ int fluid_voice_is_sustained(const fluid_voice_t* voice) { return (voice->status == FLUID_VOICE_SUSTAINED); } /** * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto. * @param voice Voice instance * @return TRUE if sostenuto, FALSE otherwise * @since 1.1.7 */ int fluid_voice_is_sostenuto(const fluid_voice_t* voice) { return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO); } /** * If the voice is playing, gets the midi channel the voice is playing on. Else the result is undefined. * @param voice Voice instance * @return The channel assigned to this voice * @since 1.1.7 */ int fluid_voice_get_channel(const fluid_voice_t* voice) { return voice->chan; } /** * If the voice is playing, gets the midi key the voice is actually playing at. Else the result is undefined. * If the voice was started from an instrument which uses a fixed key generator, it returns that. * Else returns the same as \c fluid_voice_get_key. * @param voice Voice instance * @return The midi key this voice is playing at * @since 1.1.7 */ int fluid_voice_get_actual_key(const fluid_voice_t* voice) { fluid_real_t x = _GEN(voice, GEN_KEYNUM); if (x >= 0) { return (int)x; } else { return fluid_voice_get_key(voice); } } /** * If the voice is playing, gets the midi key from the noteon event, by which the voice was initially turned on with. * Else the result is undefined. * @param voice Voice instance * @return The midi key of the noteon event that originally turned on this voice * @since 1.1.7 */ int fluid_voice_get_key(const fluid_voice_t* voice) { return voice->key; } /** * If the voice is playing, gets the midi velocity the voice is actually playing at. Else the result is undefined. * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. * Else returns the same as \c fluid_voice_get_velocity. * @param voice Voice instance * @return The midi velocity this voice is playing at * @since 1.1.7 */ int fluid_voice_get_actual_velocity(const fluid_voice_t* voice) { fluid_real_t x = _GEN(voice, GEN_VELOCITY); if (x > 0) { return (int)x; } else { return fluid_voice_get_velocity(voice); } } /** * If the voice is playing, gets the midi velocity from the noteon event, by which the voice was initially * turned on with. Else the result is undefined. * @param voice Voice instance * @return The midi velocity which originally turned on this voice * @since 1.1.7 */ int fluid_voice_get_velocity(const fluid_voice_t* voice) { return voice->vel; } /* * fluid_voice_get_lower_boundary_for_attenuation * * Purpose: * * 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 * all modulators have been run on the voice once. Also, * voice->attenuation has to be initialized. */ static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice) { int i; fluid_mod_t* mod; fluid_real_t possible_att_reduction_cB=0; fluid_real_t lower_bound; for (i = 0; i < voice->mod_count; i++) { mod = &voice->mod[i]; /* Modulator has attenuation as target and can change over time? */ if ((mod->dest == GEN_ATTENUATION) && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) { fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice); fluid_real_t v = fabs(mod->amount); if ((mod->src1 == FLUID_MOD_PITCHWHEEL) || (mod->flags1 & FLUID_MOD_BIPOLAR) || (mod->flags2 & FLUID_MOD_BIPOLAR) || (mod->amount < 0)) { /* Can this modulator produce a negative contribution? */ v *= -1.0; } else { /* No negative value possible. But still, the minimum contribution is 0. */ v = 0; } /* For example: * - current_val=100 * - min_val=-4000 * - possible_att_reduction_cB += 4100 */ if (current_val > v){ possible_att_reduction_cB += (current_val - v); } } } lower_bound = voice->attenuation-possible_att_reduction_cB; /* SF2.01 specs do not allow negative attenuation */ if (lower_bound < 0) { lower_bound = 0; } return lower_bound; } int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) { voice->gen[gen].nrpn = nrpn_value; voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET; fluid_voice_update_param(voice, gen); return FLUID_OK; } int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) { /* avoid division by zero*/ if (gain < 0.0000001){ gain = 0.0000001; } voice->synth_gain = gain; voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f; voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f; voice->amp_reverb = voice->reverb_send * gain / 32768.0f; voice->amp_chorus = voice->chorus_send * gain / 32768.0f; UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus); return FLUID_OK; } /* - Scan the loop * - determine the peak level * - Calculate, what factor will make the loop inaudible * - Store in sample */ /** * Calculate the peak volume of a sample for voice off optimization. * @param s Sample to optimize * @return #FLUID_OK on success, #FLUID_FAILED otherwise * * If the peak volume during the loop is known, then the voice can * be released earlier during the release phase. Otherwise, the * voice will operate (inaudibly), until the envelope is at the * nominal turnoff point. So it's a good idea to call * fluid_voice_optimize_sample() on each sample once. */ int fluid_voice_optimize_sample(fluid_sample_t* s) { signed short peak_max = 0; signed short peak_min = 0; signed short peak; fluid_real_t normalized_amplitude_during_loop; double result; int i; /* ignore ROM and other(?) invalid samples */ if (!s->valid) return (FLUID_OK); if (!s->amplitude_that_reaches_noise_floor_is_valid) { /* Only once */ /* Scan the loop */ for (i = (int)s->loopstart; i < (int)s->loopend; i++){ signed short val = s->data[i]; if (val > peak_max) { peak_max = val; } else if (val < peak_min) { peak_min = val; } } /* Determine the peak level */ if (peak_max > -peak_min) { peak = peak_max; } else { peak = -peak_min; } if (peak == 0){ /* Avoid division by zero */ peak = 1; } /* Calculate what factor will make the loop inaudible * For example: Take a peak of 3277 (10 % of 32768). The * normalized amplitude is 0.1 (10 % of 32768). An amplitude * factor of 0.0001 (as opposed to the default 0.00001) will * drop this sample to the noise floor. */ /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.; result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; /* Store in sample */ s->amplitude_that_reaches_noise_floor = (double)result; s->amplitude_that_reaches_noise_floor_is_valid = 1; #if 0 printf("Sample peak detection: factor %f\n", (double)result); #endif } return FLUID_OK; } fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice, fluid_overflow_prio_t* score, unsigned int cur_time) { fluid_real_t this_voice_prio = 0; /* Are we already overflowing? */ if (!voice->can_access_overflow_rvoice) { return OVERFLOW_PRIO_CANNOT_KILL; } /* Is this voice on the drum channel? * Then it is very important. * Also skip the released and sustained scores. */ if (voice->channel->channel_type == CHANNEL_TYPE_DRUM) { this_voice_prio += score->percussion; } else if (voice->has_noteoff) { /* Noteoff has */ this_voice_prio += score->released; } else if (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)) { /* This voice is still active, since the sustain pedal is held down. * Consider it less important than non-sustained channels. * This decision is somehow subjective. But usually the sustain pedal * is used to play 'more-voices-than-fingers', so it shouldn't hurt * if we kill one voice. */ this_voice_prio += score->sustained; } /* We are not enthusiastic about releasing voices, which have just been started. * Otherwise hitting a chord may result in killing notes belonging to that very same * chord. So give newer voices a higher score. */ if (score->age) { cur_time -= voice->start_time; if (cur_time < 1) { cur_time = 1; // Avoid div by zero } this_voice_prio += (score->age * voice->output_rate) / cur_time; } /* take a rough estimate of loudness into account. Louder voices are more important. */ if (score->volume) { fluid_real_t a = voice->attenuation; if (voice->has_noteoff) { // FIXME: Should take into account where on the envelope we are...? } if (a < 0.1) { a = 0.1; // Avoid div by zero } this_voice_prio += score->volume / a; } return this_voice_prio; } fluidsynth-1.1.9/src/synth/fluid_voice.h000066400000000000000000000154431322272076000203200ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_VOICE_H #define _FLUID_VOICE_H #include "fluid_phase.h" #include "fluid_gen.h" #include "fluid_mod.h" #include "fluid_iir_filter.h" #include "fluid_adsr_env.h" #include "fluid_lfo.h" #include "fluid_rvoice.h" #include "fluid_sys.h" #define NO_CHANNEL 0xff typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; struct _fluid_overflow_prio_t { fluid_real_t percussion; /**< Is this voice on the drum channel? Then add this score */ fluid_real_t released; /**< Is this voice in release stage? Then add this score (usually negative) */ fluid_real_t sustained; /**< Is this voice sustained? Then add this score (usually negative) */ fluid_real_t volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ fluid_real_t age; /**< This score will be divided by the number of seconds the voice has lasted */ }; enum fluid_voice_status { FLUID_VOICE_CLEAN, FLUID_VOICE_ON, FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */ FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */ FLUID_VOICE_OFF }; /* * fluid_voice_t */ struct _fluid_voice_t { unsigned int id; /* the id is incremented for every new noteon. it's used for noteoff's */ unsigned char status; unsigned char chan; /* the channel number, quick access for channel messages */ unsigned char key; /* the key of the noteon event, quick access for noteoff */ unsigned char vel; /* the velocity of the noteon event */ fluid_channel_t* channel; fluid_gen_t gen[GEN_LAST]; fluid_mod_t mod[FLUID_NUM_MOD]; int mod_count; fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */ int has_noteoff; /* Flag set when noteoff has been sent */ /* basic parameters */ fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ unsigned int start_time; fluid_adsr_env_t volenv; /* Volume envelope (dupe in rvoice) */ /* basic parameters */ fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ fluid_real_t root_pitch; /* master gain (dupe in rvoice) */ fluid_real_t synth_gain; /* pan */ fluid_real_t pan; fluid_real_t amp_left; fluid_real_t amp_right; /* reverb */ fluid_real_t reverb_send; fluid_real_t amp_reverb; /* chorus */ fluid_real_t chorus_send; fluid_real_t amp_chorus; /* rvoice control */ fluid_rvoice_t* rvoice; fluid_rvoice_t* overflow_rvoice; /* Used temporarily and only in overflow situations */ int can_access_rvoice; /* False if rvoice is being rendered in separate thread */ int can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ /* for debugging */ int debug; double ref; }; fluid_voice_t* new_fluid_voice(fluid_real_t output_rate); int delete_fluid_voice(fluid_voice_t* voice); void fluid_voice_start(fluid_voice_t* voice); void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice); int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf); int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int time, fluid_real_t gain); int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl); int fluid_voice_modulate_all(fluid_voice_t* voice); /** Set the NRPN value of a generator. */ int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs); /** Set the gain. */ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain); int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value); /** Update all the synthesis parameters, which depend on generator 'gen'. This is only necessary after changing a generator of an already operating voice. Most applications will not need this function.*/ void fluid_voice_update_param(fluid_voice_t* voice, int gen); void fluid_voice_release(fluid_voice_t* voice); int 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_overflow_rvoice_finished(fluid_voice_t* voice); void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, fluid_real_t* left_buf, fluid_real_t* right_buf, fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); int fluid_voice_kill_excl(fluid_voice_t* voice); fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice, fluid_overflow_prio_t* score, unsigned int cur_time); #define OVERFLOW_PRIO_CANNOT_KILL 999999. /** * Locks the rvoice for rendering, so it can't be modified directly */ static FLUID_INLINE fluid_rvoice_t* fluid_voice_lock_rvoice(fluid_voice_t* voice) { voice->can_access_rvoice = 0; return voice->rvoice; } /** * Unlocks the rvoice for rendering, so it can be modified directly */ static FLUID_INLINE void fluid_voice_unlock_rvoice(fluid_voice_t* voice) { voice->can_access_rvoice = 1; } #define _AVAILABLE(voice) ((voice)->can_access_rvoice && \ (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))) //#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) #define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) /* FIXME - This doesn't seem to be used anywhere - JG */ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); #define fluid_voice_get_loudness(voice) (fluid_adsr_env_get_max_val(&voice->volenv)) #define _GEN(_voice, _n) \ ((fluid_real_t)(_voice)->gen[_n].val \ + (fluid_real_t)(_voice)->gen[_n].mod \ + (fluid_real_t)(_voice)->gen[_n].nrpn) /* defined in fluid_dsp_float.c */ void fluid_dsp_float_config (void); int fluid_dsp_float_interpolate_none (fluid_voice_t *voice); int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice); int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice); int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice); #endif /* _FLUID_VOICE_H */ fluidsynth-1.1.9/src/unused/000077500000000000000000000000001322272076000160065ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/fluid_dsp_float.c000066400000000000000000000557241322272076000213250ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 "fluidsynth_priv.h" #include "fluid_phase.h" /* Purpose: * * Interpolates audio data (obtains values between the samples of the original * waveform data). * * Variables loaded from the voice structure (assigned in fluid_voice_write()): * - dsp_data: Pointer to the original waveform data * - dsp_phase: The position in the original waveform data. * This has an integer and a fractional part (between samples). * - dsp_phase_incr: For each output sample, the position in the original * waveform advances by dsp_phase_incr. This also has an integer * part and a fractional part. * If a sample is played at root pitch (no pitch change), * dsp_phase_incr is integer=1 and fractional=0. * - dsp_amp: The current amplitude envelope value. * - dsp_amp_incr: The changing rate of the amplitude envelope. * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) */ #include "fluidsynth_priv.h" #include "fluid_synth.h" #include "fluid_voice.h" /* 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_dsp_float_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) { v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted); /* Hamming window */ v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (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 inline int fluid_voice_is_looping(fluid_voice_t *voice) { return _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE && fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE); } /* No interpolation. Just take the sample, which is closest to * the playback pointer. Questionable quality, but very * efficient. */ int fluid_dsp_float_interpolate_none (fluid_voice_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int end_index; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = fluid_voice_is_looping(voice); end_index = looping ? voice->loopend - 1 : voice->end; while (1) { dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ /* interpolate sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index]; /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */ dsp_amp += dsp_amp_incr; } /* break out if not looping (buffer may not be full) */ if (!looping) break; /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); voice->has_looped = 1; } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* Straight line interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int end_index; short int point; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = fluid_voice_is_looping(voice); /* last index before 2nd interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 1; /* 2nd interpolation point to use at end of loop or sample */ if (looping) point = dsp_data[voice->loopstart]; /* loop start */ else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */ while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + coeffs[1] * dsp_data[dsp_phase_index+1]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the last point */ /* interpolate within last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index] + coeffs[1] * point); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; /* increment amplitude */ } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start (if past */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); voice->has_looped = 1; } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index--; /* set end back to second to last sample point */ } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* 4th order (cubic) interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int start_index, end_index; short int start_point, end_point1, end_point2; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* voice is currently looping? */ looping = fluid_voice_is_looping(voice); /* last index before 4th interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 2; if (voice->has_looped) /* set start_index and start point if looped or not */ { start_index = voice->loopstart; start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */ } else { start_index = voice->start; start_point = dsp_data[voice->start]; /* just duplicate the point */ } /* get points off the end (loop start if looping, duplicate point if end) */ if (looping) { end_point1 = dsp_data[voice->loopstart]; end_point2 = dsp_data[voice->loopstart + 1]; } else { end_point1 = dsp_data[voice->end]; end_point2 = end_point1; } while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * dsp_data[dsp_phase_index+2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * dsp_data[dsp_phase_index+2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the 2nd to last point */ /* interpolate within 2nd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * dsp_data[dsp_phase_index+1] + coeffs[3] * end_point1); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the last point */ /* interpolate within the last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1] + coeffs[1] * dsp_data[dsp_phase_index] + coeffs[2] * end_point1 + coeffs[3] * end_point2); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); if (!voice->has_looped) { voice->has_looped = 1; start_index = voice->loopstart; start_point = dsp_data[voice->loopend - 1]; } } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index -= 2; /* set end back to third to last sample point */ } voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } /* 7th order interpolation. * Returns number of samples processed (usually FLUID_BUFSIZE but could be * smaller if end of sample occurs). */ int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice) { fluid_phase_t dsp_phase = voice->phase; fluid_phase_t dsp_phase_incr; short int *dsp_data = voice->sample->data; fluid_real_t *dsp_buf = voice->dsp_buf; fluid_real_t dsp_amp = voice->amp; fluid_real_t dsp_amp_incr = voice->amp_incr; unsigned int dsp_i = 0; unsigned int dsp_phase_index; unsigned int start_index, end_index; short int start_points[3]; short int end_points[3]; fluid_real_t *coeffs; int looping; /* Convert playback "speed" floating point value to phase index/fract */ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr); /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on * the 4th sample point */ fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000); /* voice is currently looping? */ looping = fluid_voice_is_looping(voice); /* last index before 7th interpolation point must be specially handled */ end_index = (looping ? voice->loopend - 1 : voice->end) - 3; if (voice->has_looped) /* set start_index and start point if looped or not */ { start_index = voice->loopstart; start_points[0] = dsp_data[voice->loopend - 1]; start_points[1] = dsp_data[voice->loopend - 2]; start_points[2] = dsp_data[voice->loopend - 3]; } else { start_index = voice->start; start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */ start_points[1] = start_points[0]; start_points[2] = start_points[0]; } /* get the 3 points off the end (loop start if looping, duplicate point if end) */ if (looping) { end_points[0] = dsp_data[voice->loopstart]; end_points[1] = dsp_data[voice->loopstart + 1]; end_points[2] = dsp_data[voice->loopstart + 2]; } else { end_points[0] = dsp_data[voice->end]; end_points[1] = end_points[0]; end_points[2] = end_points[0]; } while (1) { dsp_phase_index = fluid_phase_index (dsp_phase); /* interpolate first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[2] + coeffs[1] * (fluid_real_t)start_points[1] + coeffs[2] * (fluid_real_t)start_points[0] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index++; /* interpolate 2nd to first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[1] + coeffs[1] * (fluid_real_t)start_points[0] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index++; /* interpolate 3rd to first sample point (start or loop start) if needed */ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)start_points[0] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } start_index -= 2; /* set back to original start index */ /* interpolate the sequence of sample points */ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } /* break out if buffer filled */ if (dsp_i >= FLUID_BUFSIZE) break; end_index++; /* we're now interpolating the 3rd to last point */ /* interpolate within 3rd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2] + coeffs[6] * (fluid_real_t)end_points[0]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the 2nd to last point */ /* interpolate within 2nd to last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1] + coeffs[5] * (fluid_real_t)end_points[0] + coeffs[6] * (fluid_real_t)end_points[1]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } end_index++; /* we're now interpolating the last point */ /* interpolate within last point */ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) { coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)]; dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3] + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2] + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1] + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index] + coeffs[4] * (fluid_real_t)end_points[0] + coeffs[5] * (fluid_real_t)end_points[1] + coeffs[6] * (fluid_real_t)end_points[2]); /* increment phase and amplitude */ fluid_phase_incr (dsp_phase, dsp_phase_incr); dsp_phase_index = fluid_phase_index (dsp_phase); dsp_amp += dsp_amp_incr; } if (!looping) break; /* break out if not looping (end of sample) */ /* go back to loop start */ if (dsp_phase_index > end_index) { fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart); if (!voice->has_looped) { voice->has_looped = 1; start_index = voice->loopstart; start_points[0] = dsp_data[voice->loopend - 1]; start_points[1] = dsp_data[voice->loopend - 2]; start_points[2] = dsp_data[voice->loopend - 3]; } } /* break out if filled buffer */ if (dsp_i >= FLUID_BUFSIZE) break; end_index -= 3; /* set end back to 4th to last sample point */ } /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on * the 4th sample point (correct back to real value) */ fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000); voice->phase = dsp_phase; voice->amp = dsp_amp; return (dsp_i); } fluidsynth-1.1.9/src/unused/fluid_dsp_simple.c000066400000000000000000000111571322272076000215010ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* Purpose: * Low-level voice processing: * * - interpolates (obtains values between the samples of the original waveform data) * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) * - mixes the processed sample to left and right output using the pan setting * - sends the processed sample to chorus and reverb * * * This file does -not- generate an object file. * Instead, it is #included in several places in fluid_voice.c. * The motivation for this is * - Calling it as a subroutine may be time consuming, especially with optimization off * - The previous implementation as a macro was clumsy to handle * * * Fluid_voice.c sets a couple of variables before #including this: * - dsp_data: Pointer to the original waveform data * - dsp_left_buf: The generated signal goes here, left channel * - dsp_right_buf: right channel * - dsp_reverb_buf: Send to reverb unit * - dsp_chorus_buf: Send to chorus unit * - dsp_start: Start processing at this output buffer index * - dsp_end: End processing just before this output buffer index * - dsp_a1: Coefficient for the filter * - dsp_a2: same * - dsp_b0: same * - dsp_b1: same * - dsp_b2: same * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use * the filter at all. If it is left at its default setting * of roughly 20 kHz, there is no need to apply filterling.) * - dsp_interp_method: Which interpolation method to use. * - voice holds the voice structure * * Some variables are set and modified: * - dsp_phase: The position in the original waveform data. * This has an integer and a fractional part (between samples). * - dsp_phase_incr: For each output sample, the position in the original * waveform advances by dsp_phase_incr. This also has an integer * part and a fractional part. * If a sample is played at root pitch (no pitch change), * dsp_phase_incr is integer=1 and fractional=0. * - dsp_amp: The current amplitude envelope value. * - dsp_amp_incr: The changing rate of the amplitude envelope. * * A couple of variables are used internally, their results are discarded: * - dsp_i: Index through the output buffer * - dsp_phase_fractional: The fractional part of dsp_phase * - dsp_coeff: A table of four coefficients, depending on the fractional phase. * Used to interpolate between samples. * - dsp_process_buffer: Holds the processed signal between stages * - dsp_centernode: delay line for the IIR filter * - dsp_hist1: same * - dsp_hist2: same * */ /* Nonoptimized DSP loop */ #warning "This code is meant for experiments only."; /* wave table interpolation */ for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) { dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; dsp_phase_index = fluid_phase_index(dsp_phase); dsp_sample = (dsp_amp * (dsp_coeff->a0 * dsp_data[dsp_phase_index] + dsp_coeff->a1 * dsp_data[dsp_phase_index+1] + dsp_coeff->a2 * dsp_data[dsp_phase_index+2] + dsp_coeff->a3 * dsp_data[dsp_phase_index+3])); /* increment phase and amplitude */ fluid_phase_incr(dsp_phase, dsp_phase_incr); dsp_amp += dsp_amp_incr; /* filter */ /* The filter is implemented in Direct-II form. */ dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2; dsp_hist2 = dsp_hist1; dsp_hist1 = dsp_centernode; /* pan */ dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample; dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample; /* reverb */ if (dsp_reverb_buf){ dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample; } /* chorus */ if (dsp_chorus_buf){ dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample; } } fluidsynth-1.1.9/src/unused/fluid_event_queue.c000066400000000000000000000050241322272076000216630ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* * Josh Green * 2009-05-28 */ #include "fluid_event_queue.h" #include "fluidsynth_priv.h" /** * Create a lock free queue with a fixed maximum count and size of elements. * @param count Count of elements in queue (fixed max number of queued elements) * @return New lock free queue or NULL if out of memory (error message logged) * * Lockless FIFO queues don't use any locking mechanisms and can therefore be * advantageous in certain situations, such as passing data between a lower * priority thread and a higher "real time" thread, without potential lock * contention which could stall the high priority thread. Note that there may * only be one producer thread and one consumer thread. */ fluid_event_queue_t * fluid_event_queue_new (int count) { fluid_event_queue_t *queue; fluid_return_val_if_fail (count > 0, NULL); queue = FLUID_NEW (fluid_event_queue_t); if (!queue) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } queue->array = FLUID_ARRAY (fluid_event_queue_elem_t, count); if (!queue->array) { FLUID_FREE (queue); FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } /* Clear array, in case dynamic pointer reclaiming is being done */ FLUID_MEMSET (queue->array, 0, sizeof (fluid_event_queue_elem_t) * count); queue->totalcount = count; queue->count = 0; queue->in = 0; queue->out = 0; return (queue); } /** * Free an event queue. * @param queue Lockless queue instance * * Care must be taken when freeing a queue, to ensure that the consumer and * producer threads will no longer access it. */ void fluid_event_queue_free (fluid_event_queue_t *queue) { FLUID_FREE (queue->array); FLUID_FREE (queue); } fluidsynth-1.1.9/src/unused/fluid_event_queue.h000066400000000000000000000154741322272076000217020ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_EVENT_QUEUE_H #define _FLUID_EVENT_QUEUE_H #include "fluid_sys.h" #include "fluid_midi.h" #include "fluid_ringbuffer.h" /** * Type of queued event. */ enum fluid_event_queue_elem { FLUID_EVENT_QUEUE_ELEM_MIDI, /**< MIDI event. Uses midi field of event value */ FLUID_EVENT_QUEUE_ELEM_UPDATE_GAIN, /**< Update synthesizer gain. No payload value */ FLUID_EVENT_QUEUE_ELEM_POLYPHONY, /**< Synth polyphony event. No payload value */ FLUID_EVENT_QUEUE_ELEM_GEN, /**< Generator event. Uses gen field of event value */ FLUID_EVENT_QUEUE_ELEM_PRESET, /**< Preset set event. Uses preset field of event value */ FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, /**< Stop voices event. Uses ival field of event value */ FLUID_EVENT_QUEUE_ELEM_FREE_PRESET, /**< Free preset return event. Uses pval field of event value */ FLUID_EVENT_QUEUE_ELEM_SET_TUNING, /**< Set tuning event. Uses set_tuning field of event value */ FLUID_EVENT_QUEUE_ELEM_REPL_TUNING, /**< Replace tuning event. Uses repl_tuning field of event value */ FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING /**< Unref tuning return event. Uses unref_tuning field of event value */ }; /** * SoundFont generator set event structure. */ typedef struct { int channel; /**< MIDI channel number */ int param; /**< FluidSynth generator ID */ float value; /**< Value for the generator (absolute or relative) */ int absolute; /**< 1 if value is absolute, 0 if relative */ } fluid_event_gen_t; /** * Preset channel assignment event structure. */ typedef struct { int channel; /**< MIDI channel number */ fluid_preset_t *preset; /**< Preset to assign (synth thread owns) */ } fluid_event_preset_t; /** * Tuning assignment event structure. */ typedef struct { char apply; /**< TRUE to set tuning in realtime */ int channel; /**< MIDI channel number */ fluid_tuning_t *tuning; /**< Tuning to assign */ } fluid_event_set_tuning_t; /** * Tuning replacement event structure. */ typedef struct { char apply; /**< TRUE if tuning change should be applied in realtime */ fluid_tuning_t *old_tuning; /**< Old tuning pointer to replace */ fluid_tuning_t *new_tuning; /**< New tuning to assign */ } fluid_event_repl_tuning_t; /** * Tuning unref event structure. */ typedef struct { fluid_tuning_t *tuning; /**< Tuning to unref */ int count; /**< Number of times to unref */ } fluid_event_unref_tuning_t; /** * Structure for an integer parameter sent to a MIDI channel (bank or SoundFont ID for example). */ typedef struct { int channel; int val; } fluid_event_channel_int_t; /** * Event queue element structure. */ typedef struct { char type; /**< fluid_event_queue_elem */ union { fluid_midi_event_t midi; /**< If type == FLUID_EVENT_QUEUE_ELEM_MIDI */ fluid_event_gen_t gen; /**< If type == FLUID_EVENT_QUEUE_ELEM_GEN */ fluid_event_preset_t preset; /**< If type == FLUID_EVENT_QUEUE_ELEM_PRESET */ fluid_event_set_tuning_t set_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_SET_TUNING */ fluid_event_repl_tuning_t repl_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_REPL_TUNING */ fluid_event_unref_tuning_t unref_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING */ double dval; /**< A floating point payload value */ int ival; /**< An integer payload value */ void *pval; /**< A pointer payload value */ }; } fluid_event_queue_elem_t; typedef struct _fluid_ringbuffer_t fluid_event_queue_t; static FLUID_INLINE fluid_event_queue_t * fluid_event_queue_new (int count) { return (fluid_event_queue_t *) new_fluid_ringbuffer(count, sizeof(fluid_event_queue_elem_t)); } static FLUID_INLINE void fluid_event_queue_free (fluid_event_queue_t *queue) { delete_fluid_ringbuffer(queue); } /** * Get pointer to next input array element in queue. * @param queue Lockless queue instance * @return Pointer to array element in queue to store data to or NULL if queue is full * * This function along with fluid_queue_next_inptr() form a queue "push" * operation and is split into 2 functions to avoid an element copy. Note that * the returned array element pointer may contain the data of a previous element * if the queue has wrapped around. This can be used to reclaim pointers to * allocated memory, etc. */ static FLUID_INLINE fluid_event_queue_elem_t * fluid_event_queue_get_inptr (fluid_event_queue_t *queue) { return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_inptr(queue, 0); } /** * Advance the input queue index to complete a "push" operation. * @param queue Lockless queue instance * * This function along with fluid_queue_get_inptr() form a queue "push" * operation and is split into 2 functions to avoid element copy. */ static FLUID_INLINE void fluid_event_queue_next_inptr (fluid_event_queue_t *queue) { fluid_ringbuffer_next_inptr(queue, 1); } /** * Get pointer to next output array element in queue. * @param queue Lockless queue instance * @return Pointer to array element data in the queue or NULL if empty, can only * be used up until fluid_queue_next_outptr() is called. * * This function along with fluid_queue_next_outptr() form a queue "pop" * operation and is split into 2 functions to avoid an element copy. */ static FLUID_INLINE fluid_event_queue_elem_t * fluid_event_queue_get_outptr (fluid_event_queue_t *queue) { return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_outptr(queue); } /** * Advance the output queue index to complete a "pop" operation. * @param queue Lockless queue instance * * This function along with fluid_queue_get_outptr() form a queue "pop" * operation and is split into 2 functions to avoid an element copy. */ static FLUID_INLINE void fluid_event_queue_next_outptr (fluid_event_queue_t *queue) { fluid_ringbuffer_next_outptr(queue); } #endif /* _FLUID_EVENT_QUEUE_H */ fluidsynth-1.1.9/src/unused/fluid_rvoice_handler.c000066400000000000000000000136141322272076000223260ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_rvoice_handler.h" fluid_rvoice_handler_t* new_fluid_rvoice_handler(void) { fluid_rvoice_handler_t* handler; handler = FLUID_NEW(fluid_rvoice_handler_t); if (handler == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } FLUID_MEMSET(handler, 0, sizeof(fluid_rvoice_handler_t)); return handler; } void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler) { if (handler == NULL) return; #if 0 FLUID_FREE(handler->finished_voices); #endif FLUID_FREE(handler->voices); FLUID_FREE(handler); } int fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice) { if (handler->active_voices >= handler->polyphony) { FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_handler_add_voice"); return FLUID_FAILED; } handler->voices[handler->active_voices++] = voice; return FLUID_OK; } /** * Update polyphony - max number of voices (NOTE: not hard real-time capable) * @return FLUID_OK or FLUID_FAILED */ int fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value) { void* newptr; if (handler->active_voices > value) return FLUID_FAILED; #if 0 if (handler->finished_voice_count > value) return FLUID_FAILED; #endif newptr = FLUID_REALLOC(handler->voices, value * sizeof(fluid_rvoice_t*)); if (newptr == NULL) return FLUID_FAILED; handler->voices = newptr; #if 0 newptr = FLUID_REALLOC(handler->finished_voices, value * sizeof(fluid_rvoice_t*)); if (newptr == NULL) return FLUID_FAILED; handler->finished_voices = newptr; #endif handler->polyphony = value; return FLUID_OK; } static void fluid_rvoice_handler_remove_voice(fluid_rvoice_handler_t* handler, int index) { #if 0 if (handler->finished_voice_count < handler->polyphony) handler->finished_voices[handler->finished_voice_count++] = handler->voices[index]; #endif if (handler->remove_voice_callback != NULL) handler->remove_voice_callback(handler->remove_voice_callback_userdata, handler->voices[index]); handler->active_voices--; if (index < handler->active_voices) /* Move the last voice into the "hole" */ handler->voices[index] = handler->voices[handler->active_voices]; } /** * Synthesize one voice * @return Number of samples written */ #if 0 static inline int fluid_rvoice_handler_write_one(fluid_rvoice_handler_t* handler, int index, fluid_real_t* buf, int blockcount) { int i, result = 0; fluid_rvoice_t* voice = handler->voices[index]; for (i=0; i < blockcount; i++) { int s = fluid_rvoice_write(voice, buf); if (s == -1) { FLUID_MEMSET(buf, 0, FLUID_BUFSIZE*sizeof(fluid_real_t)); s = FLUID_BUFSIZE; } buf += s; result += s; } return result; } #endif /** * Synthesize one voice and add to buffer. * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means * voice has been finished, removed and possibly replaced with another voice. * @return Number of samples written */ static inline int fluid_rvoice_handler_mix_one(fluid_rvoice_handler_t* handler, int index, fluid_real_t** bufs, unsigned int blockcount, unsigned int bufcount) { unsigned int i, j=0, result = 0; fluid_rvoice_t* voice = handler->voices[index]; fluid_real_t local_buf[FLUID_BUFSIZE*blockcount]; for (i=0; i < blockcount; i++) { int s = fluid_rvoice_write(voice, &local_buf[FLUID_BUFSIZE*i]); if (s == -1) { s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */ FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t*)); } result += s; if (s < FLUID_BUFSIZE) { j = 1; break; } } fluid_rvoice_buffers_mix(&voice->buffers, local_buf, result, bufs, bufcount); if (j) fluid_rvoice_handler_remove_voice(handler, index); return result; } static inline void fluid_resetbufs(int blockcount, int bufcount, fluid_real_t** bufs) { int i; for (i=0; i < bufcount; i++) FLUID_MEMSET(bufs[i], 0, blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t)); } /** * Single-threaded scenario, no worker threads */ static inline void fluid_rvoice_handler_render_loop_simple(fluid_rvoice_handler_t* handler, int blockcount, int bufcount, fluid_real_t** bufs) { int i; int scount = blockcount * FLUID_BUFSIZE; for (i=0; i < handler->active_voices; i++) { int s = fluid_rvoice_handler_mix_one(handler, i, bufs, blockcount, bufcount); if (s < scount) i--; /* Need to render the moved voice as well */ } } /** * @param blockcount number of samples to render is blockcount*FLUID_BUFSIZE * @param bufcount number of buffers to render into * @param bufs array of bufcount buffers, each containing blockcount*FLUID_BUFSIZE samples */ void fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler, int blockcount, int bufcount, fluid_real_t** bufs) { fluid_resetbufs(blockcount, bufcount, bufs); fluid_rvoice_handler_render_loop_simple(handler, blockcount, bufcount, bufs); } fluidsynth-1.1.9/src/unused/fluid_rvoice_handler.h000066400000000000000000000051401322272076000223260ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RVOICE_HANDLER_H #define _FLUID_RVOICE_HANDLER_H #include "fluid_rvoice.h" #include "fluid_sys.h" typedef struct _fluid_rvoice_handler_t fluid_rvoice_handler_t; struct _fluid_rvoice_handler_t { fluid_rvoice_t** voices; /* Sorted so that all nulls are last */ int polyphony; /* Length of voices array */ int active_voices; /* Number of non-null voices */ #if 0 fluid_rvoice_t** finished_voices; /* List of voices who have finished */ int finished_voice_count; #endif void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Recieve this callback every time a voice is removed */ void* remove_voice_callback_userdata; }; int fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice); int fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value); void fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler, int blockcount, int bufcount, fluid_real_t** bufs); static FLUID_INLINE void fluid_rvoice_handler_set_voice_callback( fluid_rvoice_handler_t* handler, void (*func)(void*, fluid_rvoice_t*), void* userdata) { handler->remove_voice_callback_userdata = userdata; handler->remove_voice_callback = func; } #if 0 static FLUID_INLINE fluid_rvoice_t** fluid_rvoice_handler_get_finished_voices(fluid_rvoice_handler_t* handler, int* count) { *count = handler->finished_voice_count; return handler->finished_voices; } static inline void fluid_rvoice_handler_clear_finished_voices(fluid_rvoice_handler_t* handler) { handler->finished_voice_count = 0; } #endif fluid_rvoice_handler_t* new_fluid_rvoice_handler(void); void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler); #endif fluidsynth-1.1.9/src/unused/macbuild/000077500000000000000000000000001322272076000175665ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/macbuild/Makefile.am000066400000000000000000000001421322272076000216170ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in EXTRA_DIST = fluidsynth.mcp README.txt fluidsynth-1.1.9/src/unused/macbuild/README.txt000066400000000000000000000014561322272076000212720ustar00rootroot00000000000000FluidSynth MacOS9 port ---------------------- Antoine Schmitt March 2003 1) Configuring As Macintosh cannot run the configure shell command, you need to do it yourself. Mainly, this consists in: - creating the include/fluidsynth/version.h file by hand, using the version.h.in file as a template, and defining the VERSION* defines by hand, using the values from the 'configure.ac' file. Example: #define FLUIDSYNTH_VERSION "1.0.0" #define FLUIDSYNTH_VERSION_MAJOR 1 #define FLUIDSYNTH_VERSION_MINOR 0 #define FLUIDSYNTH_VERSION_MICRO 0 2) src/config_macos.h file In this file, you can define which audio driver to use. Only the SoundManager driver has been tested (SNDMAN_SUPPORT). You can choose the PORTAUDIO driver. 3) Compiler, etc.. This project has been compiled with CodeWarrior 4.0 on MacOS9.2.2 fluidsynth-1.1.9/src/unused/macbuild/fluidsynth.mcp000066400000000000000000006157751322272076000225050ustar00rootroot00000000000000cool( 5 ]CodeWarrior Project QRX3@%3 #ifdef LADSPA JavaClasses.jarZIP MWZP config_macos.her.h Merge Out????APPLDLGXckidProjWSPCU\U\JavaClasses.jarZIP MWZPDo{| config_macos.her.h iiwusynth.out PPC????APPL@X????JavaClasses.zipJavaClasses.zip  SXy  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW ?__sta__start    1[  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW Merge Out????APPLDLGXckidProjWSPC67'+) #=>Bw12<pji\|?@Ay~x}"v042E(,* $!%/HJKDYI{uG.      !"#$%&'()*+,-./L- o`ng0ralk_hbm^MC;&:9  q[NOesdc ZPQWRST8UV]fXzt153F Std C Console 68K????APPLX????U {JavaClasses.jarZIP MWZPJavaClasses.jarZIP MWZPWW V Std C Console 68K????APPLX????U {JavaClasses.zipMSIEInternet ExplorerIexplore.exe a.out@U {MSIEInternet ExplorerIexplore.exe a.out@U {__startP'CODE' 'DATA' 'PICT' config_macos.her.hANSI Console MultiFirst Segment:a.outLib Import 68KMPW Import 68KBalloon HelpMW C/C++ 68KMW Pascal 68KRezPEF Import 68KANSI Console 68kHelloWorld.cMSL C.68K Fa(4i_8d).LibMSL Runtime68K.LibMathLib68K Fa(4i/8d).LibMSL C++.68K Fa(4i_8d).LibMacOS.libMSL SIOUX.68K.LibANSI Console PPC:ANSI Console PPC.outLib Import PPCMW C/C++ PPCMW Pascal PPCPPCAsmXCOFF Import PPCPEF Import PPCMSL C++.PPC.LibMSL C.PPC.LibInterfaceLibMathLibMSL RuntimePPC.LibMSL SIOUX.PPC.Lib:ANSI Console 68k.outANSI Console FAT:Merge Out:ANSI Console FAT.outANSI Console 68k.outANSI Console PPC.outMathLib68K Fa(4i_8d).Lib:ANSI C Console 68kANSI C Console 68k:ANSI C Console PPCANSI C Console PPC:ANSI C Console FATANSI C Console FATGameCode ConverterFlex PreprocessorBison Preprocessor:Std C Console 68KStd C Console 68K:Std C Console PPCStd C Console PPC:Std C Console FATStd C Console FAT68K Standard C Console68K Std C ConsolePPC Std C ConsoleMSL C.68K (2i).LibMSL C++.68K (2i).LibMathLib68K (2i).Lib:HelloWorld.c:Bin:MSL C++.PPC.Lib:Bin:MSL C.PPC.Lib:Libraries:MacOS Common:InterfaceLib:Libraries:MacOS Common:MathLib:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.Lib:Bin:MSL SIOUX.PPC.LibMacOS PPC LinkerCustom KeywordsAccess PathsTarget SettingsFile MappingsBuild ExtrasDebugger TargetC/C++ CompilerC/C++ WarningsFTP PanelIR OptimizerPascal CompilerPascal WarningsPPC CodeGenPPC DisassemblerPPC LinkerPPC PEFPPC ProjectPPCAsm PanelRez CompilerPPC Global OptimizerSource TreesDebugger RuntimePerl PanelMW Perl:iiwusynth.outsmurf.ciiwu_synth.ciiwu_midi.ciiwuplay.ciiwu_auport.ciiwu_portaudio.cpa_mac.cpa_lib.cpatest_devinfo.cSoundLibiiwusynth.ciiwu_midishare.c:iiwusynth.:iiwusynthiiwu_list.ciiwu_cmd.ciiwu_sfont.ciiwusynthiiwuplay:iiwuplaymulti_synth.ciiwusynth_ASIOPa_ASIO.libstaticlib-iiwusynthdynamiclib-iiwusynth:staticlib-iiwusynth:dynaliclib-iiwusynth:lib_iiwusynth_s:lib_iiwusynth_dstaticlibdynamiclib:libiiwusynthstatic:lib_iiwusynth_static:lib_iiwusynth_dynamiciiwusynth.expiiwu_defsfont.ciiwu_aufile.ciiwu_chan.ciiwu_chorus.ciiwu_mod.ciiwu_sys.ciiwu_voice.ciiwu_rev.ciiwu_conv.ciiwu_gen.cpa_host.hpa_trace.cpa_trace.hconsole.stubs.cconfig_macos.hconfig_macosx.hconfig_win32.hiiwusynth.hiiwusynth_priv.hiiwu_auport.hiiwu_chan.hiiwu_chorus.hiiwu_cmd.hiiwu_conv.hiiwu_defsfont.hiiwu_gen.hiiwu_midi.hiiwu_mod.hiiwu_phase.hiiwu_rev.hiiwu_synth.hiiwu_sys.hiiwu_tree.hiiwu_voice.hiiwu_event.hiiwu_seq.hiiwu_event.ciiwu_seq.ciiwu_list.hiiwu_synthbind.hiiwu_synthbind.ciiwu_seqbind.ciiwu_seqbind.hiiwu_event_priv.hstaticlib DEBUG:lib_iiwusynth_static_debugiiwusynth_outonly:iiwusynth_outonlyportaudio.h:iiwusynth.shlib:iiwusynth_no_midi_in.lib:iiwusynth_no_midi_in.shlibstaticlib no midi instaticlib all:iiwusynth.libstaticlib DEBUG no midi instaticlib DEBUG all:iiwusynth_out_debug.lib:iiwusynth_debug.lib:iiwusynth_out.libiiwusynth no midi in:iiwusynth_outdynamiclib alldynamiclib no midi out:iiwusynth_out.shlibdynamiclib no midi indynamiclib seq outiiwu_dsound.ciiwu_jack.ciiwu_oss.ciiwu_samp.hiiwu_tree.ciiwu_types.hiiwusynth_out.shlibsequencer dll no midi in:iiwusynth_seq_out.shlibiiwu_ramsfont.ciiwu_ramsfont.hiiwu_hash.ciiwu_hash.hiiwu_settings.ciiwu_settings.hiiwu_strtok.ciiwu_strtok.hiiwu_tuning.ciiwu_tuning.hevent.hseq.hseqbind.hiiwu_io.hiiwu_io.ciiwu_sndmgr.ciiwu_adriver.ciiwu_adriver.hiiwu_mdriver.ciiwu_mdriver.hmidi.hiiwu_midi_router.ciiwu_midi_router.haudio.hgen.hlog.hmisc.hmod.hramsfont.hsettings.hsfont.hshell.hsynth.htypes.hversion.hvoice.hfluidsynth.hfluidsynth.cfluidsynth_priv.hfluid_adriver.cfluid_adriver.hfluid_chan.cfluid_chan.hfluid_chorus.cfluid_chorus.hfluid_cmd.cfluid_cmd.hfluid_conv.cfluid_conv.hfluid_defsfont.cfluid_defsfont.hfluid_dll.cfluid_dsp_core.cfluid_event.cfluid_event_priv.hfluid_gen.cfluid_gen.hfluid_hash.cfluid_hash.hfluid_io.cfluid_io.hfluid_jack.cfluid_ladspa.cfluid_ladspa.hfluid_list.cfluid_list.hfluid_mdriver.cfluid_mdriver.hfluid_midi.cfluid_midi.hfluid_midishare.cfluid_midi_router.cfluid_midi_router.hfluid_mod.cfluid_mod.hfluid_oss.cfluid_phase.hfluid_portaudio.cfluid_ramsfont.cfluid_ramsfont.hfluid_rev.cfluid_rev.hfluid_seq.cfluid_seqbind.cfluid_settings.cfluid_settings.hfluid_sfont.hfluid_sndmgr.cfluid_sse.hfluid_strtok.cfluid_strtok.hfluid_synth.cfluid_synth.hfluid_sys.cfluid_sys.hfluid_tuning.cfluid_tuning.hfluid_voice.cfluid_voice.hfluid_winmidi.ciiwusynth.lib:fluidsynth.lib:fluidsynth.shlibok=B{2>f|3>n    2W__sta__start NONAME.EXE@MSIEInternet ExplorerIexplore.exe a.out@U { JBoundApp????WINDnullMSIEhttp://java.sun.com/products/jdk/1.1/docs/api/ @ :@::src:@::include:@ :MSL:werks Standard Library:MSL Precompiled Header:@:MacOS Support:@ Merge Out????APPLDLGXckidProjWSPC__startfluidsynth.libidi_in.lib????????????ROOTFILEWGRUP fluidsynthFILEGRUPincludesFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEFILEGRUPcommon36?@ABCDEFGHIJKL5MNOPVWXYRUZ[\QT]^_`abcdefghijSklmn~z}y|{$%&' #()*"+,-./012345678!9:;<`abc\_def[^ghijklmnopqrst]uvwx  BCDE>AFGH=@IJKLMNOPQRSTUV?WXYZstuvwmpxyzlqro{|}~kn"#$%&'() !*+,-./0123456789bcdef\_ghi[`a^jklmnopqrstuvwxyZ]TUVWXNQYZ[MRSP\]^_`abcdLOefghijz{|}~tuvwpsxyzor{|}~q     56789/2:;<.341=>?@ABCDE-0FGHIJK !"#$%&'()*+,     BCDEF<?GHI;@A>JKLMNOPQRSTUVWXY:= !"#$%&'()*+,-./012     !M\(J7OlFS`lin r dbjd*objd@O\jobqptlace=g !"#$$1%F& ['t(0)@*Jav+.zip,-./0ut!142F3Y4k5APPL~6GXck7PC89 :;%<=>*?=@bABCDEFG HI$J4KCLRM\NiOyPQRSTUVWXYZ[\%]4^<_I`Ua`bncdefghijklmno pq r.s=tIu]vrwxyz{|}~#3AM[fq~  ) 5 C N Z j u   " 3 B Q c s   * 9 T h   1 ? K V b n {  ' 5 C Q Y _ i s }  +3;CKU]jw$5AR`s      -:L`t !"0#>$M%Y&h'w()*+,-./0123 @ :@::src:@::include:@ :MSL:werks Standard Library:MSL Precompiled Header:@:MacOS Support:@WW Vdynamiclib:__start @ :@::src:@::include:@ :MSL:werks Standard Library:MSL Precompiled Header:@:MacOS Support:@  +3;CKU]jw$5AR`s      -:L`t !"0#>$M%Y&h'w()*+,-./01iiwusynth.shlibbi_in.shlib????shlb???? NONAME.EXE@ut.shlibn.shlib????shlb???? JBoundApp????WINDnullMSIEhttp://java.sun.com/products/jdk/1.1/docs/api/NoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86NoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86NoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86WW VP'CODE' 'DATA' 'PICT'    g1[      !"#$%&'()*+,-.///0    g2W      !"#$%&'()*+,-.///0|#8m`ci__startP'CODE' 'DATA' 'PICT' NONAME.EXE@    g1HIOQ[]`abcfghiklmnloqristuvwcxyz{_|}~`m MSIEInternet ExplorererIexplore.exeoreruJITNoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86    g2WY\]^_bcdeghijlkl`mniopqrsctuvw_xyz{|}~ @ :@::src:@:::iiwusynth_pamac:@ ::include:@ :MSL:werks Standard Library:MSL Precompiled Header:@:MacOS Support:@NoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86P'CODE' 'DATA' 'PICT'NoneMMPr@TEXT.cRunTSScriptTEXT.plMW PerlJava Linker .auJAR Importer@ .gifJAR Importer@RSRC`TEXT.cRunTSScriptTEXT.htmlTEXT.javaMW JavaTEXT.mfTEXT.plMW Perlrsrc`.classMW Java.zipMW JavaMacOS 68K LinkerAPPL`Appl`MMLBLib Import 68KMPLFLib Import 68KMWCD`OBJ MPW Import 68KPLob`RSRC`TEXT.bhBalloon HelpTEXT.cMW C/C++ 68KTEXT.c++MW C/C++ 68KTEXT.ccMW C/C++ 68KTEXT.cpMW C/C++ 68KTEXT.cppMW C/C++ 68KTEXT.expTEXT.hMW C/C++ 68KTEXT.pchMW C/C++ 68KTEXT.pch++MW C/C++ 68KTEXT.plMW PerlTEXT.rRezTEXT.segdocu`rsrc`shlbPEF Import 68KstubPEF Import 68K.docP.oMPW Import 68K.ppob`.rsrc`MacOS Merge APPL`Appl`RSRC`TEXT.bhBalloon HelpTEXT.cRunTSScriptTEXT.plMW PerlTEXT.rRezrsrc`shlbMacOS PPC LinkerAPPL`Appl`MMLBLib Import PPCMPLFLib Import PPCMWCD`RSRC`TEXT.arrTEXT.bhBalloon HelpTEXT.cMW C/C++ PPCTEXT.c++MW C/C++ PPCTEXT.ccMW C/C++ PPCTEXT.cpMW C/C++ PPCTEXT.cppMW C/C++ PPCTEXT.expTEXT.hMW C/C++ PPCTEXT.pchMW C/C++ PPCTEXT.pch++MW C/C++ PPCTEXT.plMW PerlTEXT.rRezTEXT.sPPCAsmXCOFXCOFF Import PPCdocu`rsrc`shlbPEF Import PPCstubPEF Import PPC.docP.oXCOFF Import PPC.ppob`.rsrc`Win32 x86 Linker????Obj Import x86TEXT.cMW C/C++ x86TEXT.c++MW C/C++ x86TEXT.ccMW C/C++ x86TEXT.cpMW C/C++ x86TEXT.cppMW C/C++ x86TEXT.defTEXT.hMW C/C++ x86TEXT.h++MW C/C++ x86TEXT.hppMW C/C++ x86TEXT.ordTEXT.pchMW C/C++ x86TEXT.pch++MW C/C++ x86TEXT.plMW PerlTEXT.rcMW WinRCTEXT.resWinRes ImportiLIBLib Import x86iOBJObj Import x86.aLib Import x86.docP.libLib Import x86.oObj Import x86.objObj Import x86#CshPf0 AddPinRaPoiMIte tRes' MenuI tConk Cont oseCillCShowHideMove$GetCGetCRjzeColiteGetCtCTitlVainCt%Ctl Elue gl SeTestDragTrac Dra!"nded5#istT$r%&'()  **+B,d$`-}./shP001 Add2PinR3aPoi64MIteX5tRest6Menu7tCon8Cont9oseC:illC;Show#GetC}?etCR@zeCoABGetCCtCTiDtlVa8EinCtTFCtl qGlue Hl SeITestJDragKTrac L Dra "M ;N ^O P Q R S T U 1V OW mXKeys Y ZImpo [PW I \KBa ]lpM "^68K C_l 68 b`F Im a bnded cist dTarg ect D fSett 8g Set PheSaf ni@ j k l m n` 'o L Dp%s fqg: r sANSI t 68k uKeyw vI Cowk:Ac1xhsAMyole jzet S{ANSI| 68k}ppin~ConsBuil0ANSTr4Wz5Ux?]|Be @c*Hg -Po +Jj"B]5Wv6Us -Ng#Ec:Xu5Sp .Gf  % C f     !!8!U!s!!!!""3"P"n"""" #!#'"#F##d$#%#&#'#($)$#*$F+$c,$}-$.$/$0$1%2%53%S4%z5%6%7%8&9&1:&Y;&{<&=&>&?'@',A'OB'sC'D'E'F( G(,H(TI(vJ(K(L(M)N)&O)IP)kQ)R)S)T)U*V*0W*NX*qY*Z*[*\*]+^+&_+D`+ca+}b+c+d+e,f,&g,Ch,]i,{j,k,l,m,n-o-3p-Uq-tr-s-t-u-v.w.3x.Qy.rz.{.|.}.~//"/F/h////00 0?0^000001161X11112232Y2v2222233.3I3c3y3333344!454Q4i44444555:5O5a5w5555566606J6c6x666667 77,7G7^7{777778 8'8;8L8a8w888899 9<9\9{9999::-:K:a:z::::;;1;R;m;;;;;<<5 >> >Y >w > >>??(?L?r????@@;@\@}@@@A A. AP!Av"A#A$A%B &B,'BM(Bq)B*B+B,B-C.CC/Cd0C1C2C3C4D5DC6Dc7D8D9D:D;D<E=E->EG?E_@EvAEBECEDEEEFFGF&HF?IFXJFiKF}LFMFNFOFPFQGRG2SGHTGcUGVGWGXGYGZG[H\H ]H8^HS_Hl`HaHbHcHdHeIfI$gI;hIUiIojIkIlImInIoJpJqJ5rJRsJitJuJvJwJxJyJzK{K.|KD}Kc~KKKKKKLL*L?LXLnLLLLLMM(MHM^MuMMMMN N*NKNkNNNNOO#OBOYO{OOOOPP,PIPfPPPPQQ"QAQcQQQQRR(RFRiRRRRS S&SJShSSSSTT&TETfTTTTU U/UTUvUUUVV+VLVrVVVWW%WAWhWWWWXX7XYX{XXXYY4YUYpYYYYYZZ;ZUZtZZZZ[[[9[S[r[[[[[\ \1 \K \n \ \\\]]?]j]]]^^9^e^^^_ _6_X_ _!_"`#`.$`Q%`x&`'`(`)a*aI+ay,a-a.a/b 0b,1bP2br3b4b5b6b7c!8cJ9ci:c;c<c=c>d?d0@dUAd~BdCdDdEdFeGe>He^IeJeKeLeMfNf6OfXPf}QfRfSfTg Ug.VgTWg~XgYgZg[h\h#]hJ^hk_h`hahbhcidi;ei]figihiiijjkj7ljSmjrnjojpjqjrksk%tk@uk`vkwkxkykzk{l |l,}lG~lglllllmm-mHmlmmmmnn4n[nnnnoo>ojooooppBpeppppqqAqeqqqrr(rKrnrrrss$sKsqssstt(tMtrtttuu+uLujuuuuvvDvovvvvww8wYwywwwxx+xIxhxxxxy y.yVytyyyyzz/zWzzzz{ {1{Z{{{{||I|w|||}}0}[}}}}~~B~h~~~ 7[ANSI Console Multi:Custom KeywordsANSI Console Multi:Access PathsANSI Console Multi:Target SettingsANSI Console Multi:File MappingsANSI Console Multi:Build ExtrasANSI Console Multi:68K CodeGenANSI Console Multi:68K DisassemblerANSI Console Multi:68K LinkerANSI Console Multi:68K ProjectANSI Console Multi:C/C++ CompilerANSI Console Multi:C/C++ WarningsANSI Console Multi:CFM68KANSI Console Multi:IR OptimizerANSI Console Multi:Java OutputANSI Console Multi:Java ProjectANSI Console Multi:Java VMANSI Console Multi:MacOS Merge PanelANSI Console Multi:Pascal CompilerANSI Console Multi:Pascal WarningsANSI Console Multi:PPC CodeGenANSI Console Multi:PPC DisassemblerANSI Console Multi:PPC LinkerANSI Console Multi:PPC PEFANSI Console Multi:PPC ProjectANSI Console Multi:PPCAsm PanelANSI Console Multi:Rez CompilerANSI Console Multi:WinRC CompilerANSI Console Multi:x86 CodeGenANSI Console Multi:x86 LinkerANSI Console Multi:x86 ProjectProject File ListANSI Console 68k:Custom KeywordsANSI Console 68k:Access PathsANSI Console 68k:Target SettingsANSI Console 68k:File MappingsANSI Console 68k:Build ExtrasANSI Console 68k:68K CodeGenANSI Console 68k:68K DisassemblerANSI Console 68k:68K LinkerANSI Console 68k:68K ProjectANSI Console 68k:C/C++ CompilerANSI Console 68k:C/C++ WarningsANSI Console 68k:CFM68KANSI Console 68k:IR OptimizerANSI Console 68k:Java OutputANSI Console 68k:Java ProjectANSI Console 68k:Java VMANSI Console 68k:MacOS Merge PanelANSI Console 68k:Pascal CompilerANSI Console 68k:Pascal WarningsANSI Console 68k:PPC CodeGenANSI Console 68k:PPC DisassemblerANSI Console 68k:PPC LinkerANSI Console 68k:PPC PEFANSI Console 68k:PPC ProjectANSI Console 68k:PPCAsm PanelANSI Console 68k:Rez CompilerANSI Console 68k:WinRC CompilerANSI Console 68k:x86 CodeGenANSI Console 68k:x86 LinkerANSI Console 68k:x86 ProjectANSI Console PPC:Custom KeywordsANSI Console PPC:Access PathsANSI Console PPC:Target SettingsANSI Console PPC:File MappingsANSI Console PPC:Build ExtrasANSI Console PPC:68K CodeGenANSI Console PPC:68K DisassemblerANSI Console PPC:68K LinkerANSI Console PPC:68K ProjectANSI Console PPC:C/C++ CompilerANSI Console PPC:C/C++ WarningsANSI Console PPC:CFM68KANSI Console PPC:IR OptimizerANSI Console PPC:Java OutputANSI Console PPC:Java ProjectANSI Console PPC:Java VMANSI Console PPC:MacOS Merge PanelANSI Console PPC:Pascal CompilerANSI Console PPC:Pascal WarningsANSI Console PPC:PPC CodeGenANSI Console PPC:PPC DisassemblerANSI Console PPC:PPC LinkerANSI Console PPC:PPC PEFANSI Console PPC:PPC ProjectANSI Console PPC:PPCAsm PanelANSI Console PPC:Rez CompilerANSI Console PPC:WinRC CompilerANSI Console PPC:x86 CodeGenANSI Console PPC:x86 LinkerANSI Console PPC:x86 ProjectANSI Console FAT:Custom KeywordsANSI Console FAT:Access PathsANSI Console FAT:Target SettingsANSI Console FAT:File MappingsANSI Console FAT:Build ExtrasANSI Console FAT:68K CodeGenANSI Console FAT:68K DisassemblerANSI Console FAT:68K LinkerANSI Console FAT:68K ProjectANSI Console FAT:C/C++ CompilerANSI Console FAT:C/C++ WarningsANSI Console FAT:CFM68KANSI Console FAT:IR OptimizerANSI Console FAT:Java OutputANSI Console FAT:Java ProjectANSI Console FAT:Java VMANSI Console FAT:MacOS Merge PanelANSI Console FAT:Pascal CompilerANSI Console FAT:Pascal WarningsANSI Console FAT:PPC CodeGenANSI Console FAT:PPC DisassemblerANSI Console FAT:PPC LinkerANSI Console FAT:PPC PEFANSI Console FAT:PPC ProjectANSI Console FAT:PPCAsm PanelANSI Console FAT:Rez CompilerANSI Console FAT:WinRC CompilerANSI Console FAT:x86 CodeGenANSI Console FAT:x86 LinkerANSI Console FAT:x86 ProjectANSI C Console 68k:Custom KeywordsANSI C Console 68k:Access PathsANSI C Console 68k:Target SettingsANSI C Console 68k:File MappingsANSI C Console 68k:Build ExtrasANSI C Console 68k:68K CodeGenANSI C Console 68k:68K DisassemblerANSI C Console 68k:68K LinkerANSI C Console 68k:68K ProjectANSI C Console 68k:C/C++ CompilerANSI C Console 68k:C/C++ WarningsANSI C Console 68k:CFM68KANSI C Console 68k:IR OptimizerANSI C Console 68k:MacOS Merge PanelANSI C Console 68k:Pascal CompilerANSI C Console 68k:Pascal WarningsANSI C Console 68k:PPC CodeGenANSI C Console 68k:PPC DisassemblerANSI C Console 68k:PPC LinkerANSI C Console 68k:PPC PEFANSI C Console 68k:PPC ProjectANSI C Console 68k:PPCAsm PanelANSI C Console 68k:Rez CompilerANSI C Console PPC:Custom KeywordsANSI C Console PPC:Access PathsANSI C Console PPC:Target SettingsANSI C Console PPC:File MappingsANSI C Console PPC:Build ExtrasANSI C Console PPC:68K CodeGenANSI C Console PPC:68K DisassemblerANSI C Console PPC:68K LinkerANSI C Console PPC:68K ProjectANSI C Console PPC:C/C++ CompilerANSI C Console PPC:C/C++ WarningsANSI C Console PPC:CFM68KANSI C Console PPC:IR OptimizerANSI C Console PPC:MacOS Merge PanelANSI C Console PPC:Pascal CompilerANSI C Console PPC:Pascal WarningsANSI C Console PPC:PPC CodeGenANSI C Console PPC:PPC DisassemblerANSI C Console PPC:PPC LinkerANSI C Console PPC:PPC PEFANSI C Console PPC:PPC ProjectANSI C Console PPC:PPCAsm PanelANSI C Console PPC:Rez CompilerANSI C Console FAT:Custom KeywordsANSI C Console FAT:Access PathsANSI C Console FAT:Target SettingsANSI C Console FAT:File MappingsANSI C Console FAT:Build ExtrasANSI C Console FAT:68K CodeGenANSI C Console FAT:68K DisassemblerANSI C Console FAT:68K LinkerANSI C Console FAT:68K ProjectANSI C Console FAT:C/C++ CompilerANSI C Console FAT:C/C++ WarningsANSI C Console FAT:CFM68KANSI C Console FAT:IR OptimizerANSI C Console FAT:MacOS Merge PanelANSI C Console FAT:Pascal CompilerANSI C Console FAT:Pascal WarningsANSI C Console FAT:PPC CodeGenANSI C Console FAT:PPC DisassemblerANSI C Console FAT:PPC LinkerANSI C Console FAT:PPC PEFANSI C Console FAT:PPC ProjectANSI C Console FAT:PPCAsm PanelANSI C Console FAT:Rez CompilerANSI C Console 68k:Java OutputANSI C Console 68k:Java ProjectANSI C Console 68k:Java VMANSI C Console 68k:WinRC CompilerANSI C Console 68k:x86 CodeGenANSI C Console 68k:x86 LinkerANSI C Console 68k:x86 ProjectANSI C Console PPC:Java OutputANSI C Console PPC:Java ProjectANSI C Console PPC:Java VMANSI C Console PPC:WinRC CompilerANSI C Console PPC:x86 CodeGenANSI C Console PPC:x86 LinkerANSI C Console PPC:x86 ProjectANSI C Console FAT:Java OutputANSI C Console FAT:Java ProjectANSI C Console FAT:Java VMANSI C Console FAT:WinRC CompilerANSI C Console FAT:x86 CodeGenANSI C Console FAT:x86 LinkerANSI C Console FAT:x86 ProjectStd C Console 68K:Custom KeywordsStd C Console 68K:Access PathsStd C Console 68K:Target SettingsStd C Console 68K:File MappingsStd C Console 68K:Build ExtrasStd C Console 68K:Bison PanelStd C Console 68K:Flex PanelStd C Console 68K:68K CodeGenStd C Console 68K:68K DisassemblerStd C Console 68K:68K LinkerStd C Console 68K:68K ProjectStd C Console 68K:C/C++ CompilerStd C Console 68K:C/C++ WarningsStd C Console 68K:CFM68KStd C Console 68K:IR OptimizerStd C Console 68K:Java OutputStd C Console 68K:Java ProjectStd C Console 68K:Java VMStd C Console 68K:MacOS Merge PanelStd C Console 68K:Pascal CompilerStd C Console 68K:Pascal WarningsStd C Console 68K:PPC CodeGenStd C Console 68K:PPC DisassemblerStd C Console 68K:PPC LinkerStd C Console 68K:PPC PEFStd C Console 68K:PPC ProjectStd C Console 68K:PPCAsm PanelStd C Console 68K:Rez CompilerStd C Console 68K:WinRC CompilerStd C Console 68K:x86 CodeGenStd C Console 68K:x86 LinkerStd C Console 68K:x86 ProjectStd C Console PPC:Custom KeywordsStd C Console PPC:Access PathsStd C Console PPC:Target SettingsStd C Console PPC:File MappingsStd C Console PPC:Build ExtrasStd C Console PPC:Bison PanelStd C Console PPC:Flex PanelStd C Console PPC:68K CodeGenStd C Console PPC:68K DisassemblerStd C Console PPC:68K LinkerStd C Console PPC:68K ProjectStd C Console PPC:C/C++ CompilerStd C Console PPC:C/C++ WarningsStd C Console PPC:CFM68KStd C Console PPC:IR OptimizerStd C Console PPC:Java OutputStd C Console PPC:Java ProjectStd C Console PPC:Java VMStd C Console PPC:MacOS Merge PanelStd C Console PPC:Pascal CompilerStd C Console PPC:Pascal WarningsStd C Console PPC:PPC CodeGenStd C Console PPC:PPC DisassemblerStd C Console PPC:PPC LinkerStd C Console PPC:PPC PEFStd C Console PPC:PPC ProjectStd C Console PPC:PPCAsm PanelStd C Console PPC:Rez CompilerStd C Console PPC:WinRC CompilerStd C Console PPC:x86 CodeGenStd C Console PPC:x86 LinkerStd C Console PPC:x86 ProjectStd C Console FAT:Custom KeywordsStd C Console FAT:Access PathsStd C Console FAT:Target SettingsStd C Console FAT:File MappingsStd C Console FAT:Build ExtrasStd C Console FAT:Bison PanelStd C Console FAT:Flex PanelStd C Console FAT:68K CodeGenStd C Console FAT:68K DisassemblerStd C Console FAT:68K LinkerStd C Console FAT:68K ProjectStd C Console FAT:C/C++ CompilerStd C Console FAT:C/C++ WarningsStd C Console FAT:CFM68KStd C Console FAT:IR OptimizerStd C Console FAT:Java OutputStd C Console FAT:Java ProjectStd C Console FAT:Java VMStd C Console FAT:MacOS Merge PanelStd C Console FAT:Pascal CompilerStd C Console FAT:Pascal WarningsStd C Console FAT:PPC CodeGenStd C Console FAT:PPC DisassemblerStd C Console FAT:PPC LinkerStd C Console FAT:PPC PEFStd C Console FAT:PPC ProjectStd C Console FAT:PPCAsm PanelStd C Console FAT:Rez CompilerStd C Console FAT:WinRC CompilerStd C Console FAT:x86 CodeGenStd C Console FAT:x86 LinkerStd C Console FAT:x86 Project68K Standard C Console:Custom Keywords68K Standard C Console:Access Paths68K Standard C Console:Target Settings68K Standard C Console:File Mappings68K Standard C Console:Build Extras68K Standard C Console:68K CodeGen68K Standard C Console:68K Disassembler68K Standard C Console:68K Linker68K Standard C Console:68K Project68K Standard C Console:C/C++ Compiler68K Standard C Console:C/C++ Warnings68K Standard C Console:CFM68K68K Standard C Console:IR Optimizer68K Standard C Console:Java Output68K Standard C Console:Java Project68K Standard C Console:Java VM68K Standard C Console:MacOS Merge Panel68K Standard C Console:Pascal Compiler68K Standard C Console:Pascal Warnings68K Standard C Console:PPC CodeGen68K Standard C Console:PPC Disassembler68K Standard C Console:PPC Linker68K Standard C Console:PPC PEF68K Standard C Console:PPC Project68K Standard C Console:PPCAsm Panel68K Standard C Console:Rez Compiler68K Standard C Console:WinRC Compiler68K Standard C Console:x86 CodeGen68K Standard C Console:x86 Linker68K Standard C Console:x86 Project68K Std C Console:Custom Keywords68K Std C Console:Access Paths68K Std C Console:Target Settings68K Std C Console:File Mappings68K Std C Console:Build Extras68K Std C Console:68K CodeGen68K Std C Console:68K Disassembler68K Std C Console:68K Linker68K Std C Console:68K Project68K Std C Console:C/C++ Compiler68K Std C Console:C/C++ Warnings68K Std C Console:CFM68K68K Std C Console:IR Optimizer68K Std C Console:Java Output68K Std C Console:Java Project68K Std C Console:Java VM68K Std C Console:MacOS Merge Panel68K Std C Console:Pascal Compiler68K Std C Console:Pascal Warnings68K Std C Console:PPC CodeGen68K Std C Console:PPC Disassembler68K Std C Console:PPC Linker68K Std C Console:PPC PEF68K Std C Console:PPC Project68K Std C Console:PPCAsm Panel68K Std C Console:Rez Compiler68K Std C Console:WinRC Compiler68K Std C Console:x86 CodeGen68K Std C Console:x86 Linker68K Std C Console:x86 ProjectPPC Std C Console:Custom KeywordsPPC Std C Console:Access PathsPPC Std C Console:Target SettingsPPC Std C Console:File MappingsPPC Std C Console:Build ExtrasPPC Std C Console:68K CodeGenPPC Std C Console:68K DisassemblerPPC Std C Console:68K LinkerPPC Std C Console:68K ProjectPPC Std C Console:C/C++ CompilerPPC Std C Console:C/C++ WarningsPPC Std C Console:CFM68KPPC Std C Console:IR OptimizerPPC Std C Console:Java OutputPPC Std C Console:Java ProjectPPC Std C Console:Java VMPPC Std C Console:MacOS Merge PanelPPC Std C Console:Pascal CompilerPPC Std C Console:Pascal WarningsPPC Std C Console:PPC CodeGenPPC Std C Console:PPC DisassemblerPPC Std C Console:PPC LinkerPPC Std C Console:PPC PEFPPC Std C Console:PPC ProjectPPC Std C Console:PPCAsm PanelPPC Std C Console:Rez CompilerPPC Std C Console:WinRC CompilerPPC Std C Console:x86 CodeGenPPC Std C Console:x86 LinkerPPC Std C Console:x86 ProjectPPC Std C Console:Java LanguagePPC Std C Console:Debugger TargetPPC Std C Console:FTP PanelPPC Std C Console:JavaDoc ProjectPPC Std C Console:x86 Exceptions PanelPPC Std C Console:68K Global OptimizerPPC Std C Console:PPC Global OptimizerPPC Std C Console:Source TreesPPC Std C Console:Debugger RuntimePPC Std C Console:Java Command LinePPC Std C Console:Java MacOS SettingsPPC Std C Console:Perl PanelPPC Std C Console:x86 Global Optimizeriiwusynth:Source Treesiiwusynth:Custom Keywordsiiwusynth:Access Pathsiiwusynth:Target Settingsiiwusynth:File Mappingsiiwusynth:Build Extrasiiwusynth:Debugger Runtimeiiwusynth:Debugger Targetiiwusynth:68K CodeGeniiwusynth:68K Disassembleriiwusynth:68K Global Optimizeriiwusynth:68K Linkeriiwusynth:68K Projectiiwusynth:C/C++ Compileriiwusynth:C/C++ Warningsiiwusynth:CFM68Kiiwusynth:FTP Paneliiwusynth:Java Command Lineiiwusynth:Java Languageiiwusynth:Java MacOS Settingsiiwusynth:Java Outputiiwusynth:Java Projectiiwusynth:JavaDoc Projectiiwusynth:MacOS Merge Paneliiwusynth:PPC CodeGeniiwusynth:PPC Disassembleriiwusynth:PPC Global Optimizeriiwusynth:PPC Linkeriiwusynth:PPC PEFiiwusynth:PPC Projectiiwusynth:PPCAsm Paneliiwusynth:Rez Compileriiwuplay:Source Treesiiwuplay:Custom Keywordsiiwuplay:Access Pathsiiwuplay:Target Settingsiiwuplay:File Mappingsiiwuplay:Build Extrasiiwuplay:Debugger Runtimeiiwuplay:Debugger Targetiiwuplay:68K CodeGeniiwuplay:68K Disassembleriiwuplay:68K Global Optimizeriiwuplay:68K Linkeriiwuplay:68K Projectiiwuplay:C/C++ Compileriiwuplay:C/C++ Warningsiiwuplay:CFM68Kiiwuplay:FTP Paneliiwuplay:Java Command Lineiiwuplay:Java Languageiiwuplay:Java MacOS Settingsiiwuplay:Java Outputiiwuplay:Java Projectiiwuplay:JavaDoc Projectiiwuplay:MacOS Merge Paneliiwuplay:PPC CodeGeniiwuplay:PPC Disassembleriiwuplay:PPC Global Optimizeriiwuplay:PPC Linkeriiwuplay:PPC PEFiiwuplay:PPC Projectiiwuplay:PPCAsm Paneliiwuplay:Rez Compileriiwusynth_ASIO:Source Treesiiwusynth_ASIO:Custom Keywordsiiwusynth_ASIO:Access Pathsiiwusynth_ASIO:Target Settingsiiwusynth_ASIO:File Mappingsiiwusynth_ASIO:Build Extrasiiwusynth_ASIO:Debugger Runtimeiiwusynth_ASIO:Debugger Targetiiwusynth_ASIO:68K CodeGeniiwusynth_ASIO:68K Disassembleriiwusynth_ASIO:68K Global Optimizeriiwusynth_ASIO:68K Linkeriiwusynth_ASIO:68K Projectiiwusynth_ASIO:C/C++ Compileriiwusynth_ASIO:C/C++ Warningsiiwusynth_ASIO:CFM68Kiiwusynth_ASIO:FTP Paneliiwusynth_ASIO:Java Command Lineiiwusynth_ASIO:Java Languageiiwusynth_ASIO:Java MacOS Settingsiiwusynth_ASIO:Java Outputiiwusynth_ASIO:Java Projectiiwusynth_ASIO:JavaDoc Projectiiwusynth_ASIO:MacOS Merge Paneliiwusynth_ASIO:PPC CodeGeniiwusynth_ASIO:PPC Disassembleriiwusynth_ASIO:PPC Global Optimizeriiwusynth_ASIO:PPC Linkeriiwusynth_ASIO:PPC PEFiiwusynth_ASIO:PPC Projectiiwusynth_ASIO:PPCAsm Paneliiwusynth_ASIO:Rez Compilerstaticlib-iiwusynth:Source Treesstaticlib-iiwusynth:Custom Keywordsstaticlib-iiwusynth:Access Pathsstaticlib-iiwusynth:Target Settingsstaticlib-iiwusynth:File Mappingsstaticlib-iiwusynth:Build Extrasstaticlib-iiwusynth:Debugger Runtimestaticlib-iiwusynth:Debugger Targetstaticlib-iiwusynth:68K CodeGenstaticlib-iiwusynth:68K Disassemblerstaticlib-iiwusynth:68K Global Optimizerstaticlib-iiwusynth:68K Linkerstaticlib-iiwusynth:68K Projectstaticlib-iiwusynth:C/C++ Compilerstaticlib-iiwusynth:C/C++ Warningsstaticlib-iiwusynth:CFM68Kstaticlib-iiwusynth:FTP Panelstaticlib-iiwusynth:Java Command Linestaticlib-iiwusynth:Java Languagestaticlib-iiwusynth:Java MacOS Settingsstaticlib-iiwusynth:Java Outputstaticlib-iiwusynth:Java Projectstaticlib-iiwusynth:JavaDoc Projectstaticlib-iiwusynth:MacOS Merge Panelstaticlib-iiwusynth:PPC CodeGenstaticlib-iiwusynth:PPC Disassemblerstaticlib-iiwusynth:PPC Global Optimizerstaticlib-iiwusynth:PPC Linkerstaticlib-iiwusynth:PPC PEFstaticlib-iiwusynth:PPC Projectstaticlib-iiwusynth:PPCAsm Panelstaticlib-iiwusynth:Rez Compilerdynamiclib-iiwusynth:Source Treesdynamiclib-iiwusynth:Custom Keywordsdynamiclib-iiwusynth:Access Pathsdynamiclib-iiwusynth:Target Settingsdynamiclib-iiwusynth:File Mappingsdynamiclib-iiwusynth:Build Extrasdynamiclib-iiwusynth:Debugger Runtimedynamiclib-iiwusynth:Debugger Targetdynamiclib-iiwusynth:68K CodeGendynamiclib-iiwusynth:68K Disassemblerdynamiclib-iiwusynth:68K Global Optimizerdynamiclib-iiwusynth:68K Linkerdynamiclib-iiwusynth:68K Projectdynamiclib-iiwusynth:C/C++ Compilerdynamiclib-iiwusynth:C/C++ Warningsdynamiclib-iiwusynth:CFM68Kdynamiclib-iiwusynth:FTP Paneldynamiclib-iiwusynth:Java Command Linedynamiclib-iiwusynth:Java Languagedynamiclib-iiwusynth:Java MacOS Settingsdynamiclib-iiwusynth:Java Outputdynamiclib-iiwusynth:Java Projectdynamiclib-iiwusynth:JavaDoc Projectdynamiclib-iiwusynth:MacOS Merge Paneldynamiclib-iiwusynth:PPC CodeGendynamiclib-iiwusynth:PPC Disassemblerdynamiclib-iiwusynth:PPC Global Optimizerdynamiclib-iiwusynth:PPC Linkerdynamiclib-iiwusynth:PPC PEFdynamiclib-iiwusynth:PPC Projectdynamiclib-iiwusynth:PPCAsm Paneldynamiclib-iiwusynth:Rez Compilerstaticlib:Source Treesstaticlib:Custom Keywordsstaticlib:Access Pathsstaticlib:Target Settingsstaticlib:File Mappingsstaticlib:Build Extrasstaticlib:Debugger Runtimestaticlib:Debugger Targetstaticlib:68K CodeGenstaticlib:68K Disassemblerstaticlib:68K Global Optimizerstaticlib:68K Linkerstaticlib:68K Projectstaticlib:C/C++ Compilerstaticlib:C/C++ Warningsstaticlib:CFM68Kstaticlib:FTP Panelstaticlib:Java Command Linestaticlib:Java Languagestaticlib:Java MacOS Settingsstaticlib:Java Outputstaticlib:Java Projectstaticlib:JavaDoc Projectstaticlib:MacOS Merge Panelstaticlib:PPC CodeGenstaticlib:PPC Disassemblerstaticlib:PPC Global Optimizerstaticlib:PPC Linkerstaticlib:PPC PEFstaticlib:PPC Projectstaticlib:PPCAsm Panelstaticlib:Rez Compilerdynamiclib:Source Treesdynamiclib:Custom Keywordsdynamiclib:Access Pathsdynamiclib:Target Settingsdynamiclib:File Mappingsdynamiclib:Build Extrasdynamiclib:Debugger Runtimedynamiclib:Debugger Targetdynamiclib:68K CodeGendynamiclib:68K Disassemblerdynamiclib:68K Global Optimizerdynamiclib:68K Linkerdynamiclib:68K Projectdynamiclib:C/C++ Compilerdynamiclib:C/C++ Warningsdynamiclib:CFM68Kdynamiclib:FTP Paneldynamiclib:Java Command Linedynamiclib:Java Languagedynamiclib:Java MacOS Settingsdynamiclib:Java Outputdynamiclib:Java Projectdynamiclib:JavaDoc Projectdynamiclib:MacOS Merge Paneldynamiclib:PPC CodeGendynamiclib:PPC Disassemblerdynamiclib:PPC Global Optimizerdynamiclib:PPC Linkerdynamiclib:PPC PEFdynamiclib:PPC Projectdynamiclib:PPCAsm Paneldynamiclib:Rez Compileriiwusynth:WinRC Compileriiwusynth:x86 CodeGeniiwusynth:x86 Exceptions Paneliiwusynth:x86 Global Optimizeriiwusynth:x86 Linkeriiwusynth:x86 Projectiiwuplay:WinRC Compileriiwuplay:x86 CodeGeniiwuplay:x86 Exceptions Paneliiwuplay:x86 Global Optimizeriiwuplay:x86 Linkeriiwuplay:x86 Projectstaticlib:WinRC Compilerstaticlib:x86 CodeGenstaticlib:x86 Exceptions Panelstaticlib:x86 Global Optimizerstaticlib:x86 Linkerstaticlib:x86 Projectdynamiclib:WinRC Compilerdynamiclib:x86 CodeGendynamiclib:x86 Exceptions Paneldynamiclib:x86 Global Optimizerdynamiclib:x86 Linkerdynamiclib:x86 Projectstaticlib DEBUG:Source Treesstaticlib DEBUG:Custom Keywordsstaticlib DEBUG:Access Pathsstaticlib DEBUG:Target Settingsstaticlib DEBUG:File Mappingsstaticlib DEBUG:Build Extrasstaticlib DEBUG:Debugger Runtimestaticlib DEBUG:Debugger Targetstaticlib DEBUG:68K CodeGenstaticlib DEBUG:68K Disassemblerstaticlib DEBUG:68K Global Optimizerstaticlib DEBUG:68K Linkerstaticlib DEBUG:68K Projectstaticlib DEBUG:C/C++ Compilerstaticlib DEBUG:C/C++ Warningsstaticlib DEBUG:CFM68Kstaticlib DEBUG:MacOS Merge Panelstaticlib DEBUG:PPC CodeGenstaticlib DEBUG:PPC Disassemblerstaticlib DEBUG:PPC Global Optimizerstaticlib DEBUG:PPC Linkerstaticlib DEBUG:PPC PEFstaticlib DEBUG:PPC Projectstaticlib DEBUG:PPCAsm Panelstaticlib DEBUG:Rez Compilerstaticlib DEBUG:WinRC Compilerstaticlib DEBUG:x86 CodeGenstaticlib DEBUG:x86 Exceptions Panelstaticlib DEBUG:x86 Global Optimizerstaticlib DEBUG:x86 Linkerstaticlib DEBUG:x86 Projectiiwusynth_outonly:Source Treesiiwusynth_outonly:Custom Keywordsiiwusynth_outonly:Access Pathsiiwusynth_outonly:Target Settingsiiwusynth_outonly:File Mappingsiiwusynth_outonly:Build Extrasiiwusynth_outonly:Debugger Runtimeiiwusynth_outonly:Debugger Targetiiwusynth_outonly:68K CodeGeniiwusynth_outonly:68K Disassembleriiwusynth_outonly:68K Global Optimizeriiwusynth_outonly:68K Linkeriiwusynth_outonly:68K Projectiiwusynth_outonly:C/C++ Compileriiwusynth_outonly:C/C++ Warningsiiwusynth_outonly:CFM68Kiiwusynth_outonly:MacOS Merge Paneliiwusynth_outonly:PPC CodeGeniiwusynth_outonly:PPC Disassembleriiwusynth_outonly:PPC Global Optimizeriiwusynth_outonly:PPC Linkeriiwusynth_outonly:PPC PEFiiwusynth_outonly:PPC Projectiiwusynth_outonly:PPCAsm Paneliiwusynth_outonly:Rez Compileriiwusynth_outonly:WinRC Compileriiwusynth_outonly:x86 CodeGeniiwusynth_outonly:x86 Exceptions Paneliiwusynth_outonly:x86 Global Optimizeriiwusynth_outonly:x86 Linkeriiwusynth_outonly:x86 Projectstaticlib no midi in:Source Treesstaticlib no midi in:Custom Keywordsstaticlib no midi in:Access Pathsstaticlib no midi in:Target Settingsstaticlib no midi in:File Mappingsstaticlib no midi in:Build Extrasstaticlib no midi in:Debugger Runtimestaticlib no midi in:Debugger Targetstaticlib no midi in:68K CodeGenstaticlib no midi in:68K Disassemblerstaticlib no midi in:68K Global Optimizerstaticlib no midi in:68K Linkerstaticlib no midi in:68K Projectstaticlib no midi in:C/C++ Compilerstaticlib no midi in:C/C++ Warningsstaticlib no midi in:CFM68Kstaticlib no midi in:MacOS Merge Panelstaticlib no midi in:PPC CodeGenstaticlib no midi in:PPC Disassemblerstaticlib no midi in:PPC Global Optimizerstaticlib no midi in:PPC Linkerstaticlib no midi in:PPC PEFstaticlib no midi in:PPC Projectstaticlib no midi in:PPCAsm Panelstaticlib no midi in:Rez Compilerstaticlib no midi in:WinRC Compilerstaticlib no midi in:x86 CodeGenstaticlib no midi in:x86 Exceptions Panelstaticlib no midi in:x86 Global Optimizerstaticlib no midi in:x86 Linkerstaticlib no midi in:x86 Projectstaticlib all:Source Treesstaticlib all:Custom Keywordsstaticlib all:Access Pathsstaticlib all:Target Settingsstaticlib all:File Mappingsstaticlib all:Build Extrasstaticlib all:Debugger Runtimestaticlib all:Debugger Targetstaticlib all:68K CodeGenstaticlib all:68K Disassemblerstaticlib all:68K Global Optimizerstaticlib all:68K Linkerstaticlib all:68K Projectstaticlib all:C/C++ Compilerstaticlib all:C/C++ Warningsstaticlib all:CFM68Kstaticlib all:MacOS Merge Panelstaticlib all:PPC CodeGenstaticlib all:PPC Disassemblerstaticlib all:PPC Global Optimizerstaticlib all:PPC Linkerstaticlib all:PPC PEFstaticlib all:PPC Projectstaticlib all:PPCAsm Panelstaticlib all:Rez Compilerstaticlib all:WinRC Compilerstaticlib all:x86 CodeGenstaticlib all:x86 Exceptions Panelstaticlib all:x86 Global Optimizerstaticlib all:x86 Linkerstaticlib all:x86 Projectstaticlib DEBUG no midi in:Source Treesstaticlib DEBUG no midi in:Custom Keywordsstaticlib DEBUG no midi in:Access Pathsstaticlib DEBUG no midi in:Target Settingsstaticlib DEBUG no midi in:File Mappingsstaticlib DEBUG no midi in:Build Extrasstaticlib DEBUG no midi in:Debugger Runtimestaticlib DEBUG no midi in:Debugger Targetstaticlib DEBUG no midi in:68K CodeGenstaticlib DEBUG no midi in:68K Disassemblerstaticlib DEBUG no midi in:68K Global Optimizerstaticlib DEBUG no midi in:68K Linkerstaticlib DEBUG no midi in:68K Projectstaticlib DEBUG no midi in:C/C++ Compilerstaticlib DEBUG no midi in:C/C++ Warningsstaticlib DEBUG no midi in:CFM68Kstaticlib DEBUG no midi in:MacOS Merge Panelstaticlib DEBUG no midi in:PPC CodeGenstaticlib DEBUG no midi in:PPC Disassemblerstaticlib DEBUG no midi in:PPC Global Optimizerstaticlib DEBUG no midi in:PPC Linkerstaticlib DEBUG no midi in:PPC PEFstaticlib DEBUG no midi in:PPC Projectstaticlib DEBUG no midi in:PPCAsm Panelstaticlib DEBUG no midi in:Rez Compilerstaticlib DEBUG no midi in:WinRC Compilerstaticlib DEBUG no midi in:x86 CodeGenstaticlib DEBUG no midi in:x86 Exceptions Panelstaticlib DEBUG no midi in:x86 Global Optimizerstaticlib DEBUG no midi in:x86 Linkerstaticlib DEBUG no midi in:x86 Projectstaticlib DEBUG all:Source Treesstaticlib DEBUG all:Custom Keywordsstaticlib DEBUG all:Access Pathsstaticlib DEBUG all:Target Settingsstaticlib DEBUG all:File Mappingsstaticlib DEBUG all:Build Extrasstaticlib DEBUG all:Debugger Runtimestaticlib DEBUG all:Debugger Targetstaticlib DEBUG all:68K CodeGenstaticlib DEBUG all:68K Disassemblerstaticlib DEBUG all:68K Global Optimizerstaticlib DEBUG all:68K Linkerstaticlib DEBUG all:68K Projectstaticlib DEBUG all:C/C++ Compilerstaticlib DEBUG all:C/C++ Warningsstaticlib DEBUG all:CFM68Kstaticlib DEBUG all:MacOS Merge Panelstaticlib DEBUG all:PPC CodeGenstaticlib DEBUG all:PPC Disassemblerstaticlib DEBUG all:PPC Global Optimizerstaticlib DEBUG all:PPC Linkerstaticlib DEBUG all:PPC PEFstaticlib DEBUG all:PPC Projectstaticlib DEBUG all:PPCAsm Panelstaticlib DEBUG all:Rez Compilerstaticlib DEBUG all:WinRC Compilerstaticlib DEBUG all:x86 CodeGenstaticlib DEBUG all:x86 Exceptions Panelstaticlib DEBUG all:x86 Global Optimizerstaticlib DEBUG all:x86 Linkerstaticlib DEBUG all:x86 Projectiiwusynth no midi in:Source Treesiiwusynth no midi in:Custom Keywordsiiwusynth no midi in:Access Pathsiiwusynth no midi in:Target Settingsiiwusynth no midi in:File Mappingsiiwusynth no midi in:Build Extrasiiwusynth no midi in:Debugger Runtimeiiwusynth no midi in:Debugger Targetiiwusynth no midi in:68K CodeGeniiwusynth no midi in:68K Disassembleriiwusynth no midi in:68K Global Optimizeriiwusynth no midi in:68K Linkeriiwusynth no midi in:68K Projectiiwusynth no midi in:C/C++ Compileriiwusynth no midi in:C/C++ Warningsiiwusynth no midi in:CFM68Kiiwusynth no midi in:MacOS Merge Paneliiwusynth no midi in:PPC CodeGeniiwusynth no midi in:PPC Disassembleriiwusynth no midi in:PPC Global Optimizeriiwusynth no midi in:PPC Linkeriiwusynth no midi in:PPC PEFiiwusynth no midi in:PPC Projectiiwusynth no midi in:PPCAsm Paneliiwusynth no midi in:Rez Compileriiwusynth no midi in:WinRC Compileriiwusynth no midi in:x86 CodeGeniiwusynth no midi in:x86 Exceptions Paneliiwusynth no midi in:x86 Global Optimizeriiwusynth no midi in:x86 Linkeriiwusynth no midi in:x86 Projectdynamiclib all:Source Treesdynamiclib all:Custom Keywordsdynamiclib all:Access Pathsdynamiclib all:Target Settingsdynamiclib all:File Mappingsdynamiclib all:Build Extrasdynamiclib all:Debugger Runtimedynamiclib all:Debugger Targetdynamiclib all:68K CodeGendynamiclib all:68K Disassemblerdynamiclib all:68K Global Optimizerdynamiclib all:68K Linkerdynamiclib all:68K Projectdynamiclib all:C/C++ Compilerdynamiclib all:C/C++ Warningsdynamiclib all:CFM68Kdynamiclib all:MacOS Merge Paneldynamiclib all:PPC CodeGendynamiclib all:PPC Disassemblerdynamiclib all:PPC Global Optimizerdynamiclib all:PPC Linkerdynamiclib all:PPC PEFdynamiclib all:PPC Projectdynamiclib all:PPCAsm Paneldynamiclib all:Rez Compilerdynamiclib all:WinRC Compilerdynamiclib all:x86 CodeGendynamiclib all:x86 Exceptions Paneldynamiclib all:x86 Global Optimizerdynamiclib all:x86 Linkerdynamiclib all:x86 Projectdynamiclib no midi out:Source Treesdynamiclib no midi out:Custom Keywordsdynamiclib no midi out:Access Pathsdynamiclib no midi out:Target Settingsdynamiclib no midi out:File Mappingsdynamiclib no midi out:Build Extrasdynamiclib no midi out:Debugger Runtimedynamiclib no midi out:Debugger Targetdynamiclib no midi out:68K CodeGendynamiclib no midi out:68K Disassemblerdynamiclib no midi out:68K Global Optimizerdynamiclib no midi out:68K Linkerdynamiclib no midi out:68K Projectdynamiclib no midi out:C/C++ Compilerdynamiclib no midi out:C/C++ Warningsdynamiclib no midi out:CFM68Kdynamiclib no midi out:MacOS Merge Paneldynamiclib no midi out:PPC CodeGendynamiclib no midi out:PPC Disassemblerdynamiclib no midi out:PPC Global Optimizerdynamiclib no midi out:PPC Linkerdynamiclib no midi out:PPC PEFdynamiclib no midi out:PPC Projectdynamiclib no midi out:PPCAsm Paneldynamiclib no midi out:Rez Compilerdynamiclib no midi out:WinRC Compilerdynamiclib no midi out:x86 CodeGendynamiclib no midi out:x86 Exceptions Paneldynamiclib no midi out:x86 Global Optimizerdynamiclib no midi out:x86 Linkerdynamiclib no midi out:x86 Projectdynamiclib no midi in:Source Treesdynamiclib no midi in:Custom Keywordsdynamiclib no midi in:Access Pathsdynamiclib no midi in:Target Settingsdynamiclib no midi in:File Mappingsdynamiclib no midi in:Build Extrasdynamiclib no midi in:Debugger Runtimedynamiclib no midi in:Debugger Targetdynamiclib no midi in:68K CodeGendynamiclib no midi in:68K Disassemblerdynamiclib no midi in:68K Global Optimizerdynamiclib no midi in:68K Linkerdynamiclib no midi in:68K Projectdynamiclib no midi in:C/C++ Compilerdynamiclib no midi in:C/C++ Warningsdynamiclib no midi in:CFM68Kdynamiclib no midi in:MacOS Merge Paneldynamiclib no midi in:PPC CodeGendynamiclib no midi in:PPC Disassemblerdynamiclib no midi in:PPC Global Optimizerdynamiclib no midi in:PPC Linkerdynamiclib no midi in:PPC PEFdynamiclib no midi in:PPC Projectdynamiclib no midi in:PPCAsm Paneldynamiclib no midi in:Rez Compilerdynamiclib no midi in:WinRC Compilerdynamiclib no midi in:x86 CodeGendynamiclib no midi in:x86 Exceptions Paneldynamiclib no midi in:x86 Global Optimizerdynamiclib no midi in:x86 Linkerdynamiclib no midi in:x86 Projectdynamiclib seq out:Source Treesdynamiclib seq out:Custom Keywordsdynamiclib seq out:Access Pathsdynamiclib seq out:Target Settingsdynamiclib seq out:File Mappingsdynamiclib seq out:Build Extrasdynamiclib seq out:Debugger Runtimedynamiclib seq out:Debugger Targetdynamiclib seq out:68K CodeGendynamiclib seq out:68K Disassemblerdynamiclib seq out:68K Global Optimizerdynamiclib seq out:68K Linkerdynamiclib seq out:68K Projectdynamiclib seq out:C/C++ Compilerdynamiclib seq out:C/C++ Warningsdynamiclib seq out:CFM68Kdynamiclib seq out:MacOS Merge Paneldynamiclib seq out:PPC CodeGendynamiclib seq out:PPC Disassemblerdynamiclib seq out:PPC Global Optimizerdynamiclib seq out:PPC Linkerdynamiclib seq out:PPC PEFdynamiclib seq out:PPC Projectdynamiclib seq out:PPCAsm Paneldynamiclib seq out:Rez Compilerdynamiclib seq out:WinRC Compilerdynamiclib seq out:x86 CodeGendynamiclib seq out:x86 Exceptions Paneldynamiclib seq out:x86 Global Optimizerdynamiclib seq out:x86 Linkerdynamiclib seq out:x86 Projectsequencer dll no midi in:Source Treessequencer dll no midi in:Custom Keywordssequencer dll no midi in:Access Pathssequencer dll no midi in:Target Settingssequencer dll no midi in:File Mappingssequencer dll no midi in:Build Extrassequencer dll no midi in:Debugger Runtimesequencer dll no midi in:Debugger Targetsequencer dll no midi in:68K CodeGensequencer dll no midi in:68K Disassemblersequencer dll no midi in:68K Global Optimizersequencer dll no midi in:68K Linkersequencer dll no midi in:68K Projectsequencer dll no midi in:C/C++ Compilersequencer dll no midi in:C/C++ Warningssequencer dll no midi in:CFM68Ksequencer dll no midi in:MacOS Merge Panelsequencer dll no midi in:PPC CodeGensequencer dll no midi in:PPC Disassemblersequencer dll no midi in:PPC Global Optimizersequencer dll no midi in:PPC Linkersequencer dll no midi in:PPC PEFsequencer dll no midi in:PPC Projectsequencer dll no midi in:PPCAsm Panelsequencer dll no midi in:Rez Compilersequencer dll no midi in:WinRC Compilersequencer dll no midi in:x86 CodeGensequencer dll no midi in:x86 Exceptions Panelsequencer dll no midi in:x86 Global Optimizersequencer dll no midi in:x86 Linkersequencer dll no midi in:x86 Project]mstrX1mstlmstn((msti7mstrmstlmstnP(msti),mpsiKLmtgl{ PLst.pref̹-(wIprefA.pref}/,$pref): prefO;,pref4<-vJprefz= mtsl,`mtpl pmtpsxmtpi 5Pmtlo".prefVK# IprefnL'iprefM'm$prefX' pref`GY'prefZ(-JprefT.[.prefi]Iprefjpref~DkbE$prefUv prefVwbipref%hxbJprefcypref% prefC1prefMb9 prefdozpref, pref prefNN/|prefN]8prefMfw prefMɔ""prefMvprefNS,prefM5jprefNKprefmallmaplpref9h pref:EEmotipref/prefKk prefpref[bpref);pref=pref)Bpref/prefy pref|q pref9pref.!pref[2L>prefNpref-prefT<4prefWppref\xprefcE\pref2prefwcPpref?hpref4bprefprefP prefs prefΚpref ,/pref[ prefH.prefK0 prefV04prefdprefHprefaOl\prefĿ2pref@PprefPpref 3P4preflK/pref=HL6pref6KM4\prefN 2prefyOPprefR]P:>prefQ84prefklpref6ltprefXmՄ\prefSn^2prefBoHPprefpprefkq2"pref/a{pref/|2pref/@} pref/~v=pref/]jpref/6 mtsl`mtplUpmtps mtpiTmtlo,pref;;Xpref;%]<J pref;UO= pref:C>=Rpref;? pref;G@pref;A pref:@B4pref;OVCr pref:+D| pref:Epref:Fpref;F@G>pref;cUH pref;IxLpref;2Jbpref;'R pref;|S pref;ܢT pref;>US4pref;3V~pref;RWz&pref;kX .pref;o^Y pref;Z+"pref:TMpref;apref;c pref;NGpref;jpref;@ mtsl`mtplYpmtpsmtpiQTmtlopref;D [pref;\ pref;1]Ŝpref;^yrpref;%_vpref;u`pref;xapref;y+bpref;c pref;^d pref;epref;ufpref;Zg2>pref;k?h!pref:wi$pref;jwbpref;6r38pref;2Es! pref;ltS pref; uNpref;vpref;Qw@pref;$xT.pref;Ly  pref;qzz"pref;x8pref;8zpref;a pref;ئpref;U(jpref;ifluidsynth-1.1.9/src/unused/winbuild/000077500000000000000000000000001322272076000176235ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/winbuild/Makefile.am000066400000000000000000000002311322272076000216530ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in SUBDIRS = fluidsynth fluidsynth_dll fluidsynth_lib EXTRA_DIST = fluidsynth.dsw fluidsynth.sln fluidsynth-1.1.9/src/unused/winbuild/fluidsynth.dsw000066400000000000000000000017301322272076000225340ustar00rootroot00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "fluidsynth"=fluidsynth\fluidsynth.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "fluidsynth_dll"=fluidsynth_dll\fluidsynth_dll.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "fluidsynth_lib"=fluidsynth_lib\fluidsynth_lib.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### fluidsynth-1.1.9/src/unused/winbuild/fluidsynth.sln000077500000000000000000000037331322272076000225430ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fluidsynth", "fluidsynth\fluidsynth.vcproj", "{8150EAA4-CF92-448B-972A-01A423B3A23D}" ProjectSection(ProjectDependencies) = postProject {A52C4164-8C82-4E38-A70B-6D0E836D6644} = {A52C4164-8C82-4E38-A70B-6D0E836D6644} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fluidsynth_dll", "fluidsynth_dll\fluidsynth_dll.vcproj", "{A52C4164-8C82-4E38-A70B-6D0E836D6644}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fluidsynth_lib", "fluidsynth_lib\fluidsynth_lib.vcproj", "{CD7D1A45-9970-4958-BD8F-7F42B083093C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8150EAA4-CF92-448B-972A-01A423B3A23D}.Debug|Win32.ActiveCfg = Debug|Win32 {8150EAA4-CF92-448B-972A-01A423B3A23D}.Debug|Win32.Build.0 = Debug|Win32 {8150EAA4-CF92-448B-972A-01A423B3A23D}.Release|Win32.ActiveCfg = Release|Win32 {8150EAA4-CF92-448B-972A-01A423B3A23D}.Release|Win32.Build.0 = Release|Win32 {A52C4164-8C82-4E38-A70B-6D0E836D6644}.Debug|Win32.ActiveCfg = Debug|Win32 {A52C4164-8C82-4E38-A70B-6D0E836D6644}.Debug|Win32.Build.0 = Debug|Win32 {A52C4164-8C82-4E38-A70B-6D0E836D6644}.Release|Win32.ActiveCfg = Release|Win32 {A52C4164-8C82-4E38-A70B-6D0E836D6644}.Release|Win32.Build.0 = Release|Win32 {CD7D1A45-9970-4958-BD8F-7F42B083093C}.Debug|Win32.ActiveCfg = Debug|Win32 {CD7D1A45-9970-4958-BD8F-7F42B083093C}.Debug|Win32.Build.0 = Debug|Win32 {CD7D1A45-9970-4958-BD8F-7F42B083093C}.Release|Win32.ActiveCfg = Release|Win32 {CD7D1A45-9970-4958-BD8F-7F42B083093C}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal fluidsynth-1.1.9/src/unused/winbuild/fluidsynth/000077500000000000000000000000001322272076000220145ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/winbuild/fluidsynth/Makefile.am000066400000000000000000000001511322272076000240450ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in EXTRA_DIST = fluidsynth.dsp fluidsynth.vcproj fluidsynth-1.1.9/src/unused/winbuild/fluidsynth/fluidsynth.dsp000066400000000000000000000073231322272076000247220ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="fluidsynth" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=fluidsynth - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "fluidsynth.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "fluidsynth.mak" CFG="fluidsynth - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "fluidsynth - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE "fluidsynth - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "fluidsynth - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir ".\Debug" # PROP BASE Intermediate_Dir ".\Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir ".\Debug" # PROP Intermediate_Dir ".\Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /MDd /I "..\..\include" /Zi /W3 /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /Fp".\Debug\fluidsynth.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD CPP /nologo /MDd /I "..\..\include" /Zi /W3 /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /Fp".\Debug\fluidsynth.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD BASE MTL /tlb ".\Debug/fluidsynth.tlb" /win32 # ADD MTL /tlb ".\Debug/fluidsynth.tlb" /win32 # ADD BASE RSC /l 1033 /d "_DEBUG" # ADD RSC /l 1033 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LINK32=link.exe # ADD BASE LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth_debug.exe" /incremental:no /debug /pdb:".\Debug/fluidsynth_debug.pdb" /pdbtype:sept /subsystem:console /machine:ix86 # ADD LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth_debug.exe" /incremental:no /debug /pdb:".\Debug/fluidsynth_debug.pdb" /pdbtype:sept /subsystem:console /machine:ix86 !ELSEIF "$(CFG)" == "fluidsynth - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir ".\Release" # PROP BASE Intermediate_Dir ".\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir ".\Release" # PROP Intermediate_Dir ".\Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD BASE MTL /tlb ".\Release/fluidsynth.tlb" /win32 # ADD MTL /tlb ".\Release/fluidsynth.tlb" /win32 # ADD BASE RSC /l 1033 /d "NDEBUG" # ADD RSC /l 1033 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LINK32=link.exe # ADD BASE LINK32 odbc32.lib odbccp32.lib dsound.lib /nologo /out:"../fluidsynth.exe" /incremental:no /pdb:".\Release/fluidsynth.pdb" /pdbtype:sept /subsystem:console /machine:ix86 # ADD LINK32 odbc32.lib odbccp32.lib dsound.lib /nologo /out:"../fluidsynth.exe" /incremental:no /pdb:".\Release/fluidsynth.pdb" /pdbtype:sept /subsystem:console /machine:ix86 !ENDIF # Begin Target # Name "fluidsynth - Win32 Debug" # Name "fluidsynth - Win32 Release" # End Target # End Project fluidsynth-1.1.9/src/unused/winbuild/fluidsynth/fluidsynth.vcproj000077500000000000000000000122271322272076000254410ustar00rootroot00000000000000 fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_dll/000077500000000000000000000000001322272076000226475ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_dll/Makefile.am000066400000000000000000000001611322272076000247010ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in EXTRA_DIST = fluidsynth_dll.dsp fluidsynth_dll.vcproj fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_dll/fluidsynth_dll.dsp000066400000000000000000000105561322272076000264120ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="fluidsynth_dll" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=fluidsynth_dll - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "fluidsynth_dll.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "fluidsynth_dll.mak" CFG="fluidsynth_dll - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "fluidsynth_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "fluidsynth_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "fluidsynth_dll - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir ".\Release" # PROP BASE Intermediate_Dir ".\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir ".\Release" # PROP Intermediate_Dir ".\Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "FLUIDSYNTH_DLL_EXPORTS" /D "FLUIDSYNTH_SEQ_DLL_EXPORTS" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "FLUIDSYNTH_DLL_EXPORTS" /D "FLUIDSYNTH_SEQ_DLL_EXPORTS" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /tlb ".\Release/fluidsynth_dll.tlb" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /tlb ".\Release/fluidsynth_dll.tlb" /win32 # ADD BASE RSC /l 1033 /d "NDEBUG" # ADD RSC /l 1033 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LINK32=link.exe # ADD BASE LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth.dll" /incremental:no /pdb:".\Release/fluidsynth.pdb" /pdbtype:sept /subsystem:windows /implib:".\Release/fluidsynth.lib" /machine:ix86 # ADD LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth.dll" /incremental:no /pdb:".\Release/fluidsynth.pdb" /pdbtype:sept /subsystem:windows /implib:".\Release/fluidsynth.lib" /machine:ix86 !ELSEIF "$(CFG)" == "fluidsynth_dll - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir ".\Debug" # PROP BASE Intermediate_Dir ".\Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir ".\Debug" # PROP Intermediate_Dir ".\Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /I "..\..\include" /Zi /W3 /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "FLUIDSYNTH_DLL_EXPORTS" /D "FLUIDSYNTH_SEQ_DLL_EXPORTS" /D "_MBCS" /YX /Fp".\Debug\fluidsynth_dll.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD CPP /nologo /MTd /I "..\..\include" /Zi /W3 /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "FLUIDSYNTH_DLL_EXPORTS" /D "FLUIDSYNTH_SEQ_DLL_EXPORTS" /D "_MBCS" /YX /Fp".\Debug\fluidsynth_dll.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /tlb ".\Debug/fluidsynth_dll.tlb" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /tlb ".\Debug/fluidsynth_dll.tlb" /win32 # ADD BASE RSC /l 1033 /d "_DEBUG" # ADD RSC /l 1033 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LINK32=link.exe # ADD BASE LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth_debug.dll" /incremental:no /debug /pdb:".\Debug/fluidsynth_debug.pdb" /pdbtype:sept /subsystem:windows /implib:".\Debug/fluidsynth_debug.lib" /machine:ix86 # ADD LINK32 odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /out:"../fluidsynth_debug.dll" /incremental:no /debug /pdb:".\Debug/fluidsynth_debug.pdb" /pdbtype:sept /subsystem:windows /implib:".\Debug/fluidsynth_debug.lib" /machine:ix86 !ENDIF # Begin Target # Name "fluidsynth_dll - Win32 Release" # Name "fluidsynth_dll - Win32 Debug" # End Target # End Project fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_dll/fluidsynth_dll.vcproj000077500000000000000000000636601322272076000271360ustar00rootroot00000000000000 fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_lib/000077500000000000000000000000001322272076000226425ustar00rootroot00000000000000fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_lib/Makefile.am000066400000000000000000000001611322272076000246740ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in EXTRA_DIST = fluidsynth_lib.dsp fluidsynth_lib.vcproj fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_lib/fluidsynth_lib.dsp000066400000000000000000000062721322272076000264000ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="fluidsynth_lib" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Static Library" 0x0104 CFG=fluidsynth_lib - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "fluidsynth_lib.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "fluidsynth_lib.mak" CFG="fluidsynth_lib - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "fluidsynth_lib - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "fluidsynth_lib - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "fluidsynth_lib - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir ".\Release" # PROP BASE Intermediate_Dir ".\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir ".\Release" # PROP Intermediate_Dir ".\Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "NDEBUG" /D "WIN32" /D "_LIB" /D "FLUIDSYNTH_NOT_A_DLL" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD CPP /nologo /MD /I "..\..\include" /W3 /O2 /Ob1 /D "NDEBUG" /D "WIN32" /D "_LIB" /D "FLUIDSYNTH_NOT_A_DLL" /D "_MBCS" /GF /Gy /YX /Fo".\Release\" /Fd".\Release\" /c # ADD BASE MTL /win32 # ADD MTL /win32 # ADD BASE RSC /l 1036 /d "NDEBUG" # ADD RSC /l 1036 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LIB32=link.exe -lib # ADD BASE LIB32 /nologo /out:"..\fluidsynth_lib.lib" # ADD LIB32 /nologo /out:"..\fluidsynth_lib.lib" !ELSEIF "$(CFG)" == "fluidsynth_lib - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir ".\Debug" # PROP BASE Intermediate_Dir ".\Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir ".\Debug" # PROP Intermediate_Dir ".\Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /MDd /I "..\..\include" /Zi /W3 /Od /D "_DEBUG" /D "WIN32" /D "_LIB" /D "FLUIDSYNTH_NOT_A_DLL" /D "_MBCS" /YX /Fp".\Debug\fluidsynth_lib.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD CPP /nologo /MDd /I "..\..\include" /Zi /W3 /Od /D "_DEBUG" /D "WIN32" /D "_LIB" /D "FLUIDSYNTH_NOT_A_DLL" /D "_MBCS" /YX /Fp".\Debug\fluidsynth_lib.pch" /Fo".\Debug\" /Fd".\Debug\" /GZ /c # ADD BASE MTL /win32 # ADD MTL /win32 # ADD BASE RSC /l 1036 /d "_DEBUG" # ADD RSC /l 1036 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 # ADD BSC32 LIB32=link.exe -lib # ADD BASE LIB32 /nologo /out:"..\fluidsynth_lib_debug.lib" # ADD LIB32 /nologo /out:"..\fluidsynth_lib_debug.lib" !ENDIF # Begin Target # Name "fluidsynth_lib - Win32 Release" # Name "fluidsynth_lib - Win32 Debug" # End Target # End Project fluidsynth-1.1.9/src/unused/winbuild/fluidsynth_lib/fluidsynth_lib.vcproj000077500000000000000000000472061322272076000271220ustar00rootroot00000000000000 fluidsynth-1.1.9/src/utils/000077500000000000000000000000001322272076000156435ustar00rootroot00000000000000fluidsynth-1.1.9/src/utils/fluid_conv.c000066400000000000000000000203061322272076000201400ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_conv.h" /* conversion tables */ fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE]; fluid_real_t fluid_concave_tab[128]; fluid_real_t fluid_convex_tab[128]; 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); } /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont * specification in regards to volume attenuation. The below calculation * is an approx. equation for generating a table equivelant to the * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told * was generated from device testing. By the spec this should be centibels. */ for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) { fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR); } /* 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[127] = 1.0; /* convex unipolar positive transform curve */ fluid_convex_tab[0] = 0; fluid_convex_tab[127] = 1.0; x = log10(128.0 / 127.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 < 127; i++) { x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0); fluid_convex_tab[i] = (fluid_real_t) (1.0 - x); fluid_concave_tab[127 - i] = (fluid_real_t) x; } /* initialize the pan conversion table */ x = 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); } } /* * fluid_ct2hz */ fluid_real_t fluid_ct2hz_real(fluid_real_t cents) { if (cents < 0) return (fluid_real_t) 1.0; else if (cents < 900) { return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)]; } else if (cents < 2100) { return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)]; } else if (cents < 3300) { return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)]; } else if (cents < 4500) { return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)]; } else if (cents < 5700) { return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)]; } else if (cents < 6900) { return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)]; } else if (cents < 8100) { return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)]; } else if (cents < 9300) { return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)]; } else if (cents < 10500) { return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)]; } else if (cents < 11700) { return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)]; } else if (cents < 12900) { return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)]; } else if (cents < 14100) { return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)]; } else { return (fluid_real_t) 1.0; /* some loony trying to make you deaf */ } } /* * fluid_ct2hz */ fluid_real_t fluid_ct2hz(fluid_real_t cents) { /* Filter fc limit: SF2.01 page 48 # 8 */ if (cents >= 13500){ cents = 13500; /* 20 kHz */ } else if (cents < 1500){ cents = 1500; /* 20 Hz */ } return fluid_ct2hz_real(cents); } /* * fluid_cb2amp * * in: a value between 0 and 960, 0 is no attenuation * out: a value between 1 and 0 */ fluid_real_t fluid_cb2amp(fluid_real_t cb) { /* * cb: an attenuation in 'centibels' (1/10 dB) * SF2.01 page 49 # 48 limits it to 144 dB. * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit. */ /* minimum attenuation: 0 dB */ if (cb < 0) { return 1.0; } if (cb >= FLUID_CB_AMP_SIZE) { return 0.0; } return fluid_cb2amp_tab[(int) cb]; } /* * fluid_atten2amp * * in: a value between 0 and 1440, 0 is no attenuation * out: a value between 1 and 0 * * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp. */ fluid_real_t fluid_atten2amp(fluid_real_t atten) { if (atten < 0) return 1.0; else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0; else return fluid_atten2amp_tab[(int) atten]; } /* * fluid_tc2sec_delay */ fluid_real_t fluid_tc2sec_delay(fluid_real_t tc) { /* SF2.01 section 8.1.2 items 21, 23, 25, 33 * SF2.01 section 8.1.3 items 21, 23, 25, 33 * * The most negative number indicates a delay of 0. Range is limited * from -12000 to 5000 */ if (tc <= -32768.0f) { return (fluid_real_t) 0.0f; }; if (tc < -12000.) { tc = (fluid_real_t) -12000.0f; } if (tc > 5000.0f) { tc = (fluid_real_t) 5000.0f; } return (fluid_real_t) pow(2.0, (double) tc / 1200.0); } /* * fluid_tc2sec_attack */ fluid_real_t fluid_tc2sec_attack(fluid_real_t tc) { /* SF2.01 section 8.1.2 items 26, 34 * SF2.01 section 8.1.3 items 26, 34 * The most negative number indicates a delay of 0 * Range is limited from -12000 to 8000 */ if (tc<=-32768.){return (fluid_real_t) 0.0;}; if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; if (tc>8000.){tc=(fluid_real_t) 8000.0;}; return (fluid_real_t) pow(2.0, (double) tc / 1200.0); } /* * fluid_tc2sec */ fluid_real_t fluid_tc2sec(fluid_real_t tc) { /* No range checking here! */ return (fluid_real_t) pow(2.0, (double) tc / 1200.0); } /* * fluid_tc2sec_release */ fluid_real_t fluid_tc2sec_release(fluid_real_t tc) { /* SF2.01 section 8.1.2 items 30, 38 * SF2.01 section 8.1.3 items 30, 38 * No 'most negative number' rule here! * Range is limited from -12000 to 8000 */ if (tc<=-32768.){return (fluid_real_t) 0.0;}; if (tc<-12000.){tc=(fluid_real_t) -12000.0;}; if (tc>8000.){tc=(fluid_real_t) 8000.0;}; return (fluid_real_t) pow(2.0, (double) tc / 1200.0); } /* * fluid_act2hz * * Convert from absolute cents to Hertz */ fluid_real_t 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) / log(2.0)); } /* * fluid_pan */ fluid_real_t fluid_pan(fluid_real_t c, int left) { if (left) { c = -c; } if (c < -500) { return (fluid_real_t) 0.0; } else if (c > 500) { return (fluid_real_t) 1.0; } else { return fluid_pan_tab[(int) (c + 500)]; } } /* * fluid_concave */ fluid_real_t fluid_concave(fluid_real_t val) { if (val < 0) { return 0; } else if (val > 127) { return 1; } return fluid_concave_tab[(int) val]; } /* * fluid_convex */ fluid_real_t fluid_convex(fluid_real_t val) { if (val < 0) { return 0; } else if (val > 127) { return 1; } return fluid_convex_tab[(int) val]; } fluidsynth-1.1.9/src/utils/fluid_conv.h000066400000000000000000000046661322272076000201600ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_CONV_H #define _FLUID_CONV_H #include "fluidsynth_priv.h" #define FLUID_CENTS_HZ_SIZE 1200 #define FLUID_VEL_CB_SIZE 128 #define FLUID_CB_AMP_SIZE 961 #define FLUID_ATTEN_AMP_SIZE 1441 #define FLUID_PAN_SIZE 1002 /* EMU 8k/10k don't follow spec in regards to volume attenuation. * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR). * By the standard this should be -200.0. */ /* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/ #define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/ void fluid_conversion_config(void); fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); fluid_real_t fluid_cb2amp(fluid_real_t cb); fluid_real_t fluid_atten2amp(fluid_real_t atten); fluid_real_t fluid_tc2sec(fluid_real_t tc); 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_concave(fluid_real_t val); fluid_real_t fluid_convex(fluid_real_t val); extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE]; extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; extern fluid_real_t fluid_concave_tab[128]; extern fluid_real_t fluid_convex_tab[128]; extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; #endif /* _FLUID_CONV_H */ fluidsynth-1.1.9/src/utils/fluid_hash.c000066400000000000000000001117031322272076000201200ustar00rootroot00000000000000/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02110-1301, USA. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. * * Adapted for FluidSynth use by Josh Green * September 8, 2009 from glib 2.18.4 */ /* * MT safe */ #include "fluidsynth_priv.h" #include "fluid_hash.h" #include "fluid_list.h" #define HASH_TABLE_MIN_SIZE 11 #define HASH_TABLE_MAX_SIZE 13845163 typedef struct { fluid_hashtable_t *hashtable; fluid_hashnode_t *prev_node; fluid_hashnode_t *node; int position; int pre_advanced; // Boolean int version; } RealIter; /* Excerpt from glib gprimes.c */ static const guint primes[] = { 11, 19, 37, 73, 109, 163, 251, 367, 557, 823, 1237, 1861, 2777, 4177, 6247, 9371, 14057, 21089, 31627, 47431, 71143, 106721, 160073, 240101, 360163, 540217, 810343, 1215497, 1823231, 2734867, 4102283, 6153409, 9230113, 13845163, }; static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]); unsigned int spaced_primes_closest (unsigned int num) { unsigned int i; for (i = 0; i < nprimes; i++) if (primes[i] > num) return primes[i]; return primes[nprimes - 1]; } /* End excerpt from glib gprimes.c */ /* * @hashtable: our #fluid_hashtable_t * @key: the key to lookup against * @hash_return: optional key hash return location * Return value: a pointer to the described #fluid_hashnode_t pointer * * Performs a lookup in the hash table. Virtually all hash operations * will use this function internally. * * This function first computes the hash value of the key using the * user's hash function. * * If an entry in the table matching @key is found then this function * returns a pointer to the pointer to that entry in the table. In * the case that the entry is at the head of a chain, this pointer * will be an item in the nodes[] array. In the case that the entry * is not at the head of a chain, this pointer will be the ->next * pointer on the node that preceeds it. * * In the case that no matching entry exists in the table, a pointer * to a %NULL pointer will be returned. To insert a item, this %NULL * pointer should be updated to point to the new #fluid_hashnode_t. * * If @hash_return is a pass-by-reference parameter. If it is * non-%NULL then the computed hash value is returned. This is to * save insertions from having to compute the hash record again for * the new record. */ static inline fluid_hashnode_t ** fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key, unsigned int *hash_return) { fluid_hashnode_t **node_ptr, *node; unsigned int hash_value; hash_value = (* hashtable->hash_func)(key); node_ptr = &hashtable->nodes[hash_value % hashtable->size]; if (hash_return) *hash_return = hash_value; /* Hash table lookup needs to be fast. * We therefore remove the extra conditional of testing * whether to call the key_equal_func or not from * the inner loop. * * Additional optimisation: first check if our full hash * values are equal so we can avoid calling the full-blown * key equality function in most cases. */ if (hashtable->key_equal_func) { while ((node = *node_ptr)) { if (node->key_hash == hash_value && hashtable->key_equal_func (node->key, key)) break; node_ptr = &(*node_ptr)->next; } } else { while ((node = *node_ptr)) { if (node->key == key) break; node_ptr = &(*node_ptr)->next; } } return node_ptr; } /* * @hashtable: our #fluid_hashtable_t * @node_ptr_ptr: a pointer to the return value from * fluid_hashtable_lookup_node() * @notify: %TRUE if the destroy notify handlers are to be called * * Removes a node from the hash table and updates the node count. The * node is freed. No table resize is performed. * * If @notify is %TRUE then the destroy notify functions are called * for the key and value of the hash node. * * @node_ptr_ptr is a pass-by-reference in/out parameter. When the * function is called, it should point to the pointer to the node to * remove. This level of indirection is required so that the pointer * may be updated appropriately once the node has been removed. * * Before the function returns, the pointer at @node_ptr_ptr will be * updated to point to the position in the table that contains the * pointer to the "next" node in the chain. This makes this function * convenient to use from functions that iterate over the entire * table. If there is no further item in the chain then the * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL). * * Since the pointer in the table to the removed node is replaced with * either a pointer to the next node or a %NULL pointer as * appropriate, the pointer at the end of @node_ptr_ptr will never be * modified at all. Stay tuned. :) */ static void fluid_hashtable_remove_node (fluid_hashtable_t *hashtable, fluid_hashnode_t ***node_ptr_ptr, int notify) { fluid_hashnode_t **node_ptr, *node; node_ptr = *node_ptr_ptr; node = *node_ptr; *node_ptr = node->next; if (notify && hashtable->key_destroy_func) hashtable->key_destroy_func (node->key); if (notify && hashtable->value_destroy_func) hashtable->value_destroy_func (node->value); FLUID_FREE (node); hashtable->nnodes--; } /* * fluid_hashtable_remove_all_nodes: * @hashtable: our #fluid_hashtable_t * @notify: %TRUE if the destroy notify handlers are to be called * * Removes all nodes from the table. Since this may be a precursor to * freeing the table entirely, no resize is performed. * * If @notify is %TRUE then the destroy notify functions are called * for the key and value of the hash node. */ static void fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify) { fluid_hashnode_t **node_ptr; int i; for (i = 0; i < hashtable->size; i++) for (node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) fluid_hashtable_remove_node (hashtable, &node_ptr, notify); hashtable->nnodes = 0; } /* * fluid_hashtable_resize: * @hashtable: our #fluid_hashtable_t * * Resizes the hash table to the optimal size based on the number of * nodes currently held. If you call this function then a resize will * occur, even if one does not need to occur. Use * fluid_hashtable_maybe_resize() instead. */ static void fluid_hashtable_resize (fluid_hashtable_t *hashtable) { fluid_hashnode_t **new_nodes; fluid_hashnode_t *node; fluid_hashnode_t *next; unsigned int hash_val; int new_size; int i; new_size = spaced_primes_closest (hashtable->nnodes); new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); new_nodes = FLUID_ARRAY (fluid_hashnode_t *, new_size); if (!new_nodes) { FLUID_LOG (FLUID_ERR, "Out of memory"); return; } FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *)); for (i = 0; i < hashtable->size; i++) for (node = hashtable->nodes[i]; node; node = next) { next = node->next; hash_val = node->key_hash % new_size; node->next = new_nodes[hash_val]; new_nodes[hash_val] = node; } FLUID_FREE (hashtable->nodes); hashtable->nodes = new_nodes; hashtable->size = new_size; } /* * fluid_hashtable_maybe_resize: * @hashtable: our #fluid_hashtable_t * * Resizes the hash table, if needed. * * Essentially, calls fluid_hashtable_resize() if the table has strayed * too far from its ideal size for its number of nodes. */ static inline void fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable) { int nnodes = hashtable->nnodes; int size = hashtable->size; if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) fluid_hashtable_resize (hashtable); } /** * new_fluid_hashtable: * @hash_func: a function to create a hash value from a key. * Hash values are used to determine where keys are stored within the * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and * fluid_str_hash() functions are provided for some common types of keys. * If hash_func is %NULL, fluid_direct_hash() is used. * @key_equal_func: a function to check two keys for equality. This is * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(), * fluid_int_equal() and fluid_str_equal() functions are provided for the most * common types of keys. If @key_equal_func is %NULL, keys are compared * directly in a similar fashion to fluid_direct_equal(), but without the * overhead of a function call. * * Creates a new #fluid_hashtable_t with a reference count of 1. * * Return value: a new #fluid_hashtable_t. **/ fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) { return new_fluid_hashtable_full (hash_func, key_equal_func, NULL, NULL); } /** * new_fluid_hashtable_full: * @hash_func: a function to create a hash value from a key. * @key_equal_func: a function to check two keys for equality. * @key_destroy_func: a function to free the memory allocated for the key * used when removing the entry from the #fluid_hashtable_t or %NULL if you * don't want to supply such a function. * @value_destroy_func: a function to free the memory allocated for the * value used when removing the entry from the #fluid_hashtable_t or %NULL if * you don't want to supply such a function. * * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count * of 1 and allows to specify functions to free the memory allocated for the * key and value that get called when removing the entry from the #fluid_hashtable_t. * * Return value: a new #fluid_hashtable_t. **/ fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func, fluid_destroy_notify_t key_destroy_func, fluid_destroy_notify_t value_destroy_func) { fluid_hashtable_t *hashtable; hashtable = FLUID_NEW (fluid_hashtable_t); if (!hashtable) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } hashtable->size = HASH_TABLE_MIN_SIZE; hashtable->nnodes = 0; hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; hashtable->key_equal_func = key_equal_func; hashtable->ref_count = 1; hashtable->key_destroy_func = key_destroy_func; hashtable->value_destroy_func = value_destroy_func; hashtable->nodes = FLUID_ARRAY (fluid_hashnode_t*, hashtable->size); FLUID_MEMSET (hashtable->nodes, 0, hashtable->size * sizeof (fluid_hashnode_t *)); return hashtable; } /** * fluid_hashtable_iter_init: * @iter: an uninitialized #fluid_hashtable_iter_t. * @hashtable: a #fluid_hashtable_t. * * Initializes a key/value pair iterator and associates it with * @hashtable. Modifying the hash table after calling this function * invalidates the returned iterator. * |[ * fluid_hashtable_iter_t iter; * gpointer key, value; * * fluid_hashtable_iter_init (&iter, hashtable); * while (fluid_hashtable_iter_next (&iter, &key, &value)) * { * /* do something with key and value */ * } * ]| * * Since: 2.16 **/ void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable) { RealIter *ri = (RealIter *) iter; fluid_return_if_fail (iter != NULL); fluid_return_if_fail (hashtable != NULL); ri->hashtable = hashtable; ri->prev_node = NULL; ri->node = NULL; ri->position = -1; ri->pre_advanced = FALSE; } /** * fluid_hashtable_iter_next: * @iter: an initialized #fluid_hashtable_iter_t. * @key: a location to store the key, or %NULL. * @value: a location to store the value, or %NULL. * * Advances @iter and retrieves the key and/or value that are now * pointed to as a result of this advancement. If %FALSE is returned, * @key and @value are not set, and the iterator becomes invalid. * * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached. * * Since: 2.16 **/ int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value) { RealIter *ri = (RealIter *) iter; fluid_return_val_if_fail (iter != NULL, FALSE); if (ri->pre_advanced) { ri->pre_advanced = FALSE; if (ri->node == NULL) return FALSE; } else { if (ri->node != NULL) { ri->prev_node = ri->node; ri->node = ri->node->next; } while (ri->node == NULL) { ri->position++; if (ri->position >= ri->hashtable->size) return FALSE; ri->prev_node = NULL; ri->node = ri->hashtable->nodes[ri->position]; } } if (key != NULL) *key = ri->node->key; if (value != NULL) *value = ri->node->value; return TRUE; } /** * fluid_hashtable_iter_get_hash_table: * @iter: an initialized #fluid_hashtable_iter_t. * * Returns the #fluid_hashtable_t associated with @iter. * * Return value: the #fluid_hashtable_t associated with @iter. * * Since: 2.16 **/ fluid_hashtable_t * fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter) { fluid_return_val_if_fail (iter != NULL, NULL); return ((RealIter *) iter)->hashtable; } static void iter_remove_or_steal (RealIter *ri, int notify) { fluid_hashnode_t *prev; fluid_hashnode_t *node; int position; fluid_return_if_fail (ri != NULL); fluid_return_if_fail (ri->node != NULL); prev = ri->prev_node; node = ri->node; position = ri->position; /* pre-advance the iterator since we will remove the node */ ri->node = ri->node->next; /* ri->prev_node is still the correct previous node */ while (ri->node == NULL) { ri->position++; if (ri->position >= ri->hashtable->size) break; ri->prev_node = NULL; ri->node = ri->hashtable->nodes[ri->position]; } ri->pre_advanced = TRUE; /* remove the node */ if (prev != NULL) prev->next = node->next; else ri->hashtable->nodes[position] = node->next; if (notify) { if (ri->hashtable->key_destroy_func) ri->hashtable->key_destroy_func(node->key); if (ri->hashtable->value_destroy_func) ri->hashtable->value_destroy_func(node->value); } FLUID_FREE (node); ri->hashtable->nnodes--; } /** * fluid_hashtable_iter_remove(): * @iter: an initialized #fluid_hashtable_iter_t. * * Removes the key/value pair currently pointed to by the iterator * from its associated #fluid_hashtable_t. Can only be called after * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more * than once for the same key/value pair. * * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the * key and value are freed using the supplied destroy functions, otherwise * you have to make sure that any dynamically allocated values are freed * yourself. * * Since: 2.16 **/ void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter) { iter_remove_or_steal ((RealIter *) iter, TRUE); } /** * fluid_hashtable_iter_steal(): * @iter: an initialized #fluid_hashtable_iter_t. * * Removes the key/value pair currently pointed to by the iterator * from its associated #fluid_hashtable_t, without calling the key and value * destroy functions. Can only be called after * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more * than once for the same key/value pair. * * Since: 2.16 **/ void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter) { iter_remove_or_steal ((RealIter *) iter, FALSE); } /** * fluid_hashtable_ref: * @hashtable: a valid #fluid_hashtable_t. * * Atomically increments the reference count of @hashtable by one. * This function is MT-safe and may be called from any thread. * * Return value: the passed in #fluid_hashtable_t. * * Since: 2.10 **/ fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable) { fluid_return_val_if_fail (hashtable != NULL, NULL); fluid_return_val_if_fail (hashtable->ref_count > 0, hashtable); fluid_atomic_int_add (&hashtable->ref_count, 1); return hashtable; } /** * fluid_hashtable_unref: * @hashtable: a valid #fluid_hashtable_t. * * Atomically decrements the reference count of @hashtable by one. * If the reference count drops to 0, all keys and values will be * destroyed, and all memory allocated by the hash table is released. * This function is MT-safe and may be called from any thread. * * Since: 2.10 **/ void fluid_hashtable_unref (fluid_hashtable_t *hashtable) { fluid_return_if_fail (hashtable != NULL); fluid_return_if_fail (hashtable->ref_count > 0); if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 0) { fluid_hashtable_remove_all_nodes (hashtable, TRUE); FLUID_FREE (hashtable->nodes); FLUID_FREE (hashtable); } } /** * delete_fluid_hashtable: * @hashtable: a #fluid_hashtable_t. * * Destroys all keys and values in the #fluid_hashtable_t and decrements its * reference count by 1. If keys and/or values are dynamically allocated, * you should either free them first or create the #fluid_hashtable_t with destroy * notifiers using fluid_hashtable_new_full(). In the latter case the destroy * functions you supplied will be called on all keys and values during the * destruction phase. **/ void delete_fluid_hashtable (fluid_hashtable_t *hashtable) { fluid_return_if_fail (hashtable != NULL); fluid_return_if_fail (hashtable->ref_count > 0); fluid_hashtable_remove_all (hashtable); fluid_hashtable_unref (hashtable); } /** * fluid_hashtable_lookup: * @hashtable: a #fluid_hashtable_t. * @key: the key to look up. * * Looks up a key in a #fluid_hashtable_t. Note that this function cannot * distinguish between a key that is not present and one which is present * and has the value %NULL. If you need this distinction, use * fluid_hashtable_lookup_extended(). * * Return value: the associated value, or %NULL if the key is not found. **/ void * fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key) { fluid_hashnode_t *node; fluid_return_val_if_fail (hashtable != NULL, NULL); node = *fluid_hashtable_lookup_node (hashtable, key, NULL); return node ? node->value : NULL; } /** * fluid_hashtable_lookup_extended: * @hashtable: a #fluid_hashtable_t. * @lookup_key: the key to look up. * @orig_key: returns the original key. * @value: returns the value associated with the key. * * Looks up a key in the #fluid_hashtable_t, returning the original key and the * associated value and a #gboolean which is %TRUE if the key was found. This * is useful if you need to free the memory allocated for the original key, * for example before calling fluid_hashtable_remove(). * * Return value: %TRUE if the key was found in the #fluid_hashtable_t. **/ int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key, void **orig_key, void **value) { fluid_hashnode_t *node; fluid_return_val_if_fail (hashtable != NULL, FALSE); node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL); if (node == NULL) return FALSE; if (orig_key) *orig_key = node->key; if (value) *value = node->value; return TRUE; } /* * fluid_hashtable_insert_internal: * @hashtable: our #fluid_hashtable_t * @key: the key to insert * @value: the value to insert * @keep_new_key: if %TRUE and this key already exists in the table * then call the destroy notify function on the old key. If %FALSE * then call the destroy notify function on the new key. * * Implements the common logic for the fluid_hashtable_insert() and * fluid_hashtable_replace() functions. * * Do a lookup of @key. If it is found, replace it with the new * @value (and perhaps the new @key). If it is not found, create a * new node. */ static void fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key, void *value, int keep_new_key) { fluid_hashnode_t **node_ptr, *node; unsigned int key_hash; fluid_return_if_fail (hashtable != NULL); fluid_return_if_fail (hashtable->ref_count > 0); node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash); if ((node = *node_ptr)) { if (keep_new_key) { if (hashtable->key_destroy_func) hashtable->key_destroy_func (node->key); node->key = key; } else { if (hashtable->key_destroy_func) hashtable->key_destroy_func (key); } if (hashtable->value_destroy_func) hashtable->value_destroy_func (node->value); node->value = value; } else { node = FLUID_NEW (fluid_hashnode_t); if (!node) { FLUID_LOG (FLUID_ERR, "Out of memory"); return; } node->key = key; node->value = value; node->key_hash = key_hash; node->next = NULL; *node_ptr = node; hashtable->nnodes++; fluid_hashtable_maybe_resize (hashtable); } } /** * fluid_hashtable_insert: * @hashtable: a #fluid_hashtable_t. * @key: a key to insert. * @value: the value to associate with the key. * * Inserts a new key and value into a #fluid_hashtable_t. * * If the key already exists in the #fluid_hashtable_t its current value is replaced * with the new value. If you supplied a @value_destroy_func when creating the * #fluid_hashtable_t, the old value is freed using that function. If you supplied * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed * using that function. **/ void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value) { fluid_hashtable_insert_internal (hashtable, key, value, FALSE); } /** * fluid_hashtable_replace: * @hashtable: a #fluid_hashtable_t. * @key: a key to insert. * @value: the value to associate with the key. * * Inserts a new key and value into a #fluid_hashtable_t similar to * fluid_hashtable_insert(). The difference is that if the key already exists * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed * using that function. If you supplied a @key_destroy_func when creating the * #fluid_hashtable_t, the old key is freed using that function. **/ void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value) { fluid_hashtable_insert_internal (hashtable, key, value, TRUE); } /* * fluid_hashtable_remove_internal: * @hashtable: our #fluid_hashtable_t * @key: the key to remove * @notify: %TRUE if the destroy notify handlers are to be called * Return value: %TRUE if a node was found and removed, else %FALSE * * Implements the common logic for the fluid_hashtable_remove() and * fluid_hashtable_steal() functions. * * Do a lookup of @key and remove it if it is found, calling the * destroy notify handlers only if @notify is %TRUE. */ static int fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key, int notify) { fluid_hashnode_t **node_ptr; fluid_return_val_if_fail (hashtable != NULL, FALSE); node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL); if (*node_ptr == NULL) return FALSE; fluid_hashtable_remove_node (hashtable, &node_ptr, notify); fluid_hashtable_maybe_resize (hashtable); return TRUE; } /** * fluid_hashtable_remove: * @hashtable: a #fluid_hashtable_t. * @key: the key to remove. * * Removes a key and its associated value from a #fluid_hashtable_t. * * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the * key and value are freed using the supplied destroy functions, otherwise * you have to make sure that any dynamically allocated values are freed * yourself. * * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. **/ int fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key) { return fluid_hashtable_remove_internal (hashtable, key, TRUE); } /** * fluid_hashtable_steal: * @hashtable: a #fluid_hashtable_t. * @key: the key to remove. * * Removes a key and its associated value from a #fluid_hashtable_t without * calling the key and value destroy functions. * * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. **/ int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key) { return fluid_hashtable_remove_internal (hashtable, key, FALSE); } /** * fluid_hashtable_remove_all: * @hashtable: a #fluid_hashtable_t * * Removes all keys and their associated values from a #fluid_hashtable_t. * * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys * and values are freed using the supplied destroy functions, otherwise you * have to make sure that any dynamically allocated values are freed * yourself. * * Since: 2.12 **/ void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable) { fluid_return_if_fail (hashtable != NULL); fluid_hashtable_remove_all_nodes (hashtable, TRUE); fluid_hashtable_maybe_resize (hashtable); } /** * fluid_hashtable_steal_all: * @hashtable: a #fluid_hashtable_t. * * Removes all keys and their associated values from a #fluid_hashtable_t * without calling the key and value destroy functions. * * Since: 2.12 **/ void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable) { fluid_return_if_fail (hashtable != NULL); fluid_hashtable_remove_all_nodes (hashtable, FALSE); fluid_hashtable_maybe_resize (hashtable); } /* * fluid_hashtable_foreach_remove_or_steal: * @hashtable: our #fluid_hashtable_t * @func: the user's callback function * @user_data: data for @func * @notify: %TRUE if the destroy notify handlers are to be called * * Implements the common logic for fluid_hashtable_foreach_remove() and * fluid_hashtable_foreach_steal(). * * Iterates over every node in the table, calling @func with the key * and value of the node (and @user_data). If @func returns %TRUE the * node is removed from the table. * * If @notify is true then the destroy notify handlers will be called * for each removed node. */ static unsigned int fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data, int notify) { fluid_hashnode_t *node, **node_ptr; unsigned int deleted = 0; int i; for (i = 0; i < hashtable->size; i++) for (node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) if ((* func) (node->key, node->value, user_data)) { fluid_hashtable_remove_node (hashtable, &node_ptr, notify); deleted++; } else node_ptr = &node->next; fluid_hashtable_maybe_resize (hashtable); return deleted; } /** * fluid_hashtable_foreach_remove: * @hashtable: a #fluid_hashtable_t. * @func: the function to call for each key/value pair. * @user_data: user data to pass to the function. * * Calls the given function for each key/value pair in the #fluid_hashtable_t. * If the function returns %TRUE, then the key/value pair is removed from the * #fluid_hashtable_t. If you supplied key or value destroy functions when creating * the #fluid_hashtable_t, they are used to free the memory allocated for the removed * keys and values. * * See #fluid_hashtable_iter_t for an alternative way to loop over the * key/value pairs in the hash table. * * Return value: the number of key/value pairs removed. **/ unsigned int fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data) { fluid_return_val_if_fail (hashtable != NULL, 0); fluid_return_val_if_fail (func != NULL, 0); return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE); } /** * fluid_hashtable_foreach_steal: * @hashtable: a #fluid_hashtable_t. * @func: the function to call for each key/value pair. * @user_data: user data to pass to the function. * * Calls the given function for each key/value pair in the #fluid_hashtable_t. * If the function returns %TRUE, then the key/value pair is removed from the * #fluid_hashtable_t, but no key or value destroy functions are called. * * See #fluid_hashtable_iter_t for an alternative way to loop over the * key/value pairs in the hash table. * * Return value: the number of key/value pairs removed. **/ unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data) { fluid_return_val_if_fail (hashtable != NULL, 0); fluid_return_val_if_fail (func != NULL, 0); return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, FALSE); } /** * fluid_hashtable_foreach: * @hashtable: a #fluid_hashtable_t. * @func: the function to call for each key/value pair. * @user_data: user data to pass to the function. * * Calls the given function for each of the key/value pairs in the * #fluid_hashtable_t. The function is passed the key and value of each * pair, and the given @user_data parameter. The hash table may not * be modified while iterating over it (you can't add/remove * items). To remove all items matching a predicate, use * fluid_hashtable_foreach_remove(). * * See fluid_hashtable_find() for performance caveats for linear * order searches in contrast to fluid_hashtable_lookup(). **/ void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data) { fluid_hashnode_t *node; int i; fluid_return_if_fail (hashtable != NULL); fluid_return_if_fail (func != NULL); for (i = 0; i < hashtable->size; i++) for (node = hashtable->nodes[i]; node; node = node->next) (* func) (node->key, node->value, user_data); } /** * fluid_hashtable_find: * @hashtable: a #fluid_hashtable_t. * @predicate: function to test the key/value pairs for a certain property. * @user_data: user data to pass to the function. * * Calls the given function for key/value pairs in the #fluid_hashtable_t until * @predicate returns %TRUE. The function is passed the key and value of * each pair, and the given @user_data parameter. The hash table may not * be modified while iterating over it (you can't add/remove items). * * Note, that hash tables are really only optimized for forward lookups, * i.e. fluid_hashtable_lookup(). * So code that frequently issues fluid_hashtable_find() or * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a * hash table) should probably be reworked to use additional or different * data structures for reverse lookups (keep in mind that an O(n) find/foreach * operation issued for all n values in a hash table ends up needing O(n*n) * operations). * * Return value: The value of the first key/value pair is returned, for which * func evaluates to %TRUE. If no pair with the requested property is found, * %NULL is returned. * * Since: 2.4 **/ void * fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, void *user_data) { fluid_hashnode_t *node; int i; fluid_return_val_if_fail (hashtable != NULL, NULL); fluid_return_val_if_fail (predicate != NULL, NULL); for (i = 0; i < hashtable->size; i++) for (node = hashtable->nodes[i]; node; node = node->next) if (predicate (node->key, node->value, user_data)) return node->value; return NULL; } /** * fluid_hashtable_size: * @hashtable: a #fluid_hashtable_t. * * Returns the number of elements contained in the #fluid_hashtable_t. * * Return value: the number of key/value pairs in the #fluid_hashtable_t. **/ unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable) { fluid_return_val_if_fail (hashtable != NULL, 0); return hashtable->nnodes; } /** * fluid_hashtable_get_keys: * @hashtable: a #fluid_hashtable_t * * Retrieves every key inside @hashtable. The returned data is valid * until @hashtable is modified. * * Return value: a #GList containing all the keys inside the hash * table. The content of the list is owned by the hash table and * should not be modified or freed. Use delete_fluid_list() when done * using the list. * * Since: 2.14 */ fluid_list_t * fluid_hashtable_get_keys (fluid_hashtable_t *hashtable) { fluid_hashnode_t *node; int i; fluid_list_t *retval; fluid_return_val_if_fail (hashtable != NULL, NULL); retval = NULL; for (i = 0; i < hashtable->size; i++) for (node = hashtable->nodes[i]; node; node = node->next) retval = fluid_list_prepend (retval, node->key); return retval; } /** * fluid_hashtable_get_values: * @hashtable: a #fluid_hashtable_t * * Retrieves every value inside @hashtable. The returned data is * valid until @hashtable is modified. * * Return value: a #GList containing all the values inside the hash * table. The content of the list is owned by the hash table and * should not be modified or freed. Use delete_fluid_list() when done * using the list. * * Since: 2.14 */ fluid_list_t * fluid_hashtable_get_values (fluid_hashtable_t *hashtable) { fluid_hashnode_t *node; int i; fluid_list_t *retval; fluid_return_val_if_fail (hashtable != NULL, NULL); retval = NULL; for (i = 0; i < hashtable->size; i++) for (node = hashtable->nodes[i]; node; node = node->next) retval = fluid_list_prepend (retval, node->value); return retval; } /* Extracted from glib/gstring.c */ /** * fluid_str_equal: * @v1: a key * @v2: a key to compare with @v1 * * Compares two strings for byte-by-byte equality and returns %TRUE * if they are equal. It can be passed to new_fluid_hashtable() as the * @key_equal_func parameter, when using strings as keys in a #Ghashtable. * * Returns: %TRUE if the two keys match */ int fluid_str_equal (const void *v1, const void *v2) { const char *string1 = v1; const char *string2 = v2; return strcmp (string1, string2) == 0; } /** * fluid_str_hash: * @v: a string key * * Converts a string to a hash value. * It can be passed to new_fluid_hashtable() as the @hash_func * parameter, when using strings as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key */ unsigned int fluid_str_hash (const void *v) { /* 31 bit hash function */ const signed char *p = v; uint32 h = *p; if (h) for (p += 1; *p != '\0'; p++) h = (h << 5) - h + *p; return h; } /* Extracted from glib/gutils.c */ /** * fluid_direct_equal: * @v1: a key. * @v2: a key to compare with @v1. * * Compares two #gpointer arguments and returns %TRUE if they are equal. * It can be passed to new_fluid_hashtable() as the @key_equal_func * parameter, when using pointers as keys in a #fluid_hashtable_t. * * Returns: %TRUE if the two keys match. */ int fluid_direct_equal (const void *v1, const void *v2) { return v1 == v2; } /** * fluid_direct_hash: * @v: a void * key * * Converts a gpointer to a hash value. * It can be passed to g_hashtable_new() as the @hash_func parameter, * when using pointers as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key. */ unsigned int fluid_direct_hash (const void *v) { return FLUID_POINTER_TO_UINT (v); } /** * fluid_int_equal: * @v1: a pointer to a int key. * @v2: a pointer to a int key to compare with @v1. * * Compares the two #gint values being pointed to and returns * %TRUE if they are equal. * It can be passed to g_hashtable_new() as the @key_equal_func * parameter, when using pointers to integers as keys in a #fluid_hashtable_t. * * Returns: %TRUE if the two keys match. */ int fluid_int_equal (const void *v1, const void *v2) { return *((const int*) v1) == *((const int*) v2); } /** * fluid_int_hash: * @v: a pointer to a int key * * Converts a pointer to a #gint to a hash value. * It can be passed to g_hashtable_new() as the @hash_func parameter, * when using pointers to integers values as keys in a #fluid_hashtable_t. * * Returns: a hash value corresponding to the key. */ unsigned int fluid_int_hash (const void *v) { return *(const int*) v; } fluidsynth-1.1.9/src/utils/fluid_hash.h000066400000000000000000000122061322272076000201230ustar00rootroot00000000000000/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02110-1301, USA. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ /* * Adapted for FluidSynth use by Josh Green * September 8, 2009 from glib 2.18.4 * * - Self contained (no dependencies on glib) * - changed names to fluid_hashtable_... */ #ifndef _FLUID_HASH_H #define _FLUID_HASH_H #include "fluidsynth_priv.h" #include "fluid_list.h" #include "fluid_sys.h" /* Extracted from gtypes.h */ typedef void (*fluid_destroy_notify_t)(void *data); typedef unsigned int (*fluid_hash_func_t)(const void *key); typedef int (*fluid_equal_func_t)(const void *a, const void *b); /* End gtypes.h extraction */ typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data); typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t; typedef struct _fluid_hashnode_t fluid_hashnode_t; struct _fluid_hashnode_t { void *key; void *value; fluid_hashnode_t *next; unsigned int key_hash; }; struct _fluid_hashtable_t { int size; int nnodes; fluid_hashnode_t **nodes; fluid_hash_func_t hash_func; fluid_equal_func_t key_equal_func; volatile int ref_count; fluid_destroy_notify_t key_destroy_func; fluid_destroy_notify_t value_destroy_func; fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) }; struct _fluid_hashtable_iter_t { /*< private >*/ void * dummy1; void * dummy2; void * dummy3; int dummy4; int dummy5; // Bool void * dummy6; }; fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func); fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func, fluid_destroy_notify_t key_destroy_func, fluid_destroy_notify_t value_destroy_func); void delete_fluid_hashtable(fluid_hashtable_t *hashtable); void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value); fluid_hashtable_t *fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter); void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter); void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter); fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable); void fluid_hashtable_unref (fluid_hashtable_t *hashtable); void *fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key); int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key, void **orig_key, void **value); void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value); void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value); int fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key); int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key); void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable); void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable); unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data); void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, void *user_data); void *fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, void *user_data); unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable); fluid_list_t *fluid_hashtable_get_keys (fluid_hashtable_t *hashtable); fluid_list_t *fluid_hashtable_get_values (fluid_hashtable_t *hashtable); int fluid_str_equal (const void *v1, const void *v2); unsigned int fluid_str_hash (const void *v); int fluid_direct_equal (const void *v1, const void *v2); unsigned int fluid_direct_hash (const void *v); int fluid_int_equal (const void *v1, const void *v2); unsigned int fluid_int_hash (const void *v); #endif /* _FLUID_HASH_H */ fluidsynth-1.1.9/src/utils/fluid_list.c000066400000000000000000000114601322272076000201470ustar00rootroot00000000000000/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02110-1301, USA. */ /* * Modified by the GLib Team and others 1997-1999. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ #include "fluid_list.h" fluid_list_t* new_fluid_list(void) { fluid_list_t* list; list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t)); list->data = NULL; list->next = NULL; return list; } void delete_fluid_list(fluid_list_t *list) { fluid_list_t *next; while (list) { next = list->next; FLUID_FREE(list); list = next; } } void delete1_fluid_list(fluid_list_t *list) { if (list) { FLUID_FREE(list); } } fluid_list_t* fluid_list_append(fluid_list_t *list, void* data) { fluid_list_t *new_list; fluid_list_t *last; new_list = new_fluid_list(); new_list->data = data; if (list) { last = fluid_list_last(list); /* g_assert (last != NULL); */ last->next = new_list; return list; } else return new_list; } fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data) { fluid_list_t *new_list; new_list = new_fluid_list(); new_list->data = data; new_list->next = list; return new_list; } fluid_list_t* fluid_list_nth(fluid_list_t *list, int n) { while ((n-- > 0) && list) { list = list->next; } return list; } fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data) { fluid_list_t *tmp; fluid_list_t *prev; prev = NULL; tmp = list; while (tmp) { if (tmp->data == data) { if (prev) { prev->next = tmp->next; } if (list == tmp) { list = list->next; } tmp->next = NULL; delete_fluid_list(tmp); break; } prev = tmp; tmp = tmp->next; } return list; } fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link) { fluid_list_t *tmp; fluid_list_t *prev; prev = NULL; tmp = list; while (tmp) { if (tmp == link) { if (prev) { prev->next = tmp->next; } if (list == tmp) { list = list->next; } tmp->next = NULL; break; } prev = tmp; tmp = tmp->next; } return list; } static fluid_list_t* fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func) { fluid_list_t list, *l; l = &list; while (l1 && l2) { if (compare_func(l1->data,l2->data) < 0) { l = l->next = l1; l1 = l1->next; } else { l = l->next = l2; l2 = l2->next; } } l->next= l1 ? l1 : l2; return list.next; } fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) { fluid_list_t *l1, *l2; if (!list) { return NULL; } if (!list->next) { return list; } l1 = list; l2 = list->next; while ((l2 = l2->next) != NULL) { if ((l2 = l2->next) == NULL) break; l1=l1->next; } l2 = l1->next; l1->next = NULL; return fluid_list_sort_merge(fluid_list_sort(list, compare_func), fluid_list_sort(l2, compare_func), compare_func); } fluid_list_t* fluid_list_last(fluid_list_t *list) { if (list) { while (list->next) list = list->next; } return list; } int fluid_list_size(fluid_list_t *list) { int n = 0; while (list) { n++; list = list->next; } return n; } fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data) { fluid_list_t *new_list; fluid_list_t *cur; fluid_list_t *prev = NULL; new_list = new_fluid_list(); new_list->data = data; cur = list; while ((n-- > 0) && cur) { prev = cur; cur = cur->next; } new_list->next = cur; if (prev) { prev->next = new_list; return list; } else { return new_list; } } /* Compare function to sort strings alphabetically, * for use with fluid_list_sort(). */ int fluid_list_str_compare_func (void *a, void *b) { if (a && b) return FLUID_STRCMP ((char *)a, (char *)b); if (!a && !b) return 0; if (a) return -1; return 1; } fluidsynth-1.1.9/src/utils/fluid_list.h000066400000000000000000000042111322272076000201500ustar00rootroot00000000000000/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02110-1301, USA. */ #ifndef _FLUID_LIST_H #define _FLUID_LIST_H #include "fluidsynth_priv.h" /* * * Lists * * A sound font loader has to pack the data from the .SF2 file into * list structures of this type. * */ typedef struct _fluid_list_t fluid_list_t; typedef int (*fluid_compare_func_t)(void* a, void* b); struct _fluid_list_t { void* data; fluid_list_t *next; }; fluid_list_t* new_fluid_list(void); void delete_fluid_list(fluid_list_t *list); void delete1_fluid_list(fluid_list_t *list); fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func); fluid_list_t* fluid_list_append(fluid_list_t *list, void* data); fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data); fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data); fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); fluid_list_t* fluid_list_nth(fluid_list_t *list, int n); fluid_list_t* fluid_list_last(fluid_list_t *list); fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data); int fluid_list_size(fluid_list_t *list); #define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) #define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) int fluid_list_str_compare_func (void *a, void *b); #endif /* _FLUID_LIST_H */ fluidsynth-1.1.9/src/utils/fluid_ringbuffer.c000066400000000000000000000050431322272076000213250ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /* * Josh Green * 2009-05-28 */ #include "fluid_ringbuffer.h" #include "fluidsynth_priv.h" /** * Create a lock free queue with a fixed maximum count and size of elements. * @param count Count of elements in queue (fixed max number of queued elements) * @return New lock free queue or NULL if out of memory (error message logged) * * Lockless FIFO queues don't use any locking mechanisms and can therefore be * advantageous in certain situations, such as passing data between a lower * priority thread and a higher "real time" thread, without potential lock * contention which could stall the high priority thread. Note that there may * only be one producer thread and one consumer thread. */ fluid_ringbuffer_t * new_fluid_ringbuffer (int count, int elementsize) { fluid_ringbuffer_t *queue; fluid_return_val_if_fail (count > 0, NULL); queue = FLUID_NEW (fluid_ringbuffer_t); if (!queue) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } queue->array = FLUID_MALLOC (elementsize * count); if (!queue->array) { FLUID_FREE (queue); FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } /* Clear array, in case dynamic pointer reclaiming is being done */ FLUID_MEMSET (queue->array, 0, elementsize * count); queue->totalcount = count; queue->elementsize = elementsize; queue->count = 0; queue->in = 0; queue->out = 0; return (queue); } /** * Free an event queue. * @param queue Lockless queue instance * * Care must be taken when freeing a queue, to ensure that the consumer and * producer threads will no longer access it. */ void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue) { FLUID_FREE (queue->array); FLUID_FREE (queue); } fluidsynth-1.1.9/src/utils/fluid_ringbuffer.h000066400000000000000000000104671322272076000213400ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_RINGBUFFER_H #define _FLUID_RINGBUFFER_H #include "fluid_sys.h" /* * Lockless event queue instance. */ struct _fluid_ringbuffer_t { char *array; /**< Queue array of arbitrary size elements */ int totalcount; /**< Total count of elements in array */ int count; /**< Current count of elements */ int in; /**< Index in queue to store next pushed element */ int out; /**< Index in queue of next popped element */ int elementsize; /**< Size of each element */ void* userdata; }; typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t; fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize); void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue); /** * Get pointer to next input array element in queue. * @param queue Lockless queue instance * @param count Normally zero, or more if you need to push several items at once * @return Pointer to array element in queue to store data to or NULL if queue is full * * This function along with fluid_ringbuffer_next_inptr() form a queue "push" * operation and is split into 2 functions to avoid an element copy. Note that * the returned array element pointer may contain the data of a previous element * if the queue has wrapped around. This can be used to reclaim pointers to * allocated memory, etc. */ static FLUID_INLINE void* fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset) { return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount); } /** * Advance the input queue index to complete a "push" operation. * @param queue Lockless queue instance * @param count Normally one, or more if you need to push several items at once * * This function along with fluid_ringbuffer_get_inptr() form a queue "push" * operation and is split into 2 functions to avoid element copy. */ static FLUID_INLINE void fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count) { fluid_atomic_int_add (&queue->count, count); queue->in += count; if (queue->in >= queue->totalcount) queue->in -= queue->totalcount; } /** * Get amount of items currently in queue * @param queue Lockless queue instance * @return amount of items currently in queue */ static FLUID_INLINE int fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue) { return fluid_atomic_int_get (&queue->count); } /** * Get pointer to next output array element in queue. * @param queue Lockless queue instance * @return Pointer to array element data in the queue or NULL if empty, can only * be used up until fluid_ringbuffer_next_outptr() is called. * * This function along with fluid_ringbuffer_next_outptr() form a queue "pop" * operation and is split into 2 functions to avoid an element copy. */ static FLUID_INLINE void* fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue) { return fluid_ringbuffer_get_count(queue) == 0 ? NULL : queue->array + queue->elementsize * queue->out; } /** * Advance the output queue index to complete a "pop" operation. * @param queue Lockless queue instance * * This function along with fluid_ringbuffer_get_outptr() form a queue "pop" * operation and is split into 2 functions to avoid an element copy. */ static FLUID_INLINE void fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue) { fluid_atomic_int_add (&queue->count, -1); if (++queue->out == queue->totalcount) queue->out = 0; } #endif /* _FLUID_ringbuffer_H */ fluidsynth-1.1.9/src/utils/fluid_settings.c000066400000000000000000001257501322272076000210440ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 "fluidsynth_priv.h" #include "fluid_sys.h" #include "fluid_hash.h" #include "fluid_synth.h" #include "fluid_cmd.h" #include "fluid_adriver.h" #include "fluid_mdriver.h" #include "fluid_settings.h" #include "fluid_midi.h" /* Defined in fluid_filerenderer.c */ extern void fluid_file_renderer_settings (fluid_settings_t* settings); /* maximum allowed components of a settings variable (separated by '.') */ #define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */ #define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ static void fluid_settings_init(fluid_settings_t* settings); static void fluid_settings_key_destroy_func(void* value); static void fluid_settings_value_destroy_func(void* value); static int fluid_settings_tokenize(const char *s, char *buf, char **ptr); /* Common structure to all settings nodes */ typedef struct { int type; /**< fluid_types_enum */ } fluid_setting_node_t; typedef struct { fluid_setting_node_t node; char* value; char* def; int hints; fluid_list_t* options; fluid_str_update_t update; void* data; } fluid_str_setting_t; typedef struct { fluid_setting_node_t node; double value; double def; double min; double max; int hints; fluid_num_update_t update; void* data; } fluid_num_setting_t; typedef struct { fluid_setting_node_t node; int value; int def; int min; int max; int hints; fluid_int_update_t update; void* data; } fluid_int_setting_t; typedef struct { fluid_setting_node_t node; fluid_hashtable_t *hashtable; } fluid_set_setting_t; static fluid_str_setting_t* new_fluid_str_setting(const char* value, const char* def, int hints, fluid_str_update_t fun, void* data) { fluid_str_setting_t* str; str = FLUID_NEW(fluid_str_setting_t); if (!str) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } str->node.type = FLUID_STR_TYPE; str->value = value? FLUID_STRDUP(value) : NULL; str->def = def? FLUID_STRDUP(def) : NULL; str->hints = hints; str->options = NULL; str->update = fun; str->data = data; return str; } static void delete_fluid_str_setting(fluid_str_setting_t* str) { if (!str) return; if (str->value) FLUID_FREE(str->value); if (str->def) FLUID_FREE(str->def); if (str->options) { fluid_list_t* list = str->options; while (list) { FLUID_FREE (list->data); list = fluid_list_next(list); } delete_fluid_list(str->options); } FLUID_FREE(str); } static fluid_num_setting_t* new_fluid_num_setting(double min, double max, double def, int hints, fluid_num_update_t fun, void* data) { fluid_num_setting_t* setting; setting = FLUID_NEW(fluid_num_setting_t); if (!setting) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } setting->node.type = FLUID_NUM_TYPE; setting->value = def; setting->def = def; setting->min = min; setting->max = max; setting->hints = hints; setting->update = fun; setting->data = data; return setting; } static void delete_fluid_num_setting(fluid_num_setting_t* setting) { if (setting) FLUID_FREE(setting); } static fluid_int_setting_t* new_fluid_int_setting(int min, int max, int def, int hints, fluid_int_update_t fun, void* data) { fluid_int_setting_t* setting; setting = FLUID_NEW(fluid_int_setting_t); if (!setting) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } setting->node.type = FLUID_INT_TYPE; setting->value = def; setting->def = def; setting->min = min; setting->max = max; setting->hints = hints; setting->update = fun; setting->data = data; return setting; } static void delete_fluid_int_setting(fluid_int_setting_t* setting) { if (setting) FLUID_FREE(setting); } static fluid_set_setting_t* new_fluid_set_setting(void) { fluid_set_setting_t* setting; setting = FLUID_NEW(fluid_set_setting_t); if (!setting) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } setting->node.type = FLUID_SET_TYPE; setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, fluid_settings_key_destroy_func, fluid_settings_value_destroy_func); if (!setting->hashtable) { FLUID_FREE (setting); return NULL; } return setting; } static void delete_fluid_set_setting(fluid_set_setting_t* setting) { if (setting) { delete_fluid_hashtable(setting->hashtable); FLUID_FREE(setting); } } /** * Create a new settings object * @return the pointer to the settings object */ fluid_settings_t * new_fluid_settings(void) { fluid_settings_t* settings; settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, fluid_settings_key_destroy_func, fluid_settings_value_destroy_func); if (settings == NULL) return NULL; fluid_rec_mutex_init (settings->mutex); fluid_settings_init(settings); return settings; } /** * Delete the provided settings object * @param settings a settings object */ void delete_fluid_settings(fluid_settings_t* settings) { fluid_return_if_fail (settings != NULL); fluid_rec_mutex_destroy (settings->mutex); delete_fluid_hashtable(settings); } /* Settings hash key destroy function */ static void fluid_settings_key_destroy_func(void* value) { FLUID_FREE (value); /* Free the string key value */ } /* Settings hash value destroy function */ static void fluid_settings_value_destroy_func(void* value) { fluid_setting_node_t *node = value; switch (node->type) { case FLUID_NUM_TYPE: delete_fluid_num_setting((fluid_num_setting_t*) value); break; case FLUID_INT_TYPE: delete_fluid_int_setting((fluid_int_setting_t*) value); break; case FLUID_STR_TYPE: delete_fluid_str_setting((fluid_str_setting_t*) value); break; case FLUID_SET_TYPE: delete_fluid_set_setting((fluid_set_setting_t*) value); break; } } void fluid_settings_init(fluid_settings_t* settings) { fluid_return_if_fail (settings != NULL); fluid_synth_settings(settings); fluid_shell_settings(settings); fluid_player_settings(settings); fluid_file_renderer_settings(settings); fluid_audio_driver_settings(settings); fluid_midi_driver_settings(settings); } static int fluid_settings_tokenize(const char *s, char *buf, char **ptr) { char *tokstr, *tok; int n = 0; if (strlen (s) > MAX_SETTINGS_LABEL) { FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", MAX_SETTINGS_LABEL); return 0; } FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ tokstr = buf; while ((tok = fluid_strtok (&tokstr, "."))) { if (n >= MAX_SETTINGS_TOKENS) { FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", MAX_SETTINGS_TOKENS); return 0; } else ptr[n++] = tok; } return n; } /** * Get a setting name, value and type * * @param settings a settings object * @param name Settings name * @param value Location to store setting node if found * @return 1 if the node exists, 0 otherwise */ static int fluid_settings_get(fluid_settings_t* settings, const char *name, fluid_setting_node_t **value) { fluid_hashtable_t* table = settings; fluid_setting_node_t *node = NULL; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; int n; ntokens = fluid_settings_tokenize (name, buf, tokens); if (table == NULL || ntokens <= 0) return 0; for (n = 0; n < ntokens; n++) { node = fluid_hashtable_lookup(table, tokens[n]); if (!node) return 0; table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL; } if (value) *value = node; return 1; } /** * Set a setting name, value and type, replacing it if already exists * * @param settings a settings object * @param name Settings name * @param value Node instance to assign (used directly) * @return 1 if the value has been set, zero otherwise */ static int fluid_settings_set(fluid_settings_t* settings, const char *name, void* value) { fluid_hashtable_t* table = settings; fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int n, num; char *dupname; num = fluid_settings_tokenize (name, buf, tokens) - 1; if (num == 0) return 0; for (n = 0; n < num; n++) { node = fluid_hashtable_lookup(table, tokens[n]); if (node) { if (node->type == FLUID_SET_TYPE) { table = ((fluid_set_setting_t *)node)->hashtable; } else { /* path ends prematurely */ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); return 0; } } else { /* create a new node */ fluid_set_setting_t* setnode; dupname = FLUID_STRDUP (tokens[n]); setnode = new_fluid_set_setting (); if (!dupname || !setnode) { if (dupname) FLUID_FREE (dupname); else FLUID_LOG(FLUID_ERR, "Out of memory"); if (setnode) delete_fluid_set_setting (setnode); return 0; } fluid_hashtable_insert(table, dupname, setnode); table = setnode->hashtable; } } dupname = FLUID_STRDUP (tokens[num]); if (!dupname) { FLUID_LOG(FLUID_ERR, "Out of memory"); return 0; } fluid_hashtable_insert(table, dupname, value); return 1; } /** returns 1 if the value has been registered correctly, 0 otherwise */ int fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints, fluid_str_update_t fun, void* data) { fluid_setting_node_t *node; fluid_str_setting_t* setting; int retval; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, name, &node)) { setting = new_fluid_str_setting(def, def, hints, fun, data); retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_str_setting (setting); } else { /* if variable already exists, don't change its value. */ if (node->type == FLUID_STR_TYPE) { setting = (fluid_str_setting_t*) node; setting->update = fun; setting->data = data; setting->def = def? FLUID_STRDUP(def) : NULL; setting->hints = hints; retval = 1; } else { FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); retval = 0; } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** returns 1 if the value has been register correctly, zero otherwise */ int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double def, double min, double max, int hints, fluid_num_update_t fun, void* data) { fluid_setting_node_t *node; int retval; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); /* For now, all floating point settings are bounded below and above */ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, name, &node)) { /* insert a new setting */ fluid_num_setting_t* setting; setting = new_fluid_num_setting(min, max, def, hints, fun, data); retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_num_setting (setting); } else { if (node->type == FLUID_NUM_TYPE) { /* update the existing setting but don't change its value */ fluid_num_setting_t* setting = (fluid_num_setting_t*) node; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; retval = 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); retval = 0; } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** returns 1 if the value has been register correctly, zero otherwise. */ int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def, int min, int max, int hints, fluid_int_update_t fun, void* data) { fluid_setting_node_t *node; int retval; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); /* For now, all integer settings are bounded below and above */ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, name, &node)) { /* insert a new setting */ fluid_int_setting_t* setting; setting = new_fluid_int_setting(min, max, def, hints, fun, data); retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_int_setting (setting); } else { if (node->type == FLUID_INT_TYPE) { /* update the existing setting but don't change its value */ fluid_int_setting_t* setting = (fluid_int_setting_t*) node; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; retval = 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); retval = 0; } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the type of the setting with the given name * * @param settings a settings object * @param name a setting's name * @return the type for the named setting, or #FLUID_NO_TYPE when it does not exist */ int fluid_settings_get_type(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; int type; fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE); fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE); fluid_return_val_if_fail (name[0] != '\0', FLUID_NO_TYPE); fluid_rec_mutex_lock (settings->mutex); type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE; fluid_rec_mutex_unlock (settings->mutex); return (type); } /** * Get the hints for the named setting as an integer bitmap * * @param settings a settings object * @param name a setting's name * @return the hints associated to the named setting if it exists, zero otherwise */ int fluid_settings_get_hints(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; int hints = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_NUM_TYPE) { fluid_num_setting_t* setting = (fluid_num_setting_t*) node; hints = setting->hints; } else if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t* setting = (fluid_str_setting_t*) node; hints = setting->hints; } else if (node->type == FLUID_INT_TYPE) { fluid_int_setting_t* setting = (fluid_int_setting_t*) node; hints = setting->hints; } } fluid_rec_mutex_unlock (settings->mutex); return hints; } /** * Ask whether the setting is changeable in real-time. * * @param settings a settings object * @param name a setting's name * @return non zero if the setting is changeable in real-time */ int fluid_settings_is_realtime(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; int isrealtime = FALSE; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_NUM_TYPE) { fluid_num_setting_t* setting = (fluid_num_setting_t*) node; isrealtime = setting->update != NULL; } else if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t* setting = (fluid_str_setting_t*) node; isrealtime = setting->update != NULL; } else if (node->type == FLUID_INT_TYPE) { fluid_int_setting_t* setting = (fluid_int_setting_t*) node; isrealtime = setting->update != NULL; } } fluid_rec_mutex_unlock (settings->mutex); return isrealtime; } /** * Set a string value for a named setting * * @param settings a settings object * @param name a setting's name * @param str new string value * @return 1 if the value has been set, 0 otherwise */ int fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get (settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = (fluid_str_setting_t *)node; if (setting->value) FLUID_FREE (setting->value); setting->value = str ? FLUID_STRDUP (str) : NULL; /* Call under lock to keep update() synchronized with the current value */ if (setting->update) (*setting->update)(setting->data, name, str); retval = 1; } else if (node->type == FLUID_INT_TYPE) /* Handle yes/no for boolean values for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) { if (FLUID_STRCMP (str, "yes") == 0) { setting->value = TRUE; if (setting->update) (*setting->update)(setting->data, name, TRUE); } else if (FLUID_STRCMP (str, "no") == 0) { setting->value = FALSE; if (setting->update) (*setting->update)(setting->data, name, FALSE); } } } } else { /* insert a new setting */ fluid_str_setting_t* setting; setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_str_setting (setting); } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Copy the value of a string setting * @param settings a settings object * @param name a setting's name * @param str Caller supplied buffer to copy string value to * @param len Size of 'str' buffer (no more than len bytes will be written, which * will always include a zero terminator) * @return 1 if the value exists, 0 otherwise * @since 1.1.0 * * Like fluid_settings_getstr() but is thread safe. A size of 256 should be * more than sufficient for the string buffer. */ int fluid_settings_copystr(fluid_settings_t* settings, const char *name, char *str, int len) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (str != NULL, 0); fluid_return_val_if_fail (len > 0, 0); str[0] = 0; fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get (settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = (fluid_str_setting_t *)node; if (setting->value) { FLUID_STRNCPY (str, setting->value, len); str[len - 1] = 0; /* Force terminate, in case of truncation */ } retval = 1; } else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) { FLUID_STRNCPY (str, setting->value ? "yes" : "no", len); str[len - 1] = 0; /* Force terminate, in case of truncation */ retval = 1; } } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Duplicate the value of a string setting * @param settings a settings object * @param name a setting's name * @param str Location to store pointer to allocated duplicate string * @return 1 if the value exists and was successfully duplicated, 0 otherwise * @since 1.1.0 * * Like fluid_settings_copystr() but allocates a new copy of the string. Caller * owns the string and should free it with free() when done using it. */ int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (str != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = (fluid_str_setting_t *)node; if (setting->value) { *str = FLUID_STRDUP (setting->value); if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory"); } if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ } else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) { *str = FLUID_STRDUP (setting->value ? "yes" : "no"); if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory"); if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ } } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the value of a string setting * @param settings a settings object * @param name a setting's name * @param str Location to store pointer to the settings string value * @return 1 if the value exists, 0 otherwise * @deprecated * * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will * point to the value. The application does not own the returned value and it * is valid only until a new value is assigned to the setting of the given name. * * NOTE: In a multi-threaded environment, caller must ensure that the setting * being read by fluid_settings_getstr() is not assigned during the * duration of callers use of the setting's value. Use fluid_settings_copystr() * or fluid_settings_dupstr() which does not have this restriction. */ int fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (str != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = (fluid_str_setting_t *)node; *str = setting->value; retval = 1; } else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) { *str = setting->value ? "yes" : "no"; retval = 1; } } } else *str = NULL; fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Test a string setting for some value. * * @param settings a settings object * @param name a setting's name * @param s a string to be tested * @return 1 if the value exists and is equal to 's', 0 otherwise */ int fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (s != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get (settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = (fluid_str_setting_t *)node; if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0; } else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0; } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the default value of a string setting. Note that the returned string is * not owned by the caller and should not be modified or freed. * * @param settings a settings object * @param name a setting's name * @return the default string value of the setting if it exists, NULL otherwise */ char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; char *retval = NULL; fluid_return_val_if_fail (settings != NULL, NULL); fluid_return_val_if_fail (name != NULL, NULL); fluid_return_val_if_fail (name[0] != '\0', NULL); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get (settings, name, &node)) { if (node->type == FLUID_STR_TYPE) { fluid_str_setting_t* setting = (fluid_str_setting_t*) node; retval = setting->def; } else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ { fluid_int_setting_t *setting = (fluid_int_setting_t *)node; if (setting->hints & FLUID_HINT_TOGGLED) retval = setting->def ? "yes" : "no"; } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Add an option to a string setting (like an enumeration value). * @param settings a settings object * @param name a setting's name * @param s option string to add * @return 1 if the setting exists and option was added, 0 otherwise * * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set. */ int fluid_settings_add_option(fluid_settings_t* settings, const char *name, const char *s) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (s != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) node; char* copy = FLUID_STRDUP(s); setting->options = fluid_list_append(setting->options, copy); setting->hints |= FLUID_HINT_OPTIONLIST; retval = 1; } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Remove an option previously assigned by fluid_settings_add_option(). * @param settings a settings object * @param name a setting's name * @param s option string to remove * @return 1 if the setting exists and option was removed, 0 otherwise */ int fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (s != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) node; fluid_list_t* list = setting->options; while (list) { char* option = (char*) fluid_list_get(list); if (FLUID_STRCMP(s, option) == 0) { FLUID_FREE (option); setting->options = fluid_list_remove_link(setting->options, list); retval = 1; break; } list = fluid_list_next(list); } } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Set a numeric value for a named setting. * * @param settings a settings object * @param name a setting's name * @param val new setting's value * @return 1 if the value has been set, 0 otherwise */ int fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val) { fluid_setting_node_t *node; fluid_num_setting_t* setting; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_NUM_TYPE) { setting = (fluid_num_setting_t*) node; if (val < setting->min) val = setting->min; else if (val > setting->max) val = setting->max; setting->value = val; /* Call under lock to keep update() synchronized with the current value */ if (setting->update) (*setting->update)(setting->data, name, val); retval = 1; } } else { /* insert a new setting */ fluid_num_setting_t* setting; setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL); setting->value = val; retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_num_setting (setting); } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the numeric value of a named setting * * @param settings a settings object * @param name a setting's name * @param val variable pointer to receive the setting's numeric value * @return 1 if the value exists, 0 otherwise */ int fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (val != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) node; *val = setting->value; retval = 1; } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the range of values of a numeric setting * * @param settings a settings object * @param name a setting's name * @param min setting's range lower limit * @param max setting's range upper limit */ void fluid_settings_getnum_range(fluid_settings_t* settings, const char *name, double* min, double* max) { fluid_setting_node_t *node; fluid_return_if_fail (settings != NULL); fluid_return_if_fail (name != NULL); fluid_return_if_fail (name[0] != '\0'); fluid_return_if_fail (min != NULL); fluid_return_if_fail (max != NULL); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) node; *min = setting->min; *max = setting->max; } fluid_rec_mutex_unlock (settings->mutex); } /** * Get the default value of a named numeric (double) setting * * @param settings a settings object * @param name a setting's name * @return the default value if the named setting exists, 0.0f otherwise */ double fluid_settings_getnum_default(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; double retval = 0.0; fluid_return_val_if_fail (settings != NULL, 0.0); fluid_return_val_if_fail (name != NULL, 0.0); fluid_return_val_if_fail (name[0] != '\0', 0.0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) node; retval = setting->def; } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Set an integer value for a setting * * @param settings a settings object * @param name a setting's name * @param val new setting's integer value * @return 1 if the value has been set, 0 otherwise */ int fluid_settings_setint(fluid_settings_t* settings, const char *name, int val) { fluid_setting_node_t *node; fluid_int_setting_t* setting; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node)) { if (node->type == FLUID_INT_TYPE) { setting = (fluid_int_setting_t*) node; if (val < setting->min) val = setting->min; else if (val > setting->max) val = setting->max; setting->value = val; /* Call under lock to keep update() synchronized with the current value */ if (setting->update) (*setting->update)(setting->data, name, val); retval = 1; } } else { /* insert a new setting */ fluid_int_setting_t* setting; setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL); setting->value = val; retval = fluid_settings_set(settings, name, setting); if (retval != 1) delete_fluid_int_setting (setting); } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get an integer value setting. * * @param settings a settings object * @param name a setting's name * @param val pointer to a variable to receive the setting's integer value * @return 1 if the value exists, 0 otherwise */ int fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_return_val_if_fail (val != NULL, 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) node; *val = setting->value; retval = 1; } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Get the range of values of an integer setting * @param settings a settings object * @param name a setting's name * @param min setting's range lower limit * @param max setting's range upper limit */ void fluid_settings_getint_range(fluid_settings_t* settings, const char *name, int* min, int* max) { fluid_setting_node_t *node; fluid_return_if_fail (settings != NULL); fluid_return_if_fail (name != NULL); fluid_return_if_fail (name[0] != '\0'); fluid_return_if_fail (min != NULL); fluid_return_if_fail (max != NULL); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) node; *min = setting->min; *max = setting->max; } fluid_rec_mutex_unlock (settings->mutex); } /** * Get the default value of an integer setting. * * @param settings a settings object * @param name a setting's name * @return the setting's default integer value it it exists, zero otherwise */ int fluid_settings_getint_default(fluid_settings_t* settings, const char *name) { fluid_setting_node_t *node; int retval = 0; fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); fluid_return_val_if_fail (name[0] != '\0', 0); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && (node->type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) node; retval = setting->def; } fluid_rec_mutex_unlock (settings->mutex); return retval; } /** * Iterate the available options for a named string setting, calling the provided * callback function for each existing option. * * @param settings a settings object * @param name a setting's name * @param data any user provided pointer * @param func callback function to be called on each iteration * * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each * option in alphabetical order. Sort order was undefined in previous versions. */ void fluid_settings_foreach_option (fluid_settings_t* settings, const char *name, void* data, fluid_settings_foreach_option_t func) { fluid_setting_node_t *node; fluid_str_setting_t *setting; fluid_list_t *p, *newlist = NULL; fluid_return_if_fail (settings != NULL); fluid_return_if_fail (name != NULL); fluid_return_if_fail (name[0] != '\0'); fluid_return_if_fail (func != NULL); fluid_rec_mutex_lock (settings->mutex); /* ++ lock */ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE) { fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ return; } setting = (fluid_str_setting_t*)node; /* Duplicate option list */ for (p = setting->options; p; p = p->next) newlist = fluid_list_append (newlist, fluid_list_get (p)); /* Sort by name */ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func); for (p = newlist; p; p = p->next) (*func)(data, (char *)name, (char *)fluid_list_get (p)); fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ delete_fluid_list (newlist); } /** * Count option string values for a string setting. * @param settings a settings object * @param name Name of setting * @return Count of options for this string setting (0 if none, -1 if not found * or not a string setting) * @since 1.1.0 */ int fluid_settings_option_count (fluid_settings_t *settings, const char *name) { fluid_setting_node_t *node; int count = -1; fluid_return_val_if_fail (settings != NULL, -1); fluid_return_val_if_fail (name != NULL, -1); fluid_return_val_if_fail (name[0] != '\0', -1); fluid_rec_mutex_lock (settings->mutex); if (fluid_settings_get(settings, name, &node) && node->type == FLUID_STR_TYPE) count = fluid_list_size (((fluid_str_setting_t *)node)->options); fluid_rec_mutex_unlock (settings->mutex); return (count); } /** * Concatenate options for a string setting together with a separator between. * @param settings Settings object * @param name Settings name * @param separator String to use between options (NULL to use ", ") * @return Newly allocated string or NULL on error (out of memory, not a valid * setting \a name or not a string setting). Free the string when finished with it. * @since 1.1.0 */ char * 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; int count, len; char *str, *option; fluid_return_val_if_fail (settings != NULL, NULL); fluid_return_val_if_fail (name != NULL, NULL); fluid_return_val_if_fail (name[0] != '\0', NULL); if (!separator) separator = ", "; fluid_rec_mutex_lock (settings->mutex); /* ++ lock */ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE) { fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ return (NULL); } setting = (fluid_str_setting_t*)node; /* Duplicate option list, count options and get total string length */ for (p = setting->options, count = 0, len = 0; p; p = p->next, count++) { option = fluid_list_get (p); if (option) { newlist = fluid_list_append (newlist, option); len += strlen (option); } } if (count > 1) len += (count - 1) * strlen (separator); len++; /* For terminator */ /* Sort by name */ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func); str = FLUID_MALLOC (len); if (str) { str[0] = 0; for (p = newlist; p; p = p->next) { option = fluid_list_get (p); strcat (str, option); if (p->next) strcat (str, separator); } } fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ delete_fluid_list (newlist); if (!str) FLUID_LOG (FLUID_ERR, "Out of memory"); return (str); } /* Structure passed to fluid_settings_foreach_iter recursive function */ typedef struct { char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */ fluid_list_t *names; /* For fluid_settings_foreach() */ } fluid_settings_foreach_bag_t; static int fluid_settings_foreach_iter (void* key, void* value, void* data) { fluid_settings_foreach_bag_t *bag = data; char *keystr = key; fluid_setting_node_t *node = value; int pathlen; char *s; pathlen = strlen (bag->path); if (pathlen > 0) { bag->path[pathlen] = '.'; bag->path[pathlen + 1] = 0; } strcat (bag->path, keystr); switch (node->type) { case FLUID_NUM_TYPE: case FLUID_INT_TYPE: case FLUID_STR_TYPE: s = FLUID_STRDUP (bag->path); if (s) bag->names = fluid_list_append (bag->names, s); break; case FLUID_SET_TYPE: fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable, fluid_settings_foreach_iter, bag); break; } bag->path[pathlen] = 0; return 0; } /** * Iterate the existing settings defined in a settings object, calling the * provided callback function for each setting. * * @param settings a settings object * @param data any user provided pointer * @param func callback function to be called on each iteration * * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each * setting in alphabetical order. Sort order was undefined in previous versions. */ void fluid_settings_foreach (fluid_settings_t* settings, void* data, fluid_settings_foreach_t func) { fluid_settings_foreach_bag_t bag; fluid_setting_node_t *node; fluid_list_t *p; int r; fluid_return_if_fail (settings != NULL); fluid_return_if_fail (func != NULL); bag.path[0] = 0; bag.names = NULL; fluid_rec_mutex_lock (settings->mutex); /* Add all node names to the bag.names list */ fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag); /* Sort names */ bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func); /* Loop over names and call the callback */ for (p = bag.names; p; p = p->next) { r = fluid_settings_get (settings, (char *)(p->data), &node); if (r && node) (*func) (data, (char *)(p->data), node->type); FLUID_FREE (p->data); /* -- Free name */ } fluid_rec_mutex_unlock (settings->mutex); delete_fluid_list (bag.names); /* -- Free names list */ } fluidsynth-1.1.9/src/utils/fluid_settings.h000066400000000000000000000044171322272076000210450ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUID_SETTINGS_H #define _FLUID_SETTINGS_H /** returns 1 if the option was added, 0 otherwise */ int fluid_settings_add_option(fluid_settings_t* settings, const char* name, const char* s); /** returns 1 if the option was added, 0 otherwise */ int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, const char* s); typedef int (*fluid_num_update_t)(void* data, const char* name, double value); typedef int (*fluid_str_update_t)(void* data, const char* name, const char* value); typedef int (*fluid_int_update_t)(void* data, const char* name, int value); /** returns 0 if the value has been registered correctly, non-zero otherwise */ int fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints, fluid_str_update_t fun, void* data); /** returns 0 if the value has been registered correctly, non-zero otherwise */ int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double def, double min, double max, int hints, fluid_num_update_t fun, void* data); /** returns 0 if the value has been registered correctly, non-zero otherwise */ int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def, int min, int max, int hints, fluid_int_update_t fun, void* data); #endif /* _FLUID_SETTINGS_H */ fluidsynth-1.1.9/src/utils/fluid_sys.c000066400000000000000000000744031322272076000200200ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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_sys.h" #if WITH_READLINE #include #include #endif #ifdef DBUS_SUPPORT #include "fluid_rtkit.h" #endif /* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket. * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */ #define WIN32_SOCKET_FLAG 0x40000000 /* SCHED_FIFO priority for high priority timer threads */ #define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10 typedef struct { fluid_thread_func_t func; void *data; int prio_level; } fluid_thread_info_t; struct _fluid_timer_t { long msec; fluid_timer_callback_t callback; void *data; fluid_thread_t *thread; int cont; int auto_destroy; }; struct _fluid_server_socket_t { fluid_socket_t socket; fluid_thread_t *thread; int cont; fluid_server_func_t func; void *data; }; static int fluid_istream_gets(fluid_istream_t in, char* buf, int len); static char fluid_errbuf[512]; /* buffer for error message */ static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL]; static void* fluid_log_user_data[LAST_LOG_LEVEL]; static int fluid_log_initialized = 0; static char* fluid_libname = "fluidsynth"; void fluid_sys_config() { fluid_log_config(); } unsigned int fluid_debug_flags = 0; #if DEBUG /* * fluid_debug */ int fluid_debug(int level, char * fmt, ...) { if (fluid_debug_flags & level) { fluid_log_function_t fun; va_list args; va_start (args, fmt); vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); va_end (args); fun = fluid_log_function[FLUID_DBG]; if (fun != NULL) { (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]); } } return 0; } #endif /** * Installs a new log function for a specified log level. * @param level Log level to install handler for. * @param fun Callback function handler to call for logged messages * @param data User supplied data pointer to pass to log function * @return The previously installed function. */ fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data) { fluid_log_function_t old = NULL; if ((level >= 0) && (level < LAST_LOG_LEVEL)) { old = fluid_log_function[level]; fluid_log_function[level] = fun; fluid_log_user_data[level] = data; } return old; } /** * Default log function which prints to the stderr. * @param level Log level * @param message Log message * @param data User supplied data (not used) */ void fluid_default_log_function(int level, char* message, void* data) { FILE* out; #if defined(WIN32) out = stdout; #else out = stderr; #endif if (fluid_log_initialized == 0) { fluid_log_config(); } switch (level) { case FLUID_PANIC: FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); break; case FLUID_ERR: FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); break; case FLUID_WARN: FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); break; case FLUID_INFO: FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); break; case FLUID_DBG: #if DEBUG FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); #endif break; default: FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); break; } fflush(out); } /* * fluid_init_log */ void fluid_log_config(void) { if (fluid_log_initialized == 0) { fluid_log_initialized = 1; if (fluid_log_function[FLUID_PANIC] == NULL) { fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_ERR] == NULL) { fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_WARN] == NULL) { fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_INFO] == NULL) { fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_DBG] == NULL) { fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); } } } /** * Print a message to the log. * @param level Log level (#fluid_log_level). * @param fmt Printf style format string for log message * @param ... Arguments for printf 'fmt' message string * @return Always returns #FLUID_FAILED */ int fluid_log(int level, const char* fmt, ...) { fluid_log_function_t fun = NULL; va_list args; va_start (args, fmt); vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); va_end (args); if ((level >= 0) && (level < LAST_LOG_LEVEL)) { fun = fluid_log_function[level]; if (fun != NULL) { (*fun)(level, fluid_errbuf, fluid_log_user_data[level]); } } return FLUID_FAILED; } /** * An improved strtok, still trashes the input string, but is portable and * thread safe. Also skips token chars at beginning of token string and never * returns an empty token (will return NULL if source ends in token chars though). * NOTE: NOT part of public API * @internal * @param str Pointer to a string pointer of source to tokenize. Pointer gets * updated on each invocation to point to beginning of next token. Note that * token char get's overwritten with a 0 byte. String pointer is set to NULL * when final token is returned. * @param delim String of delimiter chars. * @return Pointer to the next token or NULL if no more tokens. */ char *fluid_strtok (char **str, char *delim) { char *s, *d, *token; char c; if (str == NULL || delim == NULL || !*delim) { FLUID_LOG(FLUID_ERR, "Null pointer"); return NULL; } s = *str; if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */ /* skip delimiter chars at beginning of token */ do { c = *s; if (!c) /* end of source string? */ { *str = NULL; return NULL; } for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { s++; /* advance to next source char */ break; } } } while (*d); /* while token char match */ token = s; /* start of token found */ /* search for next token char or end of source string */ for (s = s+1; *s; s++) { c = *s; for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { *s = '\0'; /* overwrite token char with zero byte to terminate token */ *str = s+1; /* update str to point to beginning of next token */ return token; } } } /* we get here only if source string ended */ *str = NULL; return token; } /* * fluid_error */ char* fluid_error() { return fluid_errbuf; } /** * 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 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 * * The current implementation only checks for the "RIFF" header in the file. * It is useful only to distinguish between SoundFont and MIDI files. */ int fluid_is_soundfont(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 strncmp(id, "RIFF", 4) == 0; } /** * Get time in milliseconds to be used in relative timing operations. * @return Unix time in milliseconds. */ unsigned int fluid_curtime(void) { static glong initial_seconds = 0; GTimeVal timeval; if (initial_seconds == 0) { g_get_current_time (&timeval); initial_seconds = timeval.tv_sec; } g_get_current_time (&timeval); return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0); } /** * Get time in microseconds to be used in relative timing operations. * @return Unix time in microseconds. */ double fluid_utime (void) { GTimeVal timeval; g_get_current_time (&timeval); return (timeval.tv_sec * 1000000.0 + timeval.tv_usec); } #if defined(WIN32) /* Windoze specific stuff */ void fluid_thread_self_set_prio (int prio_level) { if (prio_level > 0) SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST); } #elif defined(__OS2__) /* OS/2 specific stuff */ void fluid_thread_self_set_prio (int prio_level) { if (prio_level > 0) DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); } #else /* POSIX stuff.. Nice POSIX.. Good POSIX. */ void fluid_thread_self_set_prio (int prio_level) { struct sched_param priority; if (prio_level > 0) { memset(&priority, 0, sizeof(priority)); priority.sched_priority = prio_level; if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) == 0) { return; } #ifdef DBUS_SUPPORT /* Try to gain high priority via rtkit */ if (fluid_rtkit_make_realtime(0, prio_level) == 0) { return; } #endif FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); } } #ifdef FPE_CHECK /*************************************************************** * * Floating point exceptions * * The floating point exception functions were taken from Ircam's * jMax source code. http://www.ircam.fr/jmax * * FIXME: check in config for i386 machine * * Currently not used. I leave the code here in case we want to pick * this up again some time later. */ /* Exception flags */ #define _FPU_STATUS_IE 0x001 /* Invalid Operation */ #define _FPU_STATUS_DE 0x002 /* Denormalized Operand */ #define _FPU_STATUS_ZE 0x004 /* Zero Divide */ #define _FPU_STATUS_OE 0x008 /* Overflow */ #define _FPU_STATUS_UE 0x010 /* Underflow */ #define _FPU_STATUS_PE 0x020 /* Precision */ #define _FPU_STATUS_SF 0x040 /* Stack Fault */ #define _FPU_STATUS_ES 0x080 /* Error Summary Status */ /* Macros for accessing the FPU status word. */ /* get the FPU status */ #define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw)) /* clear the FPU status */ #define _FPU_CLR_SW() __asm__ ("fnclex" : : ) /* Purpose: * Checks, if the floating point unit has produced an exception, print a message * if so and clear the exception. */ unsigned int fluid_check_fpe_i386(char* explanation) { unsigned int s; _FPU_GET_SW(s); _FPU_CLR_SW(); s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; if (s) { FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation, (s & _FPU_STATUS_IE) ? "Invalid operation " : "", (s & _FPU_STATUS_DE) ? "Denormal number " : "", (s & _FPU_STATUS_ZE) ? "Zero divide " : "", (s & _FPU_STATUS_OE) ? "Overflow " : "", (s & _FPU_STATUS_UE) ? "Underflow " : ""); } return s; } /* Purpose: * Clear floating point exception. */ void fluid_clear_fpe_i386 (void) { _FPU_CLR_SW(); } #endif // ifdef FPE_CHECK #endif // #else (its POSIX) /*************************************************************** * * Profiling (Linux, i586 only) * */ #if WITH_PROFILING fluid_profile_data_t fluid_profile_data[] = { { FLUID_PROF_WRITE, "fluid_synth_write_* ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0} }; void fluid_profiling_print(void) { int i; printf("fluid_profiling_print\n"); FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); for (i = 0; i < FLUID_PROF_LAST; i++) { if (fluid_profile_data[i].count > 0) { FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f", fluid_profile_data[i].description, fluid_profile_data[i].min, fluid_profile_data[i].total / fluid_profile_data[i].count, fluid_profile_data[i].max); } else { FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description); } } } #endif /* WITH_PROFILING */ /*************************************************************** * * Threads * */ #if OLD_GLIB_THREAD_API /* Rather than inline this one, we just declare it as a function, to prevent * GCC warning about inline failure. */ fluid_cond_t * new_fluid_cond (void) { if (!g_thread_supported ()) g_thread_init (NULL); return g_cond_new (); } #endif static gpointer fluid_thread_high_prio (gpointer data) { fluid_thread_info_t *info = data; fluid_thread_self_set_prio (info->prio_level); info->func (info->data); FLUID_FREE (info); return NULL; } /** * Create a new thread. * @param func Function to execute in new thread context * @param data User defined data to pass to func * @param prio_level Priority level. If greater than 0 then high priority scheduling will * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling. * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished. * @return New thread pointer or NULL on error */ fluid_thread_t * new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach) { GThread *thread; fluid_thread_info_t *info; GError *err = NULL; g_return_val_if_fail (func != NULL, NULL); #if OLD_GLIB_THREAD_API /* Make sure g_thread_init has been called. * FIXME - Probably not a good idea in a shared library, * but what can we do *and* remain backwards compatible? */ if (!g_thread_supported ()) g_thread_init (NULL); #endif if (prio_level > 0) { info = FLUID_NEW (fluid_thread_info_t); if (!info) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } info->func = func; info->data = data; info->prio_level = prio_level; #if NEW_GLIB_THREAD_API thread = g_thread_try_new (name, fluid_thread_high_prio, info, &err); #else thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err); #endif } #if NEW_GLIB_THREAD_API else thread = g_thread_try_new (name, (GThreadFunc)func, data, &err); #else else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err); #endif if (!thread) { FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s", fluid_gerror_message (err)); g_clear_error (&err); return NULL; } #if NEW_GLIB_THREAD_API if (detach) g_thread_unref (thread); // Release thread reference, if caller wants to detach #endif return thread; } /** * Frees data associated with a thread (does not actually stop thread). * @param thread Thread to free */ void delete_fluid_thread(fluid_thread_t* thread) { /* Threads free themselves when they quit, nothing to do */ } /** * Join a thread (wait for it to terminate). * @param thread Thread to join * @return FLUID_OK */ int fluid_thread_join(fluid_thread_t* thread) { g_thread_join (thread); return FLUID_OK; } void fluid_timer_run (void *data) { fluid_timer_t *timer; int count = 0; int cont; long start; long delay; timer = (fluid_timer_t *)data; /* keep track of the start time for absolute positioning */ start = fluid_curtime (); while (timer->cont) { cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; if (!cont) break; /* to avoid incremental time errors, calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); if (delay > 0) g_usleep (delay * 1000); } FLUID_LOG (FLUID_DBG, "Timer thread finished"); if (timer->auto_destroy) FLUID_FREE (timer); return; } fluid_timer_t* new_fluid_timer (int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy, int high_priority) { fluid_timer_t *timer; timer = FLUID_NEW (fluid_timer_t); if (timer == NULL) { FLUID_LOG (FLUID_ERR, "Out of memory"); return NULL; } timer->msec = msec; timer->callback = callback; timer->data = data; timer->cont = TRUE ; timer->thread = NULL; timer->auto_destroy = auto_destroy; if (new_thread) { timer->thread = new_fluid_thread ("timer", fluid_timer_run, timer, high_priority ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE); if (!timer->thread) { FLUID_FREE (timer); return NULL; } } else { fluid_timer_run (timer); /* Run directly, instead of as a separate thread */ if(auto_destroy) { /* do NOT return freed memory */ return NULL; } } return timer; } int delete_fluid_timer (fluid_timer_t *timer) { int auto_destroy = timer->auto_destroy; timer->cont = 0; fluid_timer_join (timer); /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */ if (!auto_destroy) FLUID_FREE (timer); return FLUID_OK; } int fluid_timer_join (fluid_timer_t *timer) { int auto_destroy; if (timer->thread) { auto_destroy = timer->auto_destroy; fluid_thread_join (timer->thread); if (!auto_destroy) timer->thread = NULL; } return FLUID_OK; } /*************************************************************** * * Sockets and I/O * */ /** * Get standard in stream handle. * @return Standard in stream. */ fluid_istream_t fluid_get_stdin (void) { return STDIN_FILENO; } /** * Get standard output stream handle. * @return Standard out stream. */ fluid_ostream_t fluid_get_stdout (void) { return STDOUT_FILENO; } /** * Read a line from an input stream. * @return 0 if end-of-stream, -1 if error, non zero otherwise */ int fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len) { #if WITH_READLINE if (in == fluid_get_stdin ()) { char *line; line = readline (prompt); if (line == NULL) return -1; snprintf(buf, len, "%s", line); buf[len - 1] = 0; free(line); return 1; } else #endif { fluid_ostream_printf (out, "%s", prompt); return fluid_istream_gets (in, buf, len); } } /** * Reads a line from an input stream (socket). * @param in The input socket * @param buf Buffer to store data to * @param len Maximum length to store to buf * @return 1 if a line was read, 0 on end of stream, -1 on error */ static int fluid_istream_gets (fluid_istream_t in, char* buf, int len) { char c; int n; buf[len - 1] = 0; while (--len > 0) { #ifndef WIN32 n = read(in, &c, 1); if (n == -1) return -1; #else /* Handle read differently depending on if its a socket or file descriptor */ if (!(in & WIN32_SOCKET_FLAG)) { n = read(in, &c, 1); if (n == -1) return -1; } else { n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0); if (n == SOCKET_ERROR) return -1; } #endif if (n == 0) { *buf++ = 0; return 0; } if (c == '\n') { *buf++ = 0; return 1; } /* Store all characters excluding CR */ if (c != '\r') *buf++ = c; } return -1; } /** * Send a printf style string with arguments to an output stream (socket). * @param out Output stream * @param format printf style format string * @param ... Arguments for the printf format string * @return Number of bytes written or -1 on error */ int fluid_ostream_printf (fluid_ostream_t out, char* format, ...) { char buf[4096]; va_list args; int len; va_start (args, format); len = vsnprintf (buf, 4095, format, args); va_end (args); if (len == 0) { return 0; } if (len < 0) { printf("fluid_ostream_printf: buffer overflow"); return -1; } buf[4095] = 0; #ifndef WIN32 return write (out, buf, strlen (buf)); #else { int retval; /* Handle write differently depending on if its a socket or file descriptor */ if (!(out & WIN32_SOCKET_FLAG)) return write(out, buf, strlen (buf)); /* Socket */ retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0); return retval != SOCKET_ERROR ? retval : -1; } #endif } int fluid_server_socket_join(fluid_server_socket_t *server_socket) { return fluid_thread_join (server_socket->thread); } #ifndef WIN32 // Not win32? #define SOCKET_ERROR -1 fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock) { return sock; } fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock) { return sock; } void fluid_socket_close(fluid_socket_t sock) { if (sock != INVALID_SOCKET) close (sock); } static void fluid_server_socket_run (void *data) { fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; fluid_socket_t client_socket; #ifdef IPV6_SUPPORT struct sockaddr_in6 addr; char straddr[INET6_ADDRSTRLEN]; #else struct sockaddr_in addr; char straddr[INET_ADDRSTRLEN]; #endif socklen_t addrlen = sizeof (addr); int retval; FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); FLUID_LOG (FLUID_DBG, "Server listening for connections"); while (server_socket->cont) { client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen); FLUID_LOG (FLUID_DBG, "New client connection"); if (client_socket == INVALID_SOCKET) { if (server_socket->cont) FLUID_LOG(FLUID_ERR, "Failed to accept connection"); server_socket->cont = 0; return; } else { #ifdef HAVE_INETNTOP #ifdef IPV6_SUPPORT inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); #else inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); #endif #endif #ifdef HAVE_INETNTOP retval = server_socket->func (server_socket->data, client_socket, straddr); #else retval = server_socket->func (server_socket->data, client_socket, inet_ntoa (addr.sin_addr)); #endif if (retval != 0) fluid_socket_close(client_socket); } } FLUID_LOG(FLUID_DBG, "Server closing"); } fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data) { fluid_server_socket_t* server_socket; #ifdef IPV6_SUPPORT struct sockaddr_in6 addr; #else struct sockaddr_in addr; #endif fluid_socket_t sock; g_return_val_if_fail (func != NULL, NULL); #ifdef IPV6_SUPPORT sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { FLUID_LOG(FLUID_ERR, "Failed to create server socket"); return NULL; } FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(port); #else sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { FLUID_LOG(FLUID_ERR, "Failed to create server socket"); return NULL; } FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); #endif if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed to bind server socket"); fluid_socket_close(sock); return NULL; } if (listen(sock, 10) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed listen on server socket"); fluid_socket_close(sock); return NULL; } server_socket = FLUID_NEW(fluid_server_socket_t); if (server_socket == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); fluid_socket_close(sock); return NULL; } server_socket->socket = sock; server_socket->func = func; server_socket->data = data; server_socket->cont = 1; server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, 0, FALSE); if (server_socket->thread == NULL) { FLUID_FREE(server_socket); fluid_socket_close(sock); return NULL; } return server_socket; } int delete_fluid_server_socket(fluid_server_socket_t* server_socket) { server_socket->cont = 0; if (server_socket->socket != INVALID_SOCKET) { fluid_socket_close(server_socket->socket); } if (server_socket->thread) { delete_fluid_thread(server_socket->thread); } FLUID_FREE(server_socket); return FLUID_OK; } #else // Win32 is "special" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock) { return sock | WIN32_SOCKET_FLAG; } fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock) { return sock | WIN32_SOCKET_FLAG; } void fluid_socket_close (fluid_socket_t sock) { if (sock != INVALID_SOCKET) closesocket (sock); } static void fluid_server_socket_run (void *data) { fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; fluid_socket_t client_socket; #ifdef IPV6_SUPPORT struct sockaddr_in6 addr; char straddr[INET6_ADDRSTRLEN]; #else struct sockaddr_in addr; #ifdef HAVE_INETNTOP char straddr[INET_ADDRSTRLEN]; #endif #endif socklen_t addrlen = sizeof (addr); int r; FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); FLUID_LOG(FLUID_DBG, "Server listening for connections"); while (server_socket->cont) { client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen); FLUID_LOG (FLUID_DBG, "New client connection"); if (client_socket == INVALID_SOCKET) { if (server_socket->cont) FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ()); server_socket->cont = 0; return; } else { #ifdef HAVE_INETNTOP #ifdef IPV6_SUPPORT inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); #else inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); #endif #endif #ifdef HAVE_INETNTOP r = server_socket->func (server_socket->data, client_socket, straddr); #else r = server_socket->func (server_socket->data, client_socket, inet_ntoa (addr.sin_addr)); #endif if (r != 0) fluid_socket_close (client_socket); } } FLUID_LOG (FLUID_DBG, "Server closing"); } fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data) { fluid_server_socket_t* server_socket; #ifdef IPV6_SUPPORT struct sockaddr_in6 addr; #else struct sockaddr_in addr; #endif fluid_socket_t sock; WSADATA wsaData; int retval; g_return_val_if_fail (func != NULL, NULL); // Win32 requires initialization of winsock retval = WSAStartup (MAKEWORD (2,2), &wsaData); if (retval != 0) { FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval); return NULL; } #ifdef IPV6_SUPPORT sock = socket (AF_INET6, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ()); WSACleanup (); return NULL; } addr.sin6_family = AF_INET6; addr.sin6_port = htons (port); addr.sin6_addr = in6addr_any; #else sock = socket (AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ()); WSACleanup (); return NULL; } addr.sin_family = AF_INET; addr.sin_port = htons (port); addr.sin_addr.s_addr = htonl (INADDR_ANY); #endif retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr)); if (retval == SOCKET_ERROR) { FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ()); fluid_socket_close (sock); WSACleanup (); return NULL; } if (listen (sock, SOMAXCONN) == SOCKET_ERROR) { FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ()); fluid_socket_close (sock); WSACleanup (); return NULL; } server_socket = FLUID_NEW (fluid_server_socket_t); if (server_socket == NULL) { FLUID_LOG (FLUID_ERR, "Out of memory"); fluid_socket_close (sock); WSACleanup (); return NULL; } server_socket->socket = sock; server_socket->func = func; server_socket->data = data; server_socket->cont = 1; server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, 0, FALSE); if (server_socket->thread == NULL) { FLUID_FREE (server_socket); fluid_socket_close (sock); WSACleanup (); return NULL; } return server_socket; } int delete_fluid_server_socket(fluid_server_socket_t *server_socket) { server_socket->cont = 0; if (server_socket->socket != INVALID_SOCKET) fluid_socket_close (server_socket->socket); if (server_socket->thread) { fluid_thread_join(server_socket->thread); delete_fluid_thread (server_socket->thread); } FLUID_FREE (server_socket); WSACleanup (); // Should be called the same number of times as WSAStartup return FLUID_OK; } #endif fluidsynth-1.1.9/src/utils/fluid_sys.h000066400000000000000000000323031322272076000200160ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ /** This header contains a bunch of (mostly) system and machine dependent functions: - timers - current time in milliseconds and microseconds - debug logging - profiling - memory locking - checking for floating point exceptions */ #ifndef _FLUID_SYS_H #define _FLUID_SYS_H #include #include "fluidsynth_priv.h" /** * Macro used for safely accessing a message from a GError and using a default * message if it is NULL. * @param err Pointer to a GError to access the message field of. * @return Message string */ #define fluid_gerror_message(err) ((err) ? err->message : "No error details") void fluid_sys_config(void); void fluid_log_config(void); void fluid_time_config(void); /* Misc */ #define fluid_return_val_if_fail g_return_val_if_fail #define fluid_return_if_fail g_return_if_fail #define FLUID_INLINE inline #define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT #define FLUID_UINT_TO_POINTER GUINT_TO_POINTER #define FLUID_POINTER_TO_INT GPOINTER_TO_INT #define FLUID_INT_TO_POINTER GINT_TO_POINTER #define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0])) #define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) /* * Utility functions */ char *fluid_strtok (char **str, char *delim); /** Additional debugging system, separate from the log system. This allows to print selected debug messages of a specific subsystem. */ extern unsigned int fluid_debug_flags; #if DEBUG enum fluid_debug_level { FLUID_DBG_DRIVER = 1 }; int fluid_debug(int level, char * fmt, ...); #else #define fluid_debug #endif #if defined(__OS2__) #define INCL_DOS #include typedef int socklen_t; #endif unsigned int fluid_curtime(void); double fluid_utime(void); /** Timers */ /* if the callback function returns 1 the timer will continue; if it returns 0 it will stop */ typedef int (*fluid_timer_callback_t)(void* data, unsigned int msec); typedef struct _fluid_timer_t fluid_timer_t; fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy, int high_priority); int delete_fluid_timer(fluid_timer_t* timer); int fluid_timer_join(fluid_timer_t* timer); int fluid_timer_stop(fluid_timer_t* timer); // Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) #define NEW_GLIB_THREAD_API (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 32)) #define OLD_GLIB_THREAD_API (GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)) /* Muteces */ #if NEW_GLIB_THREAD_API /* glib 2.32 and newer */ /* Regular mutex */ typedef GMutex fluid_mutex_t; #define FLUID_MUTEX_INIT { 0 } #define fluid_mutex_init(_m) g_mutex_init (&(_m)) #define fluid_mutex_destroy(_m) g_mutex_clear (&(_m)) #define fluid_mutex_lock(_m) g_mutex_lock(&(_m)) #define fluid_mutex_unlock(_m) g_mutex_unlock(&(_m)) /* Recursive lock capable mutex */ typedef GRecMutex fluid_rec_mutex_t; #define fluid_rec_mutex_init(_m) g_rec_mutex_init(&(_m)) #define fluid_rec_mutex_destroy(_m) g_rec_mutex_clear(&(_m)) #define fluid_rec_mutex_lock(_m) g_rec_mutex_lock(&(_m)) #define fluid_rec_mutex_unlock(_m) g_rec_mutex_unlock(&(_m)) /* Dynamically allocated mutex suitable for fluid_cond_t use */ typedef GMutex fluid_cond_mutex_t; #define fluid_cond_mutex_lock(m) g_mutex_lock(m) #define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) static FLUID_INLINE fluid_cond_mutex_t * new_fluid_cond_mutex (void) { GMutex *mutex; mutex = g_new (GMutex, 1); g_mutex_init (mutex); return (mutex); } static FLUID_INLINE void delete_fluid_cond_mutex (fluid_cond_mutex_t *m) { g_mutex_clear (m); g_free (m); } /* Thread condition signaling */ typedef GCond fluid_cond_t; #define fluid_cond_signal(cond) g_cond_signal(cond) #define fluid_cond_broadcast(cond) g_cond_broadcast(cond) #define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) static FLUID_INLINE fluid_cond_t * new_fluid_cond (void) { GCond *cond; cond = g_new (GCond, 1); g_cond_init (cond); return (cond); } static FLUID_INLINE void delete_fluid_cond (fluid_cond_t *cond) { g_cond_clear (cond); g_free (cond); } /* Thread private data */ typedef GPrivate fluid_private_t; #define fluid_private_init(_priv) memset (&_priv, 0, sizeof (_priv)) #define fluid_private_free(_priv) #define fluid_private_get(_priv) g_private_get(&(_priv)) #define fluid_private_set(_priv, _data) g_private_set(&(_priv), _data) #else /* glib prior to 2.32 */ /* Regular mutex */ typedef GStaticMutex fluid_mutex_t; #define FLUID_MUTEX_INIT G_STATIC_MUTEX_INIT #define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m)) #define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) #define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) #define fluid_mutex_init(_m) G_STMT_START { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_mutex_init (&(_m)); \ } G_STMT_END; /* Recursive lock capable mutex */ typedef GStaticRecMutex fluid_rec_mutex_t; #define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) #define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) #define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) #define fluid_rec_mutex_init(_m) G_STMT_START { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_rec_mutex_init (&(_m)); \ } G_STMT_END; /* Dynamically allocated mutex suitable for fluid_cond_t use */ typedef GMutex fluid_cond_mutex_t; #define delete_fluid_cond_mutex(m) g_mutex_free(m) #define fluid_cond_mutex_lock(m) g_mutex_lock(m) #define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) static FLUID_INLINE fluid_cond_mutex_t * new_fluid_cond_mutex (void) { if (!g_thread_supported ()) g_thread_init (NULL); return g_mutex_new (); } /* Thread condition signaling */ typedef GCond fluid_cond_t; fluid_cond_t *new_fluid_cond (void); #define delete_fluid_cond(cond) g_cond_free(cond) #define fluid_cond_signal(cond) g_cond_signal(cond) #define fluid_cond_broadcast(cond) g_cond_broadcast(cond) #define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) /* Thread private data */ typedef GStaticPrivate fluid_private_t; #define fluid_private_get(_priv) g_static_private_get(&(_priv)) #define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL) #define fluid_private_free(_priv) g_static_private_free(&(_priv)) #define fluid_private_init(_priv) G_STMT_START { \ if (!g_thread_supported ()) g_thread_init (NULL); \ g_static_private_init (&(_priv)); \ } G_STMT_END; #endif /* Atomic operations */ #define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi) #define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi) #define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val) #define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi) #define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \ g_atomic_int_compare_and_exchange(_pi, _old, _new) #if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30) #define fluid_atomic_int_exchange_and_add(_pi, _add) \ g_atomic_int_add(_pi, _add) #define fluid_atomic_int_add(_pi, _add) \ g_atomic_int_add(_pi, _add) #else #define fluid_atomic_int_exchange_and_add(_pi, _add) \ g_atomic_int_exchange_and_add(_pi, _add) #define fluid_atomic_int_add(_pi, _add) \ g_atomic_int_exchange_and_add(_pi, _add) #endif #define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) #define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val) #define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ g_atomic_pointer_compare_and_exchange(_pp, _old, _new) static FLUID_INLINE void fluid_atomic_float_set(volatile float *fptr, float val) { sint32 ival; memcpy (&ival, &val, 4); fluid_atomic_int_set ((volatile int *)fptr, ival); } static FLUID_INLINE float fluid_atomic_float_get(volatile float *fptr) { sint32 ival; float fval; ival = fluid_atomic_int_get ((volatile int *)fptr); memcpy (&fval, &ival, 4); return fval; } /* Threads */ typedef GThread fluid_thread_t; typedef void (*fluid_thread_func_t)(void* data); #define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ #define fluid_thread_id_t GThread * /* Data type for a thread ID */ #define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ fluid_thread_t* new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach); void delete_fluid_thread(fluid_thread_t* thread); void fluid_thread_self_set_prio (int prio_level); int fluid_thread_join(fluid_thread_t* thread); /* Sockets and I/O */ fluid_istream_t fluid_get_stdin (void); fluid_ostream_t fluid_get_stdout (void); int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len); int fluid_ostream_printf (fluid_ostream_t out, char* format, ...); /* The function should return 0 if no error occured, non-zero otherwise. If the function return non-zero, the socket will be closed by the server. */ typedef int (*fluid_server_func_t)(void* data, fluid_socket_t client_socket, char* addr); fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data); int delete_fluid_server_socket(fluid_server_socket_t* sock); int fluid_server_socket_join(fluid_server_socket_t* sock); void fluid_socket_close(fluid_socket_t sock); fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock); fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock); /* Profiling */ /** * Profile numbers. List all the pieces of code you want to profile * here. Be sure to add an entry in the fluid_profile_data table in * fluid_sys.c */ enum { FLUID_PROF_WRITE, FLUID_PROF_ONE_BLOCK, FLUID_PROF_ONE_BLOCK_CLEAR, FLUID_PROF_ONE_BLOCK_VOICE, FLUID_PROF_ONE_BLOCK_VOICES, FLUID_PROF_ONE_BLOCK_REVERB, FLUID_PROF_ONE_BLOCK_CHORUS, FLUID_PROF_VOICE_NOTE, FLUID_PROF_VOICE_RELEASE, FLUID_PROF_LAST }; #if WITH_PROFILING void fluid_profiling_print(void); /** Profiling data. Keep track of min/avg/max values to execute a piece of code. */ typedef struct _fluid_profile_data_t { int num; char* description; double min, max, total; unsigned int count; } fluid_profile_data_t; extern fluid_profile_data_t fluid_profile_data[]; /** Macro to obtain a time refence used for the profiling */ #define fluid_profile_ref() fluid_utime() /** Macro to create a variable and assign the current reference time for profiling. * So we don't get unused variable warnings when profiling is disabled. */ #define fluid_profile_ref_var(name) double name = fluid_utime() /** Macro to calculate the min/avg/max. Needs a time refence and a profile number. */ #define fluid_profile(_num,_ref) { \ double _now = fluid_utime(); \ double _delta = _now - _ref; \ fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ? _delta : fluid_profile_data[_num].min; \ fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ? _delta : fluid_profile_data[_num].max; \ fluid_profile_data[_num].total += _delta; \ fluid_profile_data[_num].count++; \ _ref = _now; \ } #else /* No profiling */ #define fluid_profiling_print() #define fluid_profile_ref() 0 #define fluid_profile_ref_var(name) #define fluid_profile(_num,_ref) #endif /** Memory locking Memory locking is used to avoid swapping of the large block of sample data. */ #if defined(HAVE_SYS_MMAN_H) && !defined(__OS2__) #define fluid_mlock(_p,_n) mlock(_p, _n) #define fluid_munlock(_p,_n) munlock(_p,_n) #else #define fluid_mlock(_p,_n) 0 #define fluid_munlock(_p,_n) #endif /** Floating point exceptions fluid_check_fpe() checks for "unnormalized numbers" and other exceptions of the floating point processsor. */ #ifdef FPE_CHECK #define fluid_check_fpe(expl) fluid_check_fpe_i386(expl) #define fluid_clear_fpe() fluid_clear_fpe_i386() #else #define fluid_check_fpe(expl) #define fluid_clear_fpe() #endif unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe); void fluid_clear_fpe_i386(void); #endif /* _FLUID_SYS_H */ fluidsynth-1.1.9/src/utils/fluidsynth_priv.h000066400000000000000000000150121322272076000212440ustar00rootroot00000000000000/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * 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 */ #ifndef _FLUIDSYNTH_PRIV_H #define _FLUIDSYNTH_PRIV_H #include #if HAVE_CONFIG_H #include "config.h" #endif #if defined(__POWERPC__) && !(defined(__APPLE__) && defined(__MACH__)) #include "config_maxmsp43.h" #endif #if defined(WIN32) && !defined(MINGW32) #include "config_win32.h" #endif #if HAVE_STRING_H #include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_STDIO_H #include #endif #if HAVE_MATH_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_STDARG_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_MMAN_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TIME_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_NETINET_TCP_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_LIMITS_H #include #endif #if HAVE_PTHREAD_H #include #endif #if HAVE_IO_H #include #endif #if defined(WIN32) && HAVE_WINDOWS_H #include #include #include #endif /* MinGW32 special defines */ #ifdef MINGW32 #include #define snprintf g_snprintf #define vsnprintf g_vsnprintf #define DSOUND_SUPPORT 1 #define WINMIDI_SUPPORT 1 #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif /* Darwin special defines (taken from config_macosx.h) */ #ifdef DARWIN #define MACINTOSH #define __Types__ #define WITHOUT_SERVER 1 #endif #include "fluidsynth.h" /*************************************************************** * * BASIC TYPES */ #if defined(WITH_FLOAT) typedef float fluid_real_t; #else typedef double fluid_real_t; #endif #if defined(WIN32) typedef SOCKET fluid_socket_t; #else typedef int fluid_socket_t; #define INVALID_SOCKET -1 #endif #if defined(SUPPORTS_VLA) # define FLUID_DECLARE_VLA(_type, _name, _len) \ _type _name[_len] #else # define FLUID_DECLARE_VLA(_type, _name, _len) \ _type* _name = g_newa(_type, (_len)) #endif /** Integer types */ //typedef gint8 sint8; typedef guint8 uint8; //typedef gint16 sint16; //typedef guint16 uint16; typedef gint32 sint32; typedef guint32 uint32; //typedef gint64 sint64; //typedef guint64 uint64; /*************************************************************** * * FORWARD DECLARATIONS */ typedef struct _fluid_env_data_t fluid_env_data_t; typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t; typedef struct _fluid_channel_t fluid_channel_t; typedef struct _fluid_tuning_t fluid_tuning_t; typedef struct _fluid_hashtable_t fluid_hashtable_t; typedef struct _fluid_client_t fluid_client_t; typedef struct _fluid_server_socket_t fluid_server_socket_t; typedef struct _fluid_sample_timer_t fluid_sample_timer_t; /*************************************************************** * * CONSTANTS */ #define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ #define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ #define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ #define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ #define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */ #define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */ #ifndef PI #define PI 3.141592654 #endif /*************************************************************** * * SYSTEM INTERFACE */ typedef FILE* fluid_file; #define FLUID_MALLOC(_n) malloc(_n) #define FLUID_REALLOC(_p,_n) realloc(_p,_n) #define FLUID_NEW(_t) (_t*)malloc(sizeof(_t)) #define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t)) #define FLUID_FREE(_p) free(_p) #define FLUID_FOPEN(_f,_m) fopen(_f,_m) #define FLUID_FCLOSE(_f) fclose(_f) #define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) #define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) #define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) #define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) #define FLUID_STRLEN(_s) strlen(_s) #define FLUID_STRCMP(_s,_t) strcmp(_s,_t) #define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) #define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) #define FLUID_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n) #define FLUID_STRCHR(_s,_c) strchr(_s,_c) #define FLUID_STRRCHR(_s,_c) strrchr(_s,_c) #ifdef strdup #define FLUID_STRDUP(s) strdup(s) #else #define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) #endif #define FLUID_SPRINTF sprintf #define FLUID_FPRINTF fprintf #define fluid_clip(_val, _min, _max) \ { (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } #if WITH_FTS #define FLUID_PRINTF post #define FLUID_FLUSH() #else #define FLUID_PRINTF printf #define FLUID_FLUSH() fflush(stdout) #endif #define FLUID_LOG fluid_log #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif #define FLUID_ASSERT(a,b) #define FLUID_ASSERT_P(a,b) char* fluid_error(void); /* Internationalization */ #define _(s) s #endif /* _FLUIDSYNTH_PRIV_H */